-
Notifications
You must be signed in to change notification settings - Fork 2
/
pcm-io.s
171 lines (136 loc) · 3.82 KB
/
pcm-io.s
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
/* PCM channel increment */
.equ PCM_FDL, 0xFF0005
.equ PCM_FDH, 0xFF0007
/* General use timer */
.equ TIMER, 0x8030
.equ INT_MASK, 0x8032
.equ _LEVEL3, 0x5F82 /* TIMER INTERRUPT jump vector */
.text
.align 2
| uint8_t pcm_lcf(uint8_t pan);
| Convert pan setting to simple linear crossfade volume settings
.global pcm_lcf
pcm_lcf:
move.l 4(sp),d0 /* pan */
addq.b #8,d0
bcc.b 0f
move.l 4(sp),d0
0:
andi.w #0x00F0,d0 /* right pan volume nibble */
move.l 4(sp),d1
not.b d1
addq.b #8,d1
bcc.b 1f
move.l 4(sp),d1
not.b d1
1:
lsr.b #4,d1 /* left pan volume nibble */
or.b d1,d0
rts
| void pcm_delay(void);
| Delay after writing PCM register
.global pcm_delay
pcm_delay:
moveq #20,d1
0:
dbra d1,0b
rts
| void pcm_set_period(uint32_t period);
.global pcm_set_period
pcm_set_period:
move.l 4(sp),d1
cmpi.l #4,d1
blo.b 0f /* saturate increment to 65535 */
addq.l #1,d1
move.l #446304,d0
divu.w d1,d0
lsr.w #1,d0 /* incr = (446304 / Period + 1) >> 1 */
bra.b 1f
0:
move.w #65535,d0
1:
move.b d0,PCM_FDL.l
bsr.b pcm_delay
lsr.w #8,d0
move.b d0,PCM_FDH.l
bra.b pcm_delay
| void pcm_set_freq(uint32_t freq);
.global pcm_set_freq
pcm_set_freq:
move.l 4(sp),d0
cmpi.l #1041648,d0
bhs.b 0f /* saturate increment to 65535 */
lsl.l #8,d0
lsl.l #3,d0 /* shift freq for fixed point result */
move.w #32552,d1
divu.w d1,d0 /* incr = (freq << 11) / 32552 */
bra.b 1f
0:
move.w #65535,d0
1:
move.b d0,PCM_FDL.l
bsr.b pcm_delay
lsr.w #8,d0
move.b d0,PCM_FDH.l
bra.b pcm_delay
| void pcm_set_timer(uint16_t bpm);
| TIMER = 32552 / (bpm * 2) - 1
| should be 32552 * 5 / (bpm * 2) - 1, but that's too big for TIMER, so
| we leave out the * 5 and compensate in the int handler
.global pcm_set_timer
pcm_set_timer:
move.l #32552,d0
move.l 4(sp),d1
add.w d1,d1
beq.b 0f /* safety check... passing 0 will just exit */
divu.w d1,d0
subq.w #1,d0
ble.b 0f /* safety check */
move.w d0,TIMER.w
0:
rts
| void pcm_stop_timer(void);
.global pcm_stop_timer
pcm_stop_timer:
move #0x2700,sr /* disable interrupts */
move.w #0,TIMER.w /* stop General Timer */
move.w INT_MASK.w,d0
andi.w #0xFFF7,d0
move.w d0,INT_MASK.w /* disable General Timer interrupt */
move #0x2000,sr /* enable interrupts */
rts
| void pcm_start_timer(void (*callback)(void));
.global pcm_start_timer
pcm_start_timer:
move #0x2700,sr /* disable interrupts */
move.l 4(sp),int3_callback /* set callback vector */
move.w #0,int3_cntr /* clear int counter */
move.w #0x4EF9,_LEVEL3.w
move.l #timer_int,_LEVEL3+2.w /* set level 3 int vector for timer */
move.w #129,TIMER.w /* 125 BPM */
move.w INT_MASK.w,d0
ori.w #0x0008,d0
move.w d0,INT_MASK.w /* enable General Timer interrupt */
move #0x2000,sr /* enable interrupts */
rts
timer_int:
move.l d0,-(sp)
move.w int3_cntr,d0
addq.w #1,d0
cmpi.w #5,d0
blo.b 0f /* once every 5 ints for actual beats per minute rate */
movem.l d1/a0-a1,-(sp)
movea.l int3_callback,a1
jsr (a1)
movem.l (sp)+,d1/a0-a1
moveq #0,d0
0:
move.w d0,int3_cntr
move.l (sp)+,d0
rte
.data
.align 4
int3_callback:
.long 0
int3_cntr:
.word 0