-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathArduino-Saturn-Controller.asm
More file actions
314 lines (285 loc) · 8.84 KB
/
Arduino-Saturn-Controller.asm
File metadata and controls
314 lines (285 loc) · 8.84 KB
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
;/* Arduino Saturn Controller
; * Author: Ryan Myers <ryan.p.myers@gmail.com>
; *
; * Copyright (c) 2021 Ryan Myers <https://ryanmyers.ca>
; *
; * GNU GENERAL PUBLIC LICENSE
; * Version 3, 29 June 2007
; *
; * This program is free software: you can redistribute it and/or modify
; * it under the terms of the GNU General Public License as published by
; * the Free Software Foundation, either version 3 of the License, or
; * (at your option) any later version.
; *
; * This program is distributed in the hope that it will be useful,
; * but WITHOUT ANY WARRANTY; without even the implied warranty of
; * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; * GNU General Public License for more details.
; *
; * You should have received a copy of the GNU General Public License
; * along with this program. If not, see <https://www.gnu.org/licenses/>.
; *
; */
SCRH = 0 ;Scratch register used during multiplies. Does not need to be restored after usage.
ZERO = 1 ;Should always be maintained as ZERO. Used in MUL
SRT = 2 ;For storing the SREG temporarily while in interrupts
MTR = 16 ;Main Temp Register
VTR = 17 ;Interrupt (Vector) Temp Register
OUTPUT0 = 18 ;This is the output to PORTF for ZYXR--T-
OUTPUT1 = 19 ;This is the output to PORTF for UDLR--T-
OUTPUT2 = 20 ;This is the output to PORTF for BCAS--T-
OUTPUT3 = 21 ;This is the output to PORTF for 001L--T-
PAIRL = 22 ;For a paired MUL register Low byte
PAIRH = 23 ;For a paired MUL register High byte
TEMP = 25 ;For temporary use
XL = 26 ;X Low byte
XH = 27 ;X High byte
YL = 28 ;Y Low byte
YH = 29 ;Y High byte
ZL = 30 ;Z Low byte
ZH = 31 ;Z High byte
.equiv PINB, 0x03
.equiv DDRB, 0x04
.equiv PORTB, 0x05
.equiv PINC, 0x06
.equiv DDRC, 0x07
.equiv PORTC, 0x08
.equiv PIND, 0x09
.equiv DDRD, 0x0A
.equiv PORTD, 0x0B
.equiv PINE, 0x0C
.equiv DDRE, 0x0D
.equiv PORTE, 0x0E
.equiv PINF, 0x0F
.equiv DDRF, 0x10
.equiv PORTF, 0x11
.equiv EIMSK, 0x1D
.equiv EICRA, 0x0069
.equiv SREG, 0x3F ;STATUS REGISTER - This gets modified with almost every instruction
.equiv SPH, 0x3E ;STACK POINTER HIGH
.equiv SPL, 0x3D ;STACK POINTER LOW
.equiv PD5, 5
.equiv PD0, 0
.equiv PD1, 1
__vectors:
jmp __reset ;Reset Handler
jmp __vector_1 ;IRQ0 External Interrupt Handler
jmp __vector_1 ;IRQ1 External Interrupt Handler
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
jmp __bad_interrupt
;Called up at the start of all code, and when reset
__reset:
eor ZERO, ZERO ;Always define the ZERO register and set it to zero on load
out SREG, ZERO ;Default the Status Register to ZERO as well
ldi YL, 0B11111111 ;Not sure why we care about YL yet.
ldi YH, DDRD ;Not sure why YH is DDRD yet either.
out SPH, YH ;Set stack pointer high to YH
out SPL, YL ;Set stack pointer low to YL
call main
jmp _exit
__bad_interrupt:
jmp 0
;/**
; * Interrupt when Saturn S1 (TR) pin changes
; * Interrupt when Saturn S0 (TH) pin changes
; * I purposefully only used ASM that doesn't affect SREG
; * and doesn't write to any registers so we didn't need to
; * do any PUSH and POPS which saves several cycles.
; */
;0 = 1 + 2 + 1 + 1 + 2 + 5 = 12 cycles
;1 = 2 + 1 + 1 + 2 + 1 + 5 = 12 cycles
;2 = 1 + 2 + 2 + 1 + 1 + 5 = 12 cycles
;3 = 2 + 2 + 2 + 2 + 1 + 5 = 14 cycles
__vector_1:
sbis PIND, PD0 ;PD0 = 1?
rjmp __TH0 ;PD0 = 0
sbis PIND, PD1 ;PD1 = 1? PD0 = 1
out PORTF, OUTPUT1 ;PD1 = 0 So TH1:TR0
rjmp __TH1TR1 ;PD1 = 1 (Or 0 after above finished)
__TH0:
sbis PIND, PD1 ;PD1 = 1?
out PORTF, OUTPUT0 ;PD1 = 0 So TH0:TR0
sbic PIND, PD1 ;PD1 = 0?
out PORTF, OUTPUT2 ;PD1 = 1 So TH0:TR1
reti ;Exit
__TH1TR1: ;This technically gets called when TH1:TR0 as well
sbis PIND, PD1 ;PD1 = 0?
reti ;Exit
out PORTF, OUTPUT3 ;PD1 = l So TH1:TR1
reti ;Exit
;Set up all of the inputs/outputs
main:
sei ;Enable interrupts
;Setup controller pins Up, Down, Left, Right, A, B, C
; BCAUDLR-;
;DDRB &= ~B11111110; //Set them up as inputs
in MTR, DDRB
andi MTR, 0B00000001
out DDRB, MTR ;BCAUDLR- as inputs
;PORTB |= B11111110; //Enable internal pull-ups
in MTR, PORTB
ori MTR, 0B11111110
out PORTB, MTR ;Enable internal pullups on above
;Setup controller pin L
; -L------;
;DDRC &= ~B01000000; //Set it up as input
cbi DDRC, 0B00000110
;PORTC |= B01000000; //Enable internal pull-ups
sbi PORTC, 0B00000110
;Setup controller pins Z Y X and R as inputs
; Z--YXR--
;DDRD &= ~B10011100; //Set them up as inputs
in MTR, DDRD
andi MTR, 0B01100011
out DDRD, MTR
;PORTD |= B10011100; //Enable internal pull-ups
in MTR, PORTD
ori MTR, 0B10011100
out PORTD, MTR
;Setup controller pin Start
; -S------;
;DDRE &= ~B01000000; //Set it up as input
cbi DDRE, 0B00000110
;PORTE |= B01000000; //Enable internal pull-ups
sbi PORTE, 0B00000110
;Setup Saturn select pins S0 (TH) and S1 (TR)
; ------10
;DDRD &= ~B00000011; //Set them up as inputs
in MTR, DDRD
andi MTR, 0B11111100
out DDRD, MTR
;PORTD |= B00000011; //Enable internal pull-ups
in MTR, PORTD
ori MTR, 0B00000011
out PORTD, MTR
;Setup Saturn data pins D0, D1, D2, D3, and TL ACK
; 0123--T-
;DDRF |= B11110010; //Set them up as outputs
in MTR, DDRF
ori MTR, 0B11110010
out DDRF, MTR
;PORTF |= B11110010; //Set them HIGH by default
in MTR, PORTF
ori MTR, 0B11110010
out PORTF, MTR
;Interrupt 0 for clock (PD0, pin 3) (TH S0 on Saturn)
;EICRA &= ~(bit(ISC00) | bit (ISC01)); // Clear existing flags of interrupt 0
lds MTR, EICRA
andi MTR, 0B11111100
sts EICRA, MTR
;EICRA |= bit (ISC00);// Set interrupt on rising and falling
lds MTR, EICRA
ori MTR, 0B00000001
sts EICRA, MTR
;Interrupt 1 for clock (PD1, pin 2) (TR S1 on Saturn)
;EICRA &= ~(bit(ISC10) | bit (ISC11)); // Clear existing flags of interrupt 1
lds MTR, EICRA
andi MTR, 0B11110011
sts EICRA, MTR
;EICRA |= bit (ISC10); //Set interrupt on rising and falling
lds MTR, EICRA
ori MTR, 0B00000100
sts EICRA, MTR
;Enable both interrupts
in MTR, EIMSK
ori MTR, 0B00000011
out EIMSK, MTR
sei
;Default outputs to high which means unpressed.
ldi OUTPUT0,0B11110010 ;ZYXR--T-
ldi OUTPUT1,0B01110010 ;UDLR--T-
ldi OUTPUT2,0B11110010 ;BCAS--T-
ldi OUTPUT3,0B00110010 ;001L--T- First 3 bits are hardcoded
jmp setupoutputs;
;During the main loop, just continuosly update the values of the outputs so they're ready when the interrupts fire.
setupoutputs:
;0:0 ZYXR--T-
;PIND = Z--YXR--
;OUTPUTS[0] = ((PIND & B10000010) | ((PIND & B00011100) << 2)) | B00000010;
in MTR, PIND
in PAIRL, PIND
andi MTR, 0B10000010
ori MTR, 0B00000010;
ldi TEMP, DDRB
mul PAIRL, TEMP ;Uses temporary registers SCRH and ZERO for the multiply
movw PAIRL, SCRH ;moves SCRH:ZERO to PAIRL:PAIRH
eor ZERO, ZERO ;RESETS ZERO REGISTER
andi PAIRL, 0B01110000
or MTR, PAIRL
mov OUTPUT0,MTR ;ZYXR--T-
;0:1 UDLR--T-
;PINB = ---UDLR-
;OUTPUTS[1] = ((PINB & B00011110) << 3) | B00000010;
in MTR, PINB
ldi PAIRL, PORTC
mul MTR, PAIRL
movw MTR, SCRH
eor ZERO, ZERO
andi MTR, 0B11110000
ori MTR, 0B00000010
mov OUTPUT1,MTR ;UDLR--T-
;1:0 BCAS--T-
;PINB = BCA-----
;PINE = -S------
;OUTPUTS[2] = ((PINB & B11100000) | ((PINE & B01000000) >> 2)) | B00000010;
in MTR, PINB
in TEMP, PINE
andi MTR, 0B11100000
ori MTR, 0B00000010
lsr TEMP
lsr TEMP
andi TEMP, 0B00010000
or MTR, TEMP
mov OUTPUT2,MTR
;1:1 001L--T-
;PINC = -L------
;For this one in particular we need to set 001L according to the documentation here (page 97):
;https://cdn.preterhuman.net/texts/gaming_and_diversion/CONSOLES/sega/ST-169-R1-072694.pdf
;OUTPUTS[3] = (((PINC & B01000000) >> 2) | B00100010);
in MTR, PINC
lsr MTR
lsr MTR
andi MTR, 0B00010000
ori MTR, 0B00100010
mov OUTPUT3,MTR ;001L--T- First 3 bits are hardcoded
rjmp setupoutputs
_exit:
cli