@aptre/v86 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/LICENSE.MIT +22 -0
- package/Readme.md +237 -0
- package/dist/v86.browser.js +26666 -0
- package/dist/v86.browser.js.map +7 -0
- package/dist/v86.js +26632 -0
- package/dist/v86.js.map +7 -0
- package/gen/generate_analyzer.ts +512 -0
- package/gen/generate_interpreter.ts +522 -0
- package/gen/generate_jit.ts +624 -0
- package/gen/rust_ast.ts +107 -0
- package/gen/util.ts +35 -0
- package/gen/x86_table.ts +1836 -0
- package/lib/9p.ts +1547 -0
- package/lib/filesystem.ts +1879 -0
- package/lib/marshall.ts +168 -0
- package/lib/softfloat/softfloat.c +32501 -0
- package/lib/zstd/zstddeclib.c +13520 -0
- package/package.json +75 -0
- package/src/acpi.ts +267 -0
- package/src/browser/dummy_screen.ts +106 -0
- package/src/browser/fake_network.ts +1771 -0
- package/src/browser/fetch_network.ts +361 -0
- package/src/browser/filestorage.ts +124 -0
- package/src/browser/inbrowser_network.ts +57 -0
- package/src/browser/keyboard.ts +564 -0
- package/src/browser/main.ts +3415 -0
- package/src/browser/mouse.ts +255 -0
- package/src/browser/network.ts +142 -0
- package/src/browser/print_stats.ts +336 -0
- package/src/browser/screen.ts +978 -0
- package/src/browser/serial.ts +316 -0
- package/src/browser/speaker.ts +1223 -0
- package/src/browser/starter.ts +1688 -0
- package/src/browser/wisp_network.ts +332 -0
- package/src/browser/worker_bus.ts +64 -0
- package/src/buffer.ts +652 -0
- package/src/bus.ts +78 -0
- package/src/const.ts +128 -0
- package/src/cpu.ts +2891 -0
- package/src/dma.ts +474 -0
- package/src/elf.ts +251 -0
- package/src/floppy.ts +1778 -0
- package/src/ide.ts +3455 -0
- package/src/io.ts +504 -0
- package/src/iso9660.ts +317 -0
- package/src/kernel.ts +250 -0
- package/src/lib.ts +645 -0
- package/src/log.ts +149 -0
- package/src/main.ts +199 -0
- package/src/ne2k.ts +1589 -0
- package/src/pci.ts +815 -0
- package/src/pit.ts +406 -0
- package/src/ps2.ts +820 -0
- package/src/rtc.ts +537 -0
- package/src/rust/analysis.rs +101 -0
- package/src/rust/codegen.rs +2660 -0
- package/src/rust/config.rs +3 -0
- package/src/rust/control_flow.rs +425 -0
- package/src/rust/cpu/apic.rs +658 -0
- package/src/rust/cpu/arith.rs +1207 -0
- package/src/rust/cpu/call_indirect.rs +2 -0
- package/src/rust/cpu/cpu.rs +4501 -0
- package/src/rust/cpu/fpu.rs +923 -0
- package/src/rust/cpu/global_pointers.rs +112 -0
- package/src/rust/cpu/instructions.rs +2486 -0
- package/src/rust/cpu/instructions_0f.rs +5261 -0
- package/src/rust/cpu/ioapic.rs +316 -0
- package/src/rust/cpu/memory.rs +351 -0
- package/src/rust/cpu/misc_instr.rs +613 -0
- package/src/rust/cpu/mod.rs +16 -0
- package/src/rust/cpu/modrm.rs +133 -0
- package/src/rust/cpu/pic.rs +402 -0
- package/src/rust/cpu/sse_instr.rs +361 -0
- package/src/rust/cpu/string.rs +701 -0
- package/src/rust/cpu/vga.rs +175 -0
- package/src/rust/cpu_context.rs +69 -0
- package/src/rust/dbg.rs +98 -0
- package/src/rust/gen/analyzer.rs +3807 -0
- package/src/rust/gen/analyzer0f.rs +3992 -0
- package/src/rust/gen/interpreter.rs +4447 -0
- package/src/rust/gen/interpreter0f.rs +5404 -0
- package/src/rust/gen/jit.rs +5080 -0
- package/src/rust/gen/jit0f.rs +5547 -0
- package/src/rust/gen/mod.rs +14 -0
- package/src/rust/jit.rs +2443 -0
- package/src/rust/jit_instructions.rs +7881 -0
- package/src/rust/js_api.rs +6 -0
- package/src/rust/leb.rs +46 -0
- package/src/rust/lib.rs +29 -0
- package/src/rust/modrm.rs +330 -0
- package/src/rust/opstats.rs +249 -0
- package/src/rust/page.rs +15 -0
- package/src/rust/paging.rs +25 -0
- package/src/rust/prefix.rs +15 -0
- package/src/rust/profiler.rs +155 -0
- package/src/rust/regs.rs +38 -0
- package/src/rust/softfloat.rs +286 -0
- package/src/rust/state_flags.rs +27 -0
- package/src/rust/wasmgen/mod.rs +2 -0
- package/src/rust/wasmgen/wasm_builder.rs +1047 -0
- package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
- package/src/rust/zstd.rs +105 -0
- package/src/sb16.ts +1928 -0
- package/src/state.ts +359 -0
- package/src/uart.ts +472 -0
- package/src/vga.ts +2791 -0
- package/src/virtio.ts +1756 -0
- package/src/virtio_balloon.ts +273 -0
- package/src/virtio_console.ts +372 -0
- package/src/virtio_net.ts +326 -0
package/src/sb16.ts
ADDED
|
@@ -0,0 +1,1928 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LOG_SB16,
|
|
3
|
+
MIXER_CHANNEL_BOTH,
|
|
4
|
+
MIXER_CHANNEL_LEFT,
|
|
5
|
+
MIXER_CHANNEL_RIGHT,
|
|
6
|
+
MIXER_SRC_PCSPEAKER,
|
|
7
|
+
MIXER_SRC_DAC,
|
|
8
|
+
MIXER_SRC_MASTER,
|
|
9
|
+
} from './const.js'
|
|
10
|
+
import { h } from './lib.js'
|
|
11
|
+
import { dbg_log } from './log.js'
|
|
12
|
+
import { SyncBuffer } from './buffer.js'
|
|
13
|
+
|
|
14
|
+
import { DMA } from './dma.js'
|
|
15
|
+
import { IO } from './io.js'
|
|
16
|
+
import { BusConnector } from './bus.js'
|
|
17
|
+
import { ByteQueue, FloatQueue } from './lib.js'
|
|
18
|
+
|
|
19
|
+
// Minimal interface for the CPU fields SB16 uses.
|
|
20
|
+
interface SB16Cpu {
|
|
21
|
+
io: IO
|
|
22
|
+
devices: { dma: DMA }
|
|
23
|
+
device_raise_irq(irq: number): void
|
|
24
|
+
device_lower_irq(irq: number): void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Useful documentation, articles, and source codes for reference:
|
|
28
|
+
// ===============================================================
|
|
29
|
+
//
|
|
30
|
+
// Official Hardware Programming Guide
|
|
31
|
+
// -> https://pdos.csail.mit.edu/6.828/2011/readings/hardware/SoundBlaster.pdf
|
|
32
|
+
//
|
|
33
|
+
// Official Yamaha YMF262 Manual
|
|
34
|
+
// -> http://map.grauw.nl/resources/sound/yamaha_ymf262.pdf
|
|
35
|
+
//
|
|
36
|
+
// OPL3 Programming Guide
|
|
37
|
+
// -> http://www.fit.vutbr.cz/~arnost/opl/opl3.html
|
|
38
|
+
//
|
|
39
|
+
// DOSBox
|
|
40
|
+
// -> https://sourceforge.net/p/dosbox/code-0/HEAD/tree/dosbox/branches/mamesound/src/hardware/sblaster.cpp
|
|
41
|
+
// -> https://github.com/duganchen/dosbox/blob/master/src/hardware/sblaster.cpp
|
|
42
|
+
// -> https://github.com/joncampbell123/dosbox-x/blob/master/src/hardware/sblaster.cpp
|
|
43
|
+
//
|
|
44
|
+
// QEMU
|
|
45
|
+
// -> https://github.com/qemu/qemu/blob/master/hw/audio/sb16.c
|
|
46
|
+
// -> https://github.com/hackndev/qemu/blob/master/hw/sb16.c
|
|
47
|
+
//
|
|
48
|
+
// VirtualBox
|
|
49
|
+
// -> https://www.virtualbox.org/svn/vbox/trunk/src/VBox/Devices/Audio/DevSB16.cpp
|
|
50
|
+
// -> https://github.com/mdaniel/virtualbox-org-svn-vbox-trunk/blob/master/src/VBox/Devices/Audio/DevSB16.cpp
|
|
51
|
+
|
|
52
|
+
const // Used for drivers to identify device (DSP command 0xE3).
|
|
53
|
+
DSP_COPYRIGHT = 'COPYRIGHT (C) CREATIVE TECHNOLOGY LTD, 1992.',
|
|
54
|
+
// Value of the current DSP command that indicates that the
|
|
55
|
+
// next command/data write in port 2xC should be interpreted
|
|
56
|
+
// as a command number.
|
|
57
|
+
DSP_NO_COMMAND = 0,
|
|
58
|
+
// Size (bytes) of the DSP write/read buffers
|
|
59
|
+
DSP_BUFSIZE = 64,
|
|
60
|
+
// Size (bytes) of the buffers containing floating point linear PCM audio.
|
|
61
|
+
DSP_DACSIZE = 65536,
|
|
62
|
+
// Size (bytes) of the buffer in which DMA transfers are temporarily
|
|
63
|
+
// stored before being processed.
|
|
64
|
+
SB_DMA_BUFSIZE = 65536,
|
|
65
|
+
// Number of samples to attempt to retrieve per transfer.
|
|
66
|
+
SB_DMA_BLOCK_SAMPLES = 1024,
|
|
67
|
+
// Usable DMA channels.
|
|
68
|
+
SB_DMA0 = 0,
|
|
69
|
+
SB_DMA1 = 1,
|
|
70
|
+
SB_DMA3 = 3,
|
|
71
|
+
SB_DMA5 = 5,
|
|
72
|
+
SB_DMA6 = 6,
|
|
73
|
+
SB_DMA7 = 7,
|
|
74
|
+
// Default DMA channels.
|
|
75
|
+
SB_DMA_CHANNEL_8BIT = SB_DMA1,
|
|
76
|
+
SB_DMA_CHANNEL_16BIT = SB_DMA5,
|
|
77
|
+
// Usable IRQ channels.
|
|
78
|
+
SB_IRQ2 = 2,
|
|
79
|
+
SB_IRQ5 = 5,
|
|
80
|
+
SB_IRQ7 = 7,
|
|
81
|
+
SB_IRQ10 = 10,
|
|
82
|
+
// Default IRQ channel.
|
|
83
|
+
SB_IRQ = SB_IRQ5,
|
|
84
|
+
// Indices to the irq_triggered register.
|
|
85
|
+
SB_IRQ_8BIT = 0x1,
|
|
86
|
+
SB_IRQ_16BIT = 0x2,
|
|
87
|
+
_SB_IRQ_MIDI = 0x1,
|
|
88
|
+
_SB_IRQ_MPU = 0x4
|
|
89
|
+
|
|
90
|
+
// Probably less efficient, but it's more maintainable, instead
|
|
91
|
+
// of having a single large unorganised and decoupled table.
|
|
92
|
+
const DSP_COMMAND_SIZES = new Uint8Array(256)
|
|
93
|
+
const DSP_COMMAND_HANDLERS: Array<((this: SB16) => void) | undefined> = []
|
|
94
|
+
const MIXER_READ_HANDLERS: Array<((this: SB16) => number) | undefined> = []
|
|
95
|
+
const MIXER_WRITE_HANDLERS: Array<
|
|
96
|
+
((this: SB16, data: number) => void) | undefined
|
|
97
|
+
> = []
|
|
98
|
+
const MIXER_REGISTER_IS_LEGACY = new Uint8Array(256)
|
|
99
|
+
const FM_HANDLERS: Array<
|
|
100
|
+
| ((this: SB16, data: number, register: number, address: number) => void)
|
|
101
|
+
| undefined
|
|
102
|
+
> = []
|
|
103
|
+
|
|
104
|
+
// Sound Blaster 16 Emulator, or so it seems.
|
|
105
|
+
export class SB16 {
|
|
106
|
+
cpu: SB16Cpu
|
|
107
|
+
bus: BusConnector
|
|
108
|
+
|
|
109
|
+
// I/O Buffers.
|
|
110
|
+
write_buffer: ByteQueue
|
|
111
|
+
read_buffer: ByteQueue
|
|
112
|
+
read_buffer_lastvalue: number
|
|
113
|
+
|
|
114
|
+
// Current DSP command info.
|
|
115
|
+
command: number
|
|
116
|
+
command_size: number
|
|
117
|
+
|
|
118
|
+
// Mixer.
|
|
119
|
+
mixer_current_address: number
|
|
120
|
+
mixer_registers: Uint8Array
|
|
121
|
+
|
|
122
|
+
// Dummy status and test registers.
|
|
123
|
+
dummy_speaker_enabled: boolean
|
|
124
|
+
test_register: number
|
|
125
|
+
|
|
126
|
+
// DSP state.
|
|
127
|
+
dsp_highspeed: boolean
|
|
128
|
+
dsp_stereo: boolean
|
|
129
|
+
dsp_16bit: boolean
|
|
130
|
+
dsp_signed: boolean
|
|
131
|
+
|
|
132
|
+
// DAC buffer.
|
|
133
|
+
// The final destination for audio data before being sent off
|
|
134
|
+
// to Web Audio APIs.
|
|
135
|
+
// Format:
|
|
136
|
+
// Floating precision linear PCM, nominal between -1 and 1.
|
|
137
|
+
dac_buffers: [FloatQueue, FloatQueue]
|
|
138
|
+
|
|
139
|
+
// Direct Memory Access transfer info.
|
|
140
|
+
dma: DMA
|
|
141
|
+
dma_sample_count: number
|
|
142
|
+
dma_bytes_count: number
|
|
143
|
+
dma_bytes_left: number
|
|
144
|
+
dma_bytes_block: number
|
|
145
|
+
dma_irq: number
|
|
146
|
+
dma_channel: number
|
|
147
|
+
dma_channel_8bit: number
|
|
148
|
+
dma_channel_16bit: number
|
|
149
|
+
dma_autoinit: boolean
|
|
150
|
+
dma_buffer: ArrayBuffer
|
|
151
|
+
dma_buffer_int8: Int8Array
|
|
152
|
+
dma_buffer_uint8: Uint8Array
|
|
153
|
+
dma_buffer_int16: Int16Array
|
|
154
|
+
dma_buffer_uint16: Uint16Array
|
|
155
|
+
dma_syncbuffer: SyncBuffer
|
|
156
|
+
dma_waiting_transfer: boolean
|
|
157
|
+
dma_paused: boolean
|
|
158
|
+
sampling_rate: number
|
|
159
|
+
bytes_per_sample: number
|
|
160
|
+
|
|
161
|
+
// DMA identification data.
|
|
162
|
+
e2_value: number
|
|
163
|
+
e2_count: number
|
|
164
|
+
|
|
165
|
+
// ASP data: not understood by me.
|
|
166
|
+
asp_registers: Uint8Array
|
|
167
|
+
|
|
168
|
+
// MPU.
|
|
169
|
+
mpu_read_buffer: ByteQueue
|
|
170
|
+
mpu_read_buffer_lastvalue: number
|
|
171
|
+
// Bug-compatible: get_state/set_state reference a different spelling.
|
|
172
|
+
mpu_read_buffer_last_value: number | undefined
|
|
173
|
+
|
|
174
|
+
// FM Synthesizer.
|
|
175
|
+
fm_current_address0: number
|
|
176
|
+
fm_current_address1: number
|
|
177
|
+
fm_waveform_select_enable: Record<number, number>
|
|
178
|
+
|
|
179
|
+
// Interrupts.
|
|
180
|
+
irq: number
|
|
181
|
+
irq_triggered: Uint8Array
|
|
182
|
+
|
|
183
|
+
name: string | undefined
|
|
184
|
+
|
|
185
|
+
constructor(cpu: SB16Cpu, bus: BusConnector) {
|
|
186
|
+
this.cpu = cpu
|
|
187
|
+
this.bus = bus
|
|
188
|
+
|
|
189
|
+
// I/O Buffers.
|
|
190
|
+
this.write_buffer = new ByteQueue(DSP_BUFSIZE)
|
|
191
|
+
this.read_buffer = new ByteQueue(DSP_BUFSIZE)
|
|
192
|
+
this.read_buffer_lastvalue = 0
|
|
193
|
+
|
|
194
|
+
// Current DSP command info.
|
|
195
|
+
this.command = DSP_NO_COMMAND
|
|
196
|
+
this.command_size = 0
|
|
197
|
+
|
|
198
|
+
// Mixer.
|
|
199
|
+
this.mixer_current_address = 0
|
|
200
|
+
this.mixer_registers = new Uint8Array(256)
|
|
201
|
+
this.mixer_reset()
|
|
202
|
+
|
|
203
|
+
// Dummy status and test registers.
|
|
204
|
+
this.dummy_speaker_enabled = false
|
|
205
|
+
this.test_register = 0
|
|
206
|
+
|
|
207
|
+
// DSP state.
|
|
208
|
+
this.dsp_highspeed = false
|
|
209
|
+
this.dsp_stereo = false
|
|
210
|
+
this.dsp_16bit = false
|
|
211
|
+
this.dsp_signed = false
|
|
212
|
+
|
|
213
|
+
// DAC buffer.
|
|
214
|
+
this.dac_buffers = [
|
|
215
|
+
new FloatQueue(DSP_DACSIZE),
|
|
216
|
+
new FloatQueue(DSP_DACSIZE),
|
|
217
|
+
]
|
|
218
|
+
|
|
219
|
+
// Direct Memory Access transfer info.
|
|
220
|
+
this.dma = cpu.devices.dma
|
|
221
|
+
this.dma_sample_count = 0
|
|
222
|
+
this.dma_bytes_count = 0
|
|
223
|
+
this.dma_bytes_left = 0
|
|
224
|
+
this.dma_bytes_block = 0
|
|
225
|
+
this.dma_irq = 0
|
|
226
|
+
this.dma_channel = 0
|
|
227
|
+
this.dma_channel_8bit = SB_DMA_CHANNEL_8BIT
|
|
228
|
+
this.dma_channel_16bit = SB_DMA_CHANNEL_16BIT
|
|
229
|
+
this.dma_autoinit = false
|
|
230
|
+
this.dma_buffer = new ArrayBuffer(SB_DMA_BUFSIZE)
|
|
231
|
+
this.dma_buffer_int8 = new Int8Array(this.dma_buffer)
|
|
232
|
+
this.dma_buffer_uint8 = new Uint8Array(this.dma_buffer)
|
|
233
|
+
this.dma_buffer_int16 = new Int16Array(this.dma_buffer)
|
|
234
|
+
this.dma_buffer_uint16 = new Uint16Array(this.dma_buffer)
|
|
235
|
+
this.dma_syncbuffer = new SyncBuffer(this.dma_buffer)
|
|
236
|
+
this.dma_waiting_transfer = false
|
|
237
|
+
this.dma_paused = false
|
|
238
|
+
this.sampling_rate = 22050
|
|
239
|
+
bus.send('dac-tell-sampling-rate', this.sampling_rate)
|
|
240
|
+
this.bytes_per_sample = 1
|
|
241
|
+
|
|
242
|
+
// DMA identification data.
|
|
243
|
+
this.e2_value = 0xaa
|
|
244
|
+
this.e2_count = 0
|
|
245
|
+
|
|
246
|
+
// ASP data: not understood by me.
|
|
247
|
+
this.asp_registers = new Uint8Array(256)
|
|
248
|
+
|
|
249
|
+
// MPU.
|
|
250
|
+
this.mpu_read_buffer = new ByteQueue(DSP_BUFSIZE)
|
|
251
|
+
this.mpu_read_buffer_lastvalue = 0
|
|
252
|
+
|
|
253
|
+
// FM Synthesizer.
|
|
254
|
+
this.fm_current_address0 = 0
|
|
255
|
+
this.fm_current_address1 = 0
|
|
256
|
+
this.fm_waveform_select_enable = {}
|
|
257
|
+
|
|
258
|
+
// Interrupts.
|
|
259
|
+
this.irq = SB_IRQ
|
|
260
|
+
this.irq_triggered = new Uint8Array(0x10)
|
|
261
|
+
|
|
262
|
+
// IO Ports.
|
|
263
|
+
// http://homepages.cae.wisc.edu/~brodskye/sb16doc/sb16doc.html#DSPPorts
|
|
264
|
+
// https://pdos.csail.mit.edu/6.828/2011/readings/hardware/SoundBlaster.pdf
|
|
265
|
+
|
|
266
|
+
cpu.io.register_read_consecutive(
|
|
267
|
+
0x220,
|
|
268
|
+
this,
|
|
269
|
+
this.port2x0_read,
|
|
270
|
+
this.port2x1_read,
|
|
271
|
+
this.port2x2_read,
|
|
272
|
+
this.port2x3_read,
|
|
273
|
+
)
|
|
274
|
+
cpu.io.register_read_consecutive(
|
|
275
|
+
0x388,
|
|
276
|
+
this,
|
|
277
|
+
this.port2x0_read,
|
|
278
|
+
this.port2x1_read,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
cpu.io.register_read_consecutive(
|
|
282
|
+
0x224,
|
|
283
|
+
this,
|
|
284
|
+
this.port2x4_read,
|
|
285
|
+
this.port2x5_read,
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
cpu.io.register_read(0x226, this, this.port2x6_read)
|
|
289
|
+
cpu.io.register_read(0x227, this, this.port2x7_read)
|
|
290
|
+
cpu.io.register_read(0x228, this, this.port2x8_read)
|
|
291
|
+
cpu.io.register_read(0x229, this, this.port2x9_read)
|
|
292
|
+
|
|
293
|
+
cpu.io.register_read(0x22a, this, this.port2xA_read)
|
|
294
|
+
cpu.io.register_read(0x22b, this, this.port2xB_read)
|
|
295
|
+
cpu.io.register_read(0x22c, this, this.port2xC_read)
|
|
296
|
+
cpu.io.register_read(0x22d, this, this.port2xD_read)
|
|
297
|
+
|
|
298
|
+
cpu.io.register_read_consecutive(
|
|
299
|
+
0x22e,
|
|
300
|
+
this,
|
|
301
|
+
this.port2xE_read,
|
|
302
|
+
this.port2xF_read,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
cpu.io.register_write_consecutive(
|
|
306
|
+
0x220,
|
|
307
|
+
this,
|
|
308
|
+
this.port2x0_write,
|
|
309
|
+
this.port2x1_write,
|
|
310
|
+
this.port2x2_write,
|
|
311
|
+
this.port2x3_write,
|
|
312
|
+
)
|
|
313
|
+
cpu.io.register_write_consecutive(
|
|
314
|
+
0x388,
|
|
315
|
+
this,
|
|
316
|
+
this.port2x0_write,
|
|
317
|
+
this.port2x1_write,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
cpu.io.register_write_consecutive(
|
|
321
|
+
0x224,
|
|
322
|
+
this,
|
|
323
|
+
this.port2x4_write,
|
|
324
|
+
this.port2x5_write,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
cpu.io.register_write(0x226, this, this.port2x6_write)
|
|
328
|
+
cpu.io.register_write(0x227, this, this.port2x7_write)
|
|
329
|
+
|
|
330
|
+
cpu.io.register_write_consecutive(
|
|
331
|
+
0x228,
|
|
332
|
+
this,
|
|
333
|
+
this.port2x8_write,
|
|
334
|
+
this.port2x9_write,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
cpu.io.register_write(0x22a, this, this.port2xA_write)
|
|
338
|
+
cpu.io.register_write(0x22b, this, this.port2xB_write)
|
|
339
|
+
cpu.io.register_write(0x22c, this, this.port2xC_write)
|
|
340
|
+
cpu.io.register_write(0x22d, this, this.port2xD_write)
|
|
341
|
+
cpu.io.register_write(0x22e, this, this.port2xE_write)
|
|
342
|
+
cpu.io.register_write(0x22f, this, this.port2xF_write)
|
|
343
|
+
|
|
344
|
+
cpu.io.register_read_consecutive(
|
|
345
|
+
0x330,
|
|
346
|
+
this,
|
|
347
|
+
this.port3x0_read,
|
|
348
|
+
this.port3x1_read,
|
|
349
|
+
)
|
|
350
|
+
cpu.io.register_write_consecutive(
|
|
351
|
+
0x330,
|
|
352
|
+
this,
|
|
353
|
+
this.port3x0_write,
|
|
354
|
+
this.port3x1_write,
|
|
355
|
+
)
|
|
356
|
+
|
|
357
|
+
this.dma.on_unmask(this.dma_on_unmask, this)
|
|
358
|
+
|
|
359
|
+
bus.register(
|
|
360
|
+
'dac-request-data',
|
|
361
|
+
function (this: SB16) {
|
|
362
|
+
this.dac_handle_request()
|
|
363
|
+
},
|
|
364
|
+
this,
|
|
365
|
+
)
|
|
366
|
+
bus.register(
|
|
367
|
+
'speaker-has-initialized',
|
|
368
|
+
function (this: SB16) {
|
|
369
|
+
this.mixer_reset()
|
|
370
|
+
},
|
|
371
|
+
this,
|
|
372
|
+
)
|
|
373
|
+
bus.send('speaker-confirm-initialized')
|
|
374
|
+
|
|
375
|
+
this.dsp_reset()
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
//
|
|
379
|
+
// General
|
|
380
|
+
//
|
|
381
|
+
|
|
382
|
+
dsp_reset(): void {
|
|
383
|
+
this.write_buffer.clear()
|
|
384
|
+
this.read_buffer.clear()
|
|
385
|
+
|
|
386
|
+
this.command = DSP_NO_COMMAND
|
|
387
|
+
this.command_size = 0
|
|
388
|
+
|
|
389
|
+
this.dummy_speaker_enabled = false
|
|
390
|
+
this.test_register = 0
|
|
391
|
+
|
|
392
|
+
this.dsp_highspeed = false
|
|
393
|
+
this.dsp_stereo = false
|
|
394
|
+
this.dsp_16bit = false
|
|
395
|
+
this.dsp_signed = false
|
|
396
|
+
|
|
397
|
+
this.dac_buffers[0].clear()
|
|
398
|
+
this.dac_buffers[1].clear()
|
|
399
|
+
|
|
400
|
+
this.dma_sample_count = 0
|
|
401
|
+
this.dma_bytes_count = 0
|
|
402
|
+
this.dma_bytes_left = 0
|
|
403
|
+
this.dma_bytes_block = 0
|
|
404
|
+
this.dma_irq = 0
|
|
405
|
+
this.dma_channel = 0
|
|
406
|
+
this.dma_autoinit = false
|
|
407
|
+
this.dma_buffer_uint8.fill(0)
|
|
408
|
+
this.dma_waiting_transfer = false
|
|
409
|
+
this.dma_paused = false
|
|
410
|
+
|
|
411
|
+
this.e2_value = 0xaa
|
|
412
|
+
this.e2_count = 0
|
|
413
|
+
|
|
414
|
+
this.sampling_rate = 22050
|
|
415
|
+
this.bytes_per_sample = 1
|
|
416
|
+
|
|
417
|
+
this.lower_irq(SB_IRQ_8BIT)
|
|
418
|
+
this.irq_triggered.fill(0)
|
|
419
|
+
|
|
420
|
+
this.asp_registers.fill(0)
|
|
421
|
+
this.asp_registers[5] = 0x01
|
|
422
|
+
this.asp_registers[9] = 0xf8
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
get_state(): unknown[] {
|
|
426
|
+
const state: unknown[] = []
|
|
427
|
+
|
|
428
|
+
// state[0] = this.write_buffer;
|
|
429
|
+
// state[1] = this.read_buffer;
|
|
430
|
+
state[2] = this.read_buffer_lastvalue
|
|
431
|
+
|
|
432
|
+
state[3] = this.command
|
|
433
|
+
state[4] = this.command_size
|
|
434
|
+
|
|
435
|
+
state[5] = this.mixer_current_address
|
|
436
|
+
state[6] = this.mixer_registers
|
|
437
|
+
|
|
438
|
+
state[7] = this.dummy_speaker_enabled
|
|
439
|
+
state[8] = this.test_register
|
|
440
|
+
|
|
441
|
+
state[9] = this.dsp_highspeed
|
|
442
|
+
state[10] = this.dsp_stereo
|
|
443
|
+
state[11] = this.dsp_16bit
|
|
444
|
+
state[12] = this.dsp_signed
|
|
445
|
+
|
|
446
|
+
// state[13] = this.dac_buffers;
|
|
447
|
+
//state[14]
|
|
448
|
+
|
|
449
|
+
state[15] = this.dma_sample_count
|
|
450
|
+
state[16] = this.dma_bytes_count
|
|
451
|
+
state[17] = this.dma_bytes_left
|
|
452
|
+
state[18] = this.dma_bytes_block
|
|
453
|
+
state[19] = this.dma_irq
|
|
454
|
+
state[20] = this.dma_channel
|
|
455
|
+
state[21] = this.dma_channel_8bit
|
|
456
|
+
state[22] = this.dma_channel_16bit
|
|
457
|
+
state[23] = this.dma_autoinit
|
|
458
|
+
state[24] = this.dma_buffer_uint8
|
|
459
|
+
state[25] = this.dma_waiting_transfer
|
|
460
|
+
state[26] = this.dma_paused
|
|
461
|
+
state[27] = this.sampling_rate
|
|
462
|
+
state[28] = this.bytes_per_sample
|
|
463
|
+
|
|
464
|
+
state[29] = this.e2_value
|
|
465
|
+
state[30] = this.e2_count
|
|
466
|
+
|
|
467
|
+
state[31] = this.asp_registers
|
|
468
|
+
|
|
469
|
+
// state[32] = this.mpu_read_buffer;
|
|
470
|
+
state[33] = this.mpu_read_buffer_last_value
|
|
471
|
+
|
|
472
|
+
state[34] = this.irq
|
|
473
|
+
state[35] = this.irq_triggered
|
|
474
|
+
//state[36]
|
|
475
|
+
|
|
476
|
+
return state
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
set_state(state: any[]): void {
|
|
480
|
+
// this.write_buffer = state[0];
|
|
481
|
+
// this.read_buffer = state[1];
|
|
482
|
+
this.read_buffer_lastvalue = state[2]
|
|
483
|
+
|
|
484
|
+
this.command = state[3]
|
|
485
|
+
this.command_size = state[4]
|
|
486
|
+
|
|
487
|
+
this.mixer_current_address = state[5]
|
|
488
|
+
this.mixer_registers = state[6]
|
|
489
|
+
this.mixer_full_update()
|
|
490
|
+
|
|
491
|
+
this.dummy_speaker_enabled = state[7]
|
|
492
|
+
this.test_register = state[8]
|
|
493
|
+
|
|
494
|
+
this.dsp_highspeed = state[9]
|
|
495
|
+
this.dsp_stereo = state[10]
|
|
496
|
+
this.dsp_16bit = state[11]
|
|
497
|
+
this.dsp_signed = state[12]
|
|
498
|
+
|
|
499
|
+
// this.dac_buffers = state[13];
|
|
500
|
+
//state[14]
|
|
501
|
+
|
|
502
|
+
this.dma_sample_count = state[15]
|
|
503
|
+
this.dma_bytes_count = state[16]
|
|
504
|
+
this.dma_bytes_left = state[17]
|
|
505
|
+
this.dma_bytes_block = state[18]
|
|
506
|
+
this.dma_irq = state[19]
|
|
507
|
+
this.dma_channel = state[20]
|
|
508
|
+
this.dma_channel_8bit = state[21]
|
|
509
|
+
this.dma_channel_16bit = state[22]
|
|
510
|
+
this.dma_autoinit = state[23]
|
|
511
|
+
this.dma_buffer_uint8 = state[24]
|
|
512
|
+
this.dma_waiting_transfer = state[25]
|
|
513
|
+
this.dma_paused = state[26]
|
|
514
|
+
this.sampling_rate = state[27]
|
|
515
|
+
this.bytes_per_sample = state[28]
|
|
516
|
+
|
|
517
|
+
this.e2_value = state[29]
|
|
518
|
+
this.e2_count = state[30]
|
|
519
|
+
|
|
520
|
+
this.asp_registers = state[31]
|
|
521
|
+
|
|
522
|
+
// this.mpu_read_buffer = state[32];
|
|
523
|
+
this.mpu_read_buffer_last_value = state[33]
|
|
524
|
+
|
|
525
|
+
this.irq = state[34]
|
|
526
|
+
this.irq_triggered = state[35]
|
|
527
|
+
//state[36];
|
|
528
|
+
|
|
529
|
+
const buf = this.dma_buffer_uint8.buffer
|
|
530
|
+
if (buf instanceof ArrayBuffer) {
|
|
531
|
+
this.dma_buffer = buf
|
|
532
|
+
}
|
|
533
|
+
this.dma_buffer_int8 = new Int8Array(this.dma_buffer)
|
|
534
|
+
this.dma_buffer_int16 = new Int16Array(this.dma_buffer)
|
|
535
|
+
this.dma_buffer_uint16 = new Uint16Array(this.dma_buffer)
|
|
536
|
+
this.dma_syncbuffer = new SyncBuffer(this.dma_buffer)
|
|
537
|
+
|
|
538
|
+
if (this.dma_paused) {
|
|
539
|
+
this.bus.send('dac-disable')
|
|
540
|
+
} else {
|
|
541
|
+
this.bus.send('dac-enable')
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
//
|
|
546
|
+
// I/O handlers
|
|
547
|
+
//
|
|
548
|
+
|
|
549
|
+
port2x0_read(): number {
|
|
550
|
+
dbg_log('220 read: fm music status port (unimplemented)', LOG_SB16)
|
|
551
|
+
return 0xff
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
port2x1_read(): number {
|
|
555
|
+
dbg_log('221 read: fm music data port (write only)', LOG_SB16)
|
|
556
|
+
return 0xff
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
port2x2_read(): number {
|
|
560
|
+
dbg_log(
|
|
561
|
+
'222 read: advanced fm music status port (unimplemented)',
|
|
562
|
+
LOG_SB16,
|
|
563
|
+
)
|
|
564
|
+
return 0xff
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
port2x3_read(): number {
|
|
568
|
+
dbg_log('223 read: advanced music data port (write only)', LOG_SB16)
|
|
569
|
+
return 0xff
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Mixer Address Port.
|
|
573
|
+
port2x4_read(): number {
|
|
574
|
+
dbg_log('224 read: mixer address port', LOG_SB16)
|
|
575
|
+
return this.mixer_current_address
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// Mixer Data Port.
|
|
579
|
+
port2x5_read(): number {
|
|
580
|
+
dbg_log('225 read: mixer data port', LOG_SB16)
|
|
581
|
+
return this.mixer_read(this.mixer_current_address)
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
port2x6_read(): number {
|
|
585
|
+
dbg_log('226 read: (write only)', LOG_SB16)
|
|
586
|
+
return 0xff
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
port2x7_read(): number {
|
|
590
|
+
dbg_log('227 read: undocumented', LOG_SB16)
|
|
591
|
+
return 0xff
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
port2x8_read(): number {
|
|
595
|
+
dbg_log('228 read: fm music status port (unimplemented)', LOG_SB16)
|
|
596
|
+
return 0xff
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
port2x9_read(): number {
|
|
600
|
+
dbg_log('229 read: fm music data port (write only)', LOG_SB16)
|
|
601
|
+
return 0xff
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Read Data.
|
|
605
|
+
// Used to access in-bound DSP data.
|
|
606
|
+
port2xA_read(): number {
|
|
607
|
+
dbg_log('22A read: read data', LOG_SB16)
|
|
608
|
+
if (this.read_buffer.length) {
|
|
609
|
+
this.read_buffer_lastvalue = this.read_buffer.shift()
|
|
610
|
+
}
|
|
611
|
+
dbg_log(
|
|
612
|
+
' <- ' +
|
|
613
|
+
this.read_buffer_lastvalue +
|
|
614
|
+
' ' +
|
|
615
|
+
h(this.read_buffer_lastvalue) +
|
|
616
|
+
" '" +
|
|
617
|
+
String.fromCharCode(this.read_buffer_lastvalue) +
|
|
618
|
+
"'",
|
|
619
|
+
LOG_SB16,
|
|
620
|
+
)
|
|
621
|
+
return this.read_buffer_lastvalue
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
port2xB_read(): number {
|
|
625
|
+
dbg_log('22B read: undocumented', LOG_SB16)
|
|
626
|
+
return 0xff
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// Write-Buffer Status.
|
|
630
|
+
// Indicates whether the DSP is ready to accept commands or data.
|
|
631
|
+
port2xC_read(): number {
|
|
632
|
+
dbg_log('22C read: write-buffer status', LOG_SB16)
|
|
633
|
+
// Always return ready (bit-7 set to low)
|
|
634
|
+
return 0x7f
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
port2xD_read(): number {
|
|
638
|
+
dbg_log('22D read: undocumented', LOG_SB16)
|
|
639
|
+
return 0xff
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Read-Buffer Status.
|
|
643
|
+
// Indicates whether there is any in-bound data available for reading.
|
|
644
|
+
// Also used to acknowledge DSP 8-bit interrupt.
|
|
645
|
+
port2xE_read(): number {
|
|
646
|
+
dbg_log('22E read: read-buffer status / irq 8bit ack.', LOG_SB16)
|
|
647
|
+
if (this.irq_triggered[SB_IRQ_8BIT]) {
|
|
648
|
+
this.lower_irq(SB_IRQ_8BIT)
|
|
649
|
+
}
|
|
650
|
+
const ready = this.read_buffer.length && !this.dsp_highspeed
|
|
651
|
+
return ((ready ? 1 : 0) << 7) | 0x7f
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// DSP 16-bit interrupt acknowledgement.
|
|
655
|
+
port2xF_read(): number {
|
|
656
|
+
dbg_log('22F read: irq 16bit ack', LOG_SB16)
|
|
657
|
+
this.lower_irq(SB_IRQ_16BIT)
|
|
658
|
+
return 0
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// FM Address Port - primary register.
|
|
662
|
+
port2x0_write(_value: number): void {
|
|
663
|
+
dbg_log(
|
|
664
|
+
'220 write: (unimplemented) fm register 0 address = ' + h(_value),
|
|
665
|
+
LOG_SB16,
|
|
666
|
+
)
|
|
667
|
+
this.fm_current_address0 = 0
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// FM Data Port - primary register.
|
|
671
|
+
port2x1_write(value: number): void {
|
|
672
|
+
dbg_log(
|
|
673
|
+
'221 write: (unimplemented) fm register 0 data = ' + h(value),
|
|
674
|
+
LOG_SB16,
|
|
675
|
+
)
|
|
676
|
+
let handler = FM_HANDLERS[this.fm_current_address0]
|
|
677
|
+
if (!handler) {
|
|
678
|
+
handler = this.fm_default_write
|
|
679
|
+
}
|
|
680
|
+
handler.call(this, value, 0, this.fm_current_address0)
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// FM Address Port - secondary register.
|
|
684
|
+
port2x2_write(_value: number): void {
|
|
685
|
+
dbg_log(
|
|
686
|
+
'222 write: (unimplemented) fm register 1 address = ' + h(_value),
|
|
687
|
+
LOG_SB16,
|
|
688
|
+
)
|
|
689
|
+
this.fm_current_address1 = 0
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// FM Data Port - secondary register.
|
|
693
|
+
port2x3_write(value: number): void {
|
|
694
|
+
dbg_log(
|
|
695
|
+
'223 write: (unimplemented) fm register 1 data =' + h(value),
|
|
696
|
+
LOG_SB16,
|
|
697
|
+
)
|
|
698
|
+
let handler = FM_HANDLERS[this.fm_current_address1]
|
|
699
|
+
if (!handler) {
|
|
700
|
+
handler = this.fm_default_write
|
|
701
|
+
}
|
|
702
|
+
handler.call(this, value, 1, this.fm_current_address1)
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// Mixer Address Port.
|
|
706
|
+
port2x4_write(value: number): void {
|
|
707
|
+
dbg_log('224 write: mixer address = ' + h(value), LOG_SB16)
|
|
708
|
+
this.mixer_current_address = value
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// Mixer Data Port.
|
|
712
|
+
port2x5_write(value: number): void {
|
|
713
|
+
dbg_log('225 write: mixer data = ' + h(value), LOG_SB16)
|
|
714
|
+
this.mixer_write(this.mixer_current_address, value)
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// Reset.
|
|
718
|
+
// Used to reset the DSP to its default state and to exit highspeed mode.
|
|
719
|
+
port2x6_write(yesplease: number): void {
|
|
720
|
+
dbg_log('226 write: reset = ' + h(yesplease), LOG_SB16)
|
|
721
|
+
|
|
722
|
+
if (this.dsp_highspeed) {
|
|
723
|
+
dbg_log(' -> exit highspeed', LOG_SB16)
|
|
724
|
+
this.dsp_highspeed = false
|
|
725
|
+
} else if (yesplease) {
|
|
726
|
+
dbg_log(' -> reset', LOG_SB16)
|
|
727
|
+
this.dsp_reset()
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Signal completion.
|
|
731
|
+
this.read_buffer.clear()
|
|
732
|
+
this.read_buffer.push(0xaa)
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
port2x7_write(_value: number): void {
|
|
736
|
+
dbg_log('227 write: undocumented', LOG_SB16)
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
port2x8_write(_value: number): void {
|
|
740
|
+
dbg_log('228 write: fm music register port (unimplemented)', LOG_SB16)
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
port2x9_write(_value: number): void {
|
|
744
|
+
dbg_log('229 write: fm music data port (unimplemented)', LOG_SB16)
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
port2xA_write(_value: number): void {
|
|
748
|
+
dbg_log('22A write: dsp read data port (read only)', LOG_SB16)
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
port2xB_write(_value: number): void {
|
|
752
|
+
dbg_log('22B write: undocumented', LOG_SB16)
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Write Command/Data.
|
|
756
|
+
// Used to send commands or data to the DSP.
|
|
757
|
+
port2xC_write(value: number): void {
|
|
758
|
+
dbg_log('22C write: write command/data', LOG_SB16)
|
|
759
|
+
|
|
760
|
+
if (this.command === DSP_NO_COMMAND) {
|
|
761
|
+
// New command.
|
|
762
|
+
dbg_log('22C write: command = ' + h(value), LOG_SB16)
|
|
763
|
+
this.command = value
|
|
764
|
+
this.write_buffer.clear()
|
|
765
|
+
this.command_size = DSP_COMMAND_SIZES[value]
|
|
766
|
+
} else {
|
|
767
|
+
// More data for current command.
|
|
768
|
+
dbg_log('22C write: data: ' + h(value), LOG_SB16)
|
|
769
|
+
this.write_buffer.push(value)
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// Perform command when we have all the needed data.
|
|
773
|
+
if (this.write_buffer.length >= this.command_size) {
|
|
774
|
+
this.command_do()
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
port2xD_write(_value: number): void {
|
|
779
|
+
dbg_log('22D write: undocumented', LOG_SB16)
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
port2xE_write(_value: number): void {
|
|
783
|
+
dbg_log('22E write: dsp read buffer status (read only)', LOG_SB16)
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
port2xF_write(_value: number): void {
|
|
787
|
+
dbg_log('22F write: undocumented', LOG_SB16)
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// MPU UART Mode - Data Port
|
|
791
|
+
port3x0_read(): number {
|
|
792
|
+
dbg_log('330 read: mpu data', LOG_SB16)
|
|
793
|
+
|
|
794
|
+
if (this.mpu_read_buffer.length) {
|
|
795
|
+
this.mpu_read_buffer_lastvalue = this.mpu_read_buffer.shift()
|
|
796
|
+
}
|
|
797
|
+
dbg_log(' <- ' + h(this.mpu_read_buffer_lastvalue), LOG_SB16)
|
|
798
|
+
|
|
799
|
+
return this.mpu_read_buffer_lastvalue
|
|
800
|
+
}
|
|
801
|
+
port3x0_write(value: number): void {
|
|
802
|
+
dbg_log('330 write: mpu data (unimplemented) : ' + h(value), LOG_SB16)
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
// MPU UART Mode - Status Port
|
|
806
|
+
port3x1_read(): number {
|
|
807
|
+
dbg_log('331 read: mpu status', LOG_SB16)
|
|
808
|
+
|
|
809
|
+
let status = 0
|
|
810
|
+
status |= 0x40 * 0 // Output Ready
|
|
811
|
+
status |= 0x80 * +!this.mpu_read_buffer.length // Input Ready
|
|
812
|
+
|
|
813
|
+
return status
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// MPU UART Mode - Command Port
|
|
817
|
+
port3x1_write(value: number): void {
|
|
818
|
+
dbg_log('331 write: mpu command: ' + h(value), LOG_SB16)
|
|
819
|
+
if (value === 0xff) {
|
|
820
|
+
// Command acknowledge.
|
|
821
|
+
this.mpu_read_buffer.clear()
|
|
822
|
+
this.mpu_read_buffer.push(0xfe)
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
//
|
|
827
|
+
// DSP command handlers
|
|
828
|
+
//
|
|
829
|
+
|
|
830
|
+
command_do(): void {
|
|
831
|
+
let handler = DSP_COMMAND_HANDLERS[this.command]
|
|
832
|
+
if (!handler) {
|
|
833
|
+
handler = this.dsp_default_handler
|
|
834
|
+
}
|
|
835
|
+
handler.call(this)
|
|
836
|
+
|
|
837
|
+
// Reset Inputs.
|
|
838
|
+
this.command = DSP_NO_COMMAND
|
|
839
|
+
this.command_size = 0
|
|
840
|
+
this.write_buffer.clear()
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
dsp_default_handler(): void {
|
|
844
|
+
dbg_log('Unhandled command: ' + h(this.command), LOG_SB16)
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
//
|
|
848
|
+
// Mixer Handlers (CT1745)
|
|
849
|
+
//
|
|
850
|
+
|
|
851
|
+
mixer_read(address: number): number {
|
|
852
|
+
const handler = MIXER_READ_HANDLERS[address]
|
|
853
|
+
let data: number
|
|
854
|
+
if (handler) {
|
|
855
|
+
data = handler.call(this)
|
|
856
|
+
} else {
|
|
857
|
+
data = this.mixer_registers[address]
|
|
858
|
+
dbg_log(
|
|
859
|
+
'unhandled mixer register read. addr:' +
|
|
860
|
+
h(address) +
|
|
861
|
+
' data:' +
|
|
862
|
+
h(data),
|
|
863
|
+
LOG_SB16,
|
|
864
|
+
)
|
|
865
|
+
}
|
|
866
|
+
return data
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
mixer_write(address: number, data: number): void {
|
|
870
|
+
const handler = MIXER_WRITE_HANDLERS[address]
|
|
871
|
+
if (handler) {
|
|
872
|
+
handler.call(this, data)
|
|
873
|
+
} else {
|
|
874
|
+
dbg_log(
|
|
875
|
+
'unhandled mixer register write. addr:' +
|
|
876
|
+
h(address) +
|
|
877
|
+
' data:' +
|
|
878
|
+
h(data),
|
|
879
|
+
LOG_SB16,
|
|
880
|
+
)
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
mixer_default_read(): number {
|
|
885
|
+
dbg_log(
|
|
886
|
+
'mixer register read. addr:' + h(this.mixer_current_address),
|
|
887
|
+
LOG_SB16,
|
|
888
|
+
)
|
|
889
|
+
return this.mixer_registers[this.mixer_current_address]
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
mixer_default_write(data: number): void {
|
|
893
|
+
dbg_log(
|
|
894
|
+
'mixer register write. addr:' +
|
|
895
|
+
h(this.mixer_current_address) +
|
|
896
|
+
' data:' +
|
|
897
|
+
h(data),
|
|
898
|
+
LOG_SB16,
|
|
899
|
+
)
|
|
900
|
+
this.mixer_registers[this.mixer_current_address] = data
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
mixer_reset(): void {
|
|
904
|
+
// Values intentionally in decimal.
|
|
905
|
+
// Default values available at
|
|
906
|
+
// https://pdos.csail.mit.edu/6.828/2011/readings/hardware/SoundBlaster.pdf
|
|
907
|
+
this.mixer_registers[0x04] = (12 << 4) | 12
|
|
908
|
+
this.mixer_registers[0x22] = (12 << 4) | 12
|
|
909
|
+
this.mixer_registers[0x26] = (12 << 4) | 12
|
|
910
|
+
this.mixer_registers[0x28] = 0
|
|
911
|
+
this.mixer_registers[0x2e] = 0
|
|
912
|
+
this.mixer_registers[0x0a] = 0
|
|
913
|
+
this.mixer_registers[0x30] = 24 << 3
|
|
914
|
+
this.mixer_registers[0x31] = 24 << 3
|
|
915
|
+
this.mixer_registers[0x32] = 24 << 3
|
|
916
|
+
this.mixer_registers[0x33] = 24 << 3
|
|
917
|
+
this.mixer_registers[0x34] = 24 << 3
|
|
918
|
+
this.mixer_registers[0x35] = 24 << 3
|
|
919
|
+
this.mixer_registers[0x36] = 0
|
|
920
|
+
this.mixer_registers[0x37] = 0
|
|
921
|
+
this.mixer_registers[0x38] = 0
|
|
922
|
+
this.mixer_registers[0x39] = 0
|
|
923
|
+
this.mixer_registers[0x3b] = 0
|
|
924
|
+
this.mixer_registers[0x3c] = 0x1f
|
|
925
|
+
this.mixer_registers[0x3d] = 0x15
|
|
926
|
+
this.mixer_registers[0x3e] = 0x0b
|
|
927
|
+
this.mixer_registers[0x3f] = 0
|
|
928
|
+
this.mixer_registers[0x40] = 0
|
|
929
|
+
this.mixer_registers[0x41] = 0
|
|
930
|
+
this.mixer_registers[0x42] = 0
|
|
931
|
+
this.mixer_registers[0x43] = 0
|
|
932
|
+
this.mixer_registers[0x44] = 8 << 4
|
|
933
|
+
this.mixer_registers[0x45] = 8 << 4
|
|
934
|
+
this.mixer_registers[0x46] = 8 << 4
|
|
935
|
+
this.mixer_registers[0x47] = 8 << 4
|
|
936
|
+
|
|
937
|
+
this.mixer_full_update()
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
mixer_full_update(): void {
|
|
941
|
+
// Start at 1. Don't re-reset.
|
|
942
|
+
for (let i = 1; i < this.mixer_registers.length; i++) {
|
|
943
|
+
if (MIXER_REGISTER_IS_LEGACY[i]) {
|
|
944
|
+
// Legacy registers are actually mapped to other register locations. Update
|
|
945
|
+
// using the new registers rather than the legacy registers.
|
|
946
|
+
continue
|
|
947
|
+
}
|
|
948
|
+
this.mixer_write(i, this.mixer_registers[i])
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
//
|
|
953
|
+
// FM Handlers
|
|
954
|
+
//
|
|
955
|
+
|
|
956
|
+
fm_default_write(data: number, register: number, address: number): void {
|
|
957
|
+
dbg_log(
|
|
958
|
+
'unhandled fm register write. addr:' +
|
|
959
|
+
register +
|
|
960
|
+
'|' +
|
|
961
|
+
h(address) +
|
|
962
|
+
' data:' +
|
|
963
|
+
h(data),
|
|
964
|
+
LOG_SB16,
|
|
965
|
+
)
|
|
966
|
+
// No need to save into a dummy register as the registers are write-only.
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
//
|
|
970
|
+
// FM behaviours
|
|
971
|
+
//
|
|
972
|
+
|
|
973
|
+
fm_update_waveforms(): void {
|
|
974
|
+
// To be implemented.
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
//
|
|
978
|
+
// General behaviours
|
|
979
|
+
//
|
|
980
|
+
|
|
981
|
+
sampling_rate_change(rate: number): void {
|
|
982
|
+
this.sampling_rate = rate
|
|
983
|
+
this.bus.send('dac-tell-sampling-rate', rate)
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
get_channel_count(): number {
|
|
987
|
+
return this.dsp_stereo ? 2 : 1
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
dma_transfer_size_set(): void {
|
|
991
|
+
this.dma_sample_count =
|
|
992
|
+
1 +
|
|
993
|
+
(this.write_buffer.shift() << 0) +
|
|
994
|
+
(this.write_buffer.shift() << 8)
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
dma_transfer_start(): void {
|
|
998
|
+
dbg_log('begin dma transfer', LOG_SB16)
|
|
999
|
+
|
|
1000
|
+
// (1) Setup appropriate settings.
|
|
1001
|
+
|
|
1002
|
+
this.bytes_per_sample = 1
|
|
1003
|
+
if (this.dsp_16bit) this.bytes_per_sample *= 2
|
|
1004
|
+
|
|
1005
|
+
// Don't count stereo interleaved bits apparently.
|
|
1006
|
+
// Disabling this line is needed for sounds to work correctly,
|
|
1007
|
+
// especially double buffering autoinit mode.
|
|
1008
|
+
// Learnt the hard way.
|
|
1009
|
+
// if(this.dsp_stereo) this.bytes_per_sample *= 2;
|
|
1010
|
+
|
|
1011
|
+
this.dma_bytes_count = this.dma_sample_count * this.bytes_per_sample
|
|
1012
|
+
this.dma_bytes_block = SB_DMA_BLOCK_SAMPLES * this.bytes_per_sample
|
|
1013
|
+
|
|
1014
|
+
// Ensure block size is small enough but not too small, and is divisible by 4
|
|
1015
|
+
const max_bytes_block = Math.max((this.dma_bytes_count >> 2) & ~0x3, 32)
|
|
1016
|
+
this.dma_bytes_block = Math.min(max_bytes_block, this.dma_bytes_block)
|
|
1017
|
+
|
|
1018
|
+
// (2) Wait until channel is unmasked (if not already)
|
|
1019
|
+
this.dma_waiting_transfer = true
|
|
1020
|
+
if (!this.dma.channel_mask[this.dma_channel]) {
|
|
1021
|
+
this.dma_on_unmask(this.dma_channel)
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
dma_on_unmask(channel: number): void {
|
|
1026
|
+
if (channel !== this.dma_channel || !this.dma_waiting_transfer) {
|
|
1027
|
+
return
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
// (3) Configure amount of bytes left to transfer and tell speaker adapter
|
|
1031
|
+
// to start requesting transfers
|
|
1032
|
+
this.dma_waiting_transfer = false
|
|
1033
|
+
this.dma_bytes_left = this.dma_bytes_count
|
|
1034
|
+
this.dma_paused = false
|
|
1035
|
+
this.bus.send('dac-enable')
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
dma_transfer_next(): void {
|
|
1039
|
+
dbg_log('dma transfering next block', LOG_SB16)
|
|
1040
|
+
|
|
1041
|
+
const size = Math.min(this.dma_bytes_left, this.dma_bytes_block)
|
|
1042
|
+
const samples = Math.floor(size / this.bytes_per_sample)
|
|
1043
|
+
|
|
1044
|
+
this.dma.do_write(
|
|
1045
|
+
this.dma_syncbuffer,
|
|
1046
|
+
0,
|
|
1047
|
+
size,
|
|
1048
|
+
this.dma_channel,
|
|
1049
|
+
(error: boolean) => {
|
|
1050
|
+
dbg_log(
|
|
1051
|
+
'dma block transfer ' +
|
|
1052
|
+
(error ? 'unsuccessful' : 'successful'),
|
|
1053
|
+
LOG_SB16,
|
|
1054
|
+
)
|
|
1055
|
+
if (error) return
|
|
1056
|
+
|
|
1057
|
+
this.dma_to_dac(samples)
|
|
1058
|
+
this.dma_bytes_left -= size
|
|
1059
|
+
|
|
1060
|
+
if (!this.dma_bytes_left) {
|
|
1061
|
+
// Completed requested transfer of given size.
|
|
1062
|
+
this.raise_irq(this.dma_irq)
|
|
1063
|
+
|
|
1064
|
+
if (this.dma_autoinit) {
|
|
1065
|
+
// Restart the transfer.
|
|
1066
|
+
this.dma_bytes_left = this.dma_bytes_count
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
},
|
|
1070
|
+
)
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
dma_to_dac(sample_count: number): void {
|
|
1074
|
+
const amplitude = this.dsp_16bit ? 32767.5 : 127.5
|
|
1075
|
+
const offset = this.dsp_signed ? 0 : -1
|
|
1076
|
+
const repeats = this.dsp_stereo ? 1 : 2
|
|
1077
|
+
|
|
1078
|
+
let buffer: Int8Array | Uint8Array | Int16Array | Uint16Array
|
|
1079
|
+
if (this.dsp_16bit) {
|
|
1080
|
+
buffer = this.dsp_signed
|
|
1081
|
+
? this.dma_buffer_int16
|
|
1082
|
+
: this.dma_buffer_uint16
|
|
1083
|
+
} else {
|
|
1084
|
+
buffer = this.dsp_signed
|
|
1085
|
+
? this.dma_buffer_int8
|
|
1086
|
+
: this.dma_buffer_uint8
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
let channel = 0
|
|
1090
|
+
for (let i = 0; i < sample_count; i++) {
|
|
1091
|
+
const sample = audio_normalize(buffer[i], amplitude, offset)
|
|
1092
|
+
for (let j = 0; j < repeats; j++) {
|
|
1093
|
+
this.dac_buffers[channel].push(sample)
|
|
1094
|
+
channel ^= 1
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
this.dac_send()
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
dac_handle_request(): void {
|
|
1102
|
+
if (!this.dma_bytes_left || this.dma_paused) {
|
|
1103
|
+
// No more data to transfer or is paused. Send whatever is in the buffers.
|
|
1104
|
+
this.dac_send()
|
|
1105
|
+
} else {
|
|
1106
|
+
this.dma_transfer_next()
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
dac_send(): void {
|
|
1111
|
+
if (!this.dac_buffers[0].length) {
|
|
1112
|
+
return
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
const out0 = this.dac_buffers[0].shift_block(this.dac_buffers[0].length)
|
|
1116
|
+
const out1 = this.dac_buffers[1].shift_block(this.dac_buffers[1].length)
|
|
1117
|
+
this.bus.send('dac-send-data', [out0, out1], [out0.buffer, out1.buffer])
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
raise_irq(type?: number): void {
|
|
1121
|
+
dbg_log('raise irq', LOG_SB16)
|
|
1122
|
+
if (type !== undefined) {
|
|
1123
|
+
this.irq_triggered[type] = 1
|
|
1124
|
+
}
|
|
1125
|
+
this.cpu.device_raise_irq(this.irq)
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
lower_irq(type: number): void {
|
|
1129
|
+
dbg_log('lower irq', LOG_SB16)
|
|
1130
|
+
this.irq_triggered[type] = 0
|
|
1131
|
+
this.cpu.device_lower_irq(this.irq)
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
//
|
|
1136
|
+
// DSP command registration
|
|
1137
|
+
//
|
|
1138
|
+
|
|
1139
|
+
function register_dsp_command(
|
|
1140
|
+
commands: number[],
|
|
1141
|
+
size: number,
|
|
1142
|
+
handler?: (this: SB16) => void,
|
|
1143
|
+
): void {
|
|
1144
|
+
if (!handler) {
|
|
1145
|
+
handler = SB16.prototype.dsp_default_handler
|
|
1146
|
+
}
|
|
1147
|
+
for (let i = 0; i < commands.length; i++) {
|
|
1148
|
+
DSP_COMMAND_SIZES[commands[i]] = size
|
|
1149
|
+
DSP_COMMAND_HANDLERS[commands[i]] = handler
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
function any_first_digit(base: number): number[] {
|
|
1154
|
+
const commands: number[] = []
|
|
1155
|
+
for (let i = 0; i < 16; i++) {
|
|
1156
|
+
commands.push(base + i)
|
|
1157
|
+
}
|
|
1158
|
+
return commands
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// ASP set register
|
|
1162
|
+
register_dsp_command([0x0e], 2, function () {
|
|
1163
|
+
this.asp_registers[this.write_buffer.shift()] = this.write_buffer.shift()
|
|
1164
|
+
})
|
|
1165
|
+
|
|
1166
|
+
// ASP get register
|
|
1167
|
+
register_dsp_command([0x0f], 1, function () {
|
|
1168
|
+
this.read_buffer.clear()
|
|
1169
|
+
this.read_buffer.push(this.asp_registers[this.write_buffer.shift()])
|
|
1170
|
+
})
|
|
1171
|
+
|
|
1172
|
+
// 8-bit direct mode single byte digitized sound output.
|
|
1173
|
+
register_dsp_command([0x10], 1, function () {
|
|
1174
|
+
const value = audio_normalize(this.write_buffer.shift(), 127.5, -1)
|
|
1175
|
+
|
|
1176
|
+
this.dac_buffers[0].push(value)
|
|
1177
|
+
this.dac_buffers[1].push(value)
|
|
1178
|
+
this.bus.send('dac-enable')
|
|
1179
|
+
})
|
|
1180
|
+
|
|
1181
|
+
// 8-bit single-cycle DMA mode digitized sound output.
|
|
1182
|
+
register_dsp_command([0x14, 0x15], 2, function () {
|
|
1183
|
+
this.dma_irq = SB_IRQ_8BIT
|
|
1184
|
+
this.dma_channel = this.dma_channel_8bit
|
|
1185
|
+
this.dma_autoinit = false
|
|
1186
|
+
this.dsp_signed = false
|
|
1187
|
+
this.dsp_16bit = false
|
|
1188
|
+
this.dsp_highspeed = false
|
|
1189
|
+
this.dma_transfer_size_set()
|
|
1190
|
+
this.dma_transfer_start()
|
|
1191
|
+
})
|
|
1192
|
+
|
|
1193
|
+
// Creative 8-bit to 2-bit ADPCM single-cycle DMA mode digitized sound output.
|
|
1194
|
+
register_dsp_command([0x16], 2)
|
|
1195
|
+
|
|
1196
|
+
// Creative 8-bit to 2-bit ADPCM single-cycle DMA mode digitzed sound output
|
|
1197
|
+
// with reference byte.
|
|
1198
|
+
register_dsp_command([0x17], 2)
|
|
1199
|
+
|
|
1200
|
+
// 8-bit auto-init DMA mode digitized sound output.
|
|
1201
|
+
register_dsp_command([0x1c], 0, function () {
|
|
1202
|
+
this.dma_irq = SB_IRQ_8BIT
|
|
1203
|
+
this.dma_channel = this.dma_channel_8bit
|
|
1204
|
+
this.dma_autoinit = true
|
|
1205
|
+
this.dsp_signed = false
|
|
1206
|
+
this.dsp_16bit = false
|
|
1207
|
+
this.dsp_highspeed = false
|
|
1208
|
+
this.dma_transfer_start()
|
|
1209
|
+
})
|
|
1210
|
+
|
|
1211
|
+
// Creative 8-bit to 2-bit ADPCM auto-init DMA mode digitized sound output
|
|
1212
|
+
// with reference byte.
|
|
1213
|
+
register_dsp_command([0x1f], 0)
|
|
1214
|
+
|
|
1215
|
+
// 8-bit direct mode single byte digitized sound input.
|
|
1216
|
+
register_dsp_command([0x20], 0, function () {
|
|
1217
|
+
// Fake silent input.
|
|
1218
|
+
this.read_buffer.clear()
|
|
1219
|
+
this.read_buffer.push(0x7f)
|
|
1220
|
+
})
|
|
1221
|
+
|
|
1222
|
+
// 8-bit single-cycle DMA mode digitized sound input.
|
|
1223
|
+
register_dsp_command([0x24], 2)
|
|
1224
|
+
|
|
1225
|
+
// 8-bit auto-init DMA mode digitized sound input.
|
|
1226
|
+
register_dsp_command([0x2c], 0)
|
|
1227
|
+
|
|
1228
|
+
// Polling mode MIDI input.
|
|
1229
|
+
register_dsp_command([0x30], 0)
|
|
1230
|
+
|
|
1231
|
+
// Interrupt mode MIDI input.
|
|
1232
|
+
register_dsp_command([0x31], 0)
|
|
1233
|
+
|
|
1234
|
+
// UART polling mode MIDI I/O.
|
|
1235
|
+
register_dsp_command([0x34], 0)
|
|
1236
|
+
|
|
1237
|
+
// UART interrupt mode MIDI I/O.
|
|
1238
|
+
register_dsp_command([0x35], 0)
|
|
1239
|
+
|
|
1240
|
+
// UART polling mode MIDI I/O with time stamping.
|
|
1241
|
+
register_dsp_command([0x36], 0)
|
|
1242
|
+
|
|
1243
|
+
// UART interrupt mode MIDI I/O with time stamping.
|
|
1244
|
+
register_dsp_command([0x37], 0)
|
|
1245
|
+
|
|
1246
|
+
// MIDI output.
|
|
1247
|
+
register_dsp_command([0x38], 0)
|
|
1248
|
+
|
|
1249
|
+
// Set digitized sound transfer Time Constant.
|
|
1250
|
+
register_dsp_command([0x40], 1, function () {
|
|
1251
|
+
// Note: bTimeConstant = 256 * time constant
|
|
1252
|
+
this.sampling_rate_change(
|
|
1253
|
+
1000000 / (256 - this.write_buffer.shift()) / this.get_channel_count(),
|
|
1254
|
+
)
|
|
1255
|
+
})
|
|
1256
|
+
|
|
1257
|
+
// Set digitized sound output sampling rate.
|
|
1258
|
+
// Set digitized sound input sampling rate.
|
|
1259
|
+
register_dsp_command([0x41, 0x42], 2, function () {
|
|
1260
|
+
this.sampling_rate_change(
|
|
1261
|
+
(this.write_buffer.shift() << 8) | this.write_buffer.shift(),
|
|
1262
|
+
)
|
|
1263
|
+
})
|
|
1264
|
+
|
|
1265
|
+
// Set DSP block transfer size.
|
|
1266
|
+
register_dsp_command([0x48], 2, function () {
|
|
1267
|
+
// TODO: should be in bytes, but if this is only used
|
|
1268
|
+
// for 8 bit transfers, then this number is the same
|
|
1269
|
+
// as number of samples?
|
|
1270
|
+
// Wrong: e.g. stereo requires two bytes per sample.
|
|
1271
|
+
this.dma_transfer_size_set()
|
|
1272
|
+
})
|
|
1273
|
+
|
|
1274
|
+
// Creative 8-bit to 4-bit ADPCM single-cycle DMA mode digitized sound output.
|
|
1275
|
+
register_dsp_command([0x74], 2)
|
|
1276
|
+
|
|
1277
|
+
// Creative 8-bit to 4-bit ADPCM single-cycle DMA mode digitized sound output
|
|
1278
|
+
// with referene byte.
|
|
1279
|
+
register_dsp_command([0x75], 2)
|
|
1280
|
+
|
|
1281
|
+
// Creative 8-bit to 3-bit ADPCM single-cycle DMA mode digitized sound output.
|
|
1282
|
+
register_dsp_command([0x76], 2)
|
|
1283
|
+
|
|
1284
|
+
// Creative 8-bit to 3-bit ADPCM single-cycle DMA mode digitized sound output
|
|
1285
|
+
// with referene byte.
|
|
1286
|
+
register_dsp_command([0x77], 2)
|
|
1287
|
+
|
|
1288
|
+
// Creative 8-bit to 4-bit ADPCM auto-init DMA mode digitized sound output
|
|
1289
|
+
// with reference byte.
|
|
1290
|
+
register_dsp_command([0x7d], 0)
|
|
1291
|
+
|
|
1292
|
+
// Creative 8-bit to 3-bit ADPCM auto-init DMA mode digitized sound output
|
|
1293
|
+
// with reference byte.
|
|
1294
|
+
register_dsp_command([0x7f], 0)
|
|
1295
|
+
|
|
1296
|
+
// Pause DAC for a duration.
|
|
1297
|
+
register_dsp_command([0x80], 2)
|
|
1298
|
+
|
|
1299
|
+
// 8-bit high-speed auto-init DMA mode digitized sound output.
|
|
1300
|
+
register_dsp_command([0x90], 0, function () {
|
|
1301
|
+
this.dma_irq = SB_IRQ_8BIT
|
|
1302
|
+
this.dma_channel = this.dma_channel_8bit
|
|
1303
|
+
this.dma_autoinit = true
|
|
1304
|
+
this.dsp_signed = false
|
|
1305
|
+
this.dsp_highspeed = true
|
|
1306
|
+
this.dsp_16bit = false
|
|
1307
|
+
this.dma_transfer_start()
|
|
1308
|
+
})
|
|
1309
|
+
|
|
1310
|
+
// 8-bit high-speed single-cycle DMA mode digitized sound input.
|
|
1311
|
+
register_dsp_command([0x91], 0)
|
|
1312
|
+
|
|
1313
|
+
// 8-bit high-speed auto-init DMA mode digitized sound input.
|
|
1314
|
+
register_dsp_command([0x98], 0)
|
|
1315
|
+
|
|
1316
|
+
// 8-bit high-speed single-cycle DMA mode digitized sound input.
|
|
1317
|
+
register_dsp_command([0x99], 0)
|
|
1318
|
+
|
|
1319
|
+
// Set input mode to mono.
|
|
1320
|
+
register_dsp_command([0xa0], 0)
|
|
1321
|
+
|
|
1322
|
+
// Set input mode to stereo.
|
|
1323
|
+
register_dsp_command([0xa8], 0)
|
|
1324
|
+
|
|
1325
|
+
// Program 16-bit DMA mode digitized sound I/O.
|
|
1326
|
+
register_dsp_command(any_first_digit(0xb0), 3, function () {
|
|
1327
|
+
if (this.command & (1 << 3)) {
|
|
1328
|
+
// Analogue to digital not implemented.
|
|
1329
|
+
this.dsp_default_handler()
|
|
1330
|
+
return
|
|
1331
|
+
}
|
|
1332
|
+
const mode = this.write_buffer.shift()
|
|
1333
|
+
this.dma_irq = SB_IRQ_16BIT
|
|
1334
|
+
this.dma_channel = this.dma_channel_16bit
|
|
1335
|
+
this.dma_autoinit = !!(this.command & (1 << 2))
|
|
1336
|
+
this.dsp_signed = !!(mode & (1 << 4))
|
|
1337
|
+
this.dsp_stereo = !!(mode & (1 << 5))
|
|
1338
|
+
this.dsp_16bit = true
|
|
1339
|
+
this.dma_transfer_size_set()
|
|
1340
|
+
this.dma_transfer_start()
|
|
1341
|
+
})
|
|
1342
|
+
|
|
1343
|
+
// Program 8-bit DMA mode digitized sound I/O.
|
|
1344
|
+
register_dsp_command(any_first_digit(0xc0), 3, function () {
|
|
1345
|
+
if (this.command & (1 << 3)) {
|
|
1346
|
+
// Analogue to digital not implemented.
|
|
1347
|
+
this.dsp_default_handler()
|
|
1348
|
+
return
|
|
1349
|
+
}
|
|
1350
|
+
const mode = this.write_buffer.shift()
|
|
1351
|
+
this.dma_irq = SB_IRQ_8BIT
|
|
1352
|
+
this.dma_channel = this.dma_channel_8bit
|
|
1353
|
+
this.dma_autoinit = !!(this.command & (1 << 2))
|
|
1354
|
+
this.dsp_signed = !!(mode & (1 << 4))
|
|
1355
|
+
this.dsp_stereo = !!(mode & (1 << 5))
|
|
1356
|
+
this.dsp_16bit = false
|
|
1357
|
+
this.dma_transfer_size_set()
|
|
1358
|
+
this.dma_transfer_start()
|
|
1359
|
+
})
|
|
1360
|
+
|
|
1361
|
+
// Pause 8-bit DMA mode digitized sound I/O.
|
|
1362
|
+
register_dsp_command([0xd0], 0, function () {
|
|
1363
|
+
this.dma_paused = true
|
|
1364
|
+
this.bus.send('dac-disable')
|
|
1365
|
+
})
|
|
1366
|
+
|
|
1367
|
+
// Turn on speaker.
|
|
1368
|
+
// Documented to have no effect on SB16.
|
|
1369
|
+
register_dsp_command([0xd1], 0, function () {
|
|
1370
|
+
this.dummy_speaker_enabled = true
|
|
1371
|
+
})
|
|
1372
|
+
|
|
1373
|
+
// Turn off speaker.
|
|
1374
|
+
// Documented to have no effect on SB16.
|
|
1375
|
+
register_dsp_command([0xd3], 0, function () {
|
|
1376
|
+
this.dummy_speaker_enabled = false
|
|
1377
|
+
})
|
|
1378
|
+
|
|
1379
|
+
// Continue 8-bit DMA mode digitized sound I/O.
|
|
1380
|
+
register_dsp_command([0xd4], 0, function () {
|
|
1381
|
+
this.dma_paused = false
|
|
1382
|
+
this.bus.send('dac-enable')
|
|
1383
|
+
})
|
|
1384
|
+
|
|
1385
|
+
// Pause 16-bit DMA mode digitized sound I/O.
|
|
1386
|
+
register_dsp_command([0xd5], 0, function () {
|
|
1387
|
+
this.dma_paused = true
|
|
1388
|
+
this.bus.send('dac-disable')
|
|
1389
|
+
})
|
|
1390
|
+
|
|
1391
|
+
// Continue 16-bit DMA mode digitized sound I/O.
|
|
1392
|
+
register_dsp_command([0xd6], 0, function () {
|
|
1393
|
+
this.dma_paused = false
|
|
1394
|
+
this.bus.send('dac-enable')
|
|
1395
|
+
})
|
|
1396
|
+
|
|
1397
|
+
// Get speaker status.
|
|
1398
|
+
register_dsp_command([0xd8], 0, function () {
|
|
1399
|
+
this.read_buffer.clear()
|
|
1400
|
+
this.read_buffer.push(+this.dummy_speaker_enabled * 0xff)
|
|
1401
|
+
})
|
|
1402
|
+
|
|
1403
|
+
// Exit 16-bit auto-init DMA mode digitized sound I/O.
|
|
1404
|
+
// Exit 8-bit auto-init mode digitized sound I/O.
|
|
1405
|
+
register_dsp_command([0xd9, 0xda], 0, function () {
|
|
1406
|
+
this.dma_autoinit = false
|
|
1407
|
+
})
|
|
1408
|
+
|
|
1409
|
+
// DSP identification
|
|
1410
|
+
register_dsp_command([0xe0], 1, function () {
|
|
1411
|
+
this.read_buffer.clear()
|
|
1412
|
+
this.read_buffer.push(~this.write_buffer.shift())
|
|
1413
|
+
})
|
|
1414
|
+
|
|
1415
|
+
// Get DSP version number.
|
|
1416
|
+
register_dsp_command([0xe1], 0, function () {
|
|
1417
|
+
this.read_buffer.clear()
|
|
1418
|
+
this.read_buffer.push(4)
|
|
1419
|
+
this.read_buffer.push(5)
|
|
1420
|
+
})
|
|
1421
|
+
|
|
1422
|
+
// DMA identification.
|
|
1423
|
+
register_dsp_command([0xe2], 1)
|
|
1424
|
+
|
|
1425
|
+
// Get DSP copyright.
|
|
1426
|
+
register_dsp_command([0xe3], 0, function () {
|
|
1427
|
+
this.read_buffer.clear()
|
|
1428
|
+
for (let i = 0; i < DSP_COPYRIGHT.length; i++) {
|
|
1429
|
+
this.read_buffer.push(DSP_COPYRIGHT.charCodeAt(i))
|
|
1430
|
+
}
|
|
1431
|
+
// Null terminator.
|
|
1432
|
+
this.read_buffer.push(0)
|
|
1433
|
+
})
|
|
1434
|
+
|
|
1435
|
+
// Write test register.
|
|
1436
|
+
register_dsp_command([0xe4], 1, function () {
|
|
1437
|
+
this.test_register = this.write_buffer.shift()
|
|
1438
|
+
})
|
|
1439
|
+
|
|
1440
|
+
// Read test register.
|
|
1441
|
+
register_dsp_command([0xe8], 0, function () {
|
|
1442
|
+
this.read_buffer.clear()
|
|
1443
|
+
this.read_buffer.push(this.test_register)
|
|
1444
|
+
})
|
|
1445
|
+
|
|
1446
|
+
// Trigger IRQ
|
|
1447
|
+
register_dsp_command([0xf2, 0xf3], 0, function () {
|
|
1448
|
+
this.raise_irq()
|
|
1449
|
+
})
|
|
1450
|
+
|
|
1451
|
+
// ASP - unknown function
|
|
1452
|
+
const SB_F9 = new Uint8Array(256)
|
|
1453
|
+
SB_F9[0x0e] = 0xff
|
|
1454
|
+
SB_F9[0x0f] = 0x07
|
|
1455
|
+
SB_F9[0x37] = 0x38
|
|
1456
|
+
register_dsp_command([0xf9], 1, function () {
|
|
1457
|
+
const input = this.write_buffer.shift()
|
|
1458
|
+
dbg_log('dsp 0xf9: unknown function. input: ' + input, LOG_SB16)
|
|
1459
|
+
|
|
1460
|
+
this.read_buffer.clear()
|
|
1461
|
+
this.read_buffer.push(SB_F9[input])
|
|
1462
|
+
})
|
|
1463
|
+
|
|
1464
|
+
//
|
|
1465
|
+
// Mixer registration
|
|
1466
|
+
//
|
|
1467
|
+
|
|
1468
|
+
function register_mixer_read(
|
|
1469
|
+
address: number,
|
|
1470
|
+
handler?: (this: SB16) => number,
|
|
1471
|
+
): void {
|
|
1472
|
+
if (!handler) {
|
|
1473
|
+
handler = SB16.prototype.mixer_default_read
|
|
1474
|
+
}
|
|
1475
|
+
MIXER_READ_HANDLERS[address] = handler
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
function register_mixer_write(
|
|
1479
|
+
address: number,
|
|
1480
|
+
handler?: (this: SB16, data: number) => void,
|
|
1481
|
+
): void {
|
|
1482
|
+
if (!handler) {
|
|
1483
|
+
handler = SB16.prototype.mixer_default_write
|
|
1484
|
+
}
|
|
1485
|
+
MIXER_WRITE_HANDLERS[address] = handler
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// Legacy registers map each nibble to the last 4 bits of the new registers
|
|
1489
|
+
function register_mixer_legacy(
|
|
1490
|
+
address_old: number,
|
|
1491
|
+
address_new_left: number,
|
|
1492
|
+
address_new_right: number,
|
|
1493
|
+
): void {
|
|
1494
|
+
MIXER_REGISTER_IS_LEGACY[address_old] = 1
|
|
1495
|
+
|
|
1496
|
+
MIXER_READ_HANDLERS[address_old] = function (this: SB16): number {
|
|
1497
|
+
const left = this.mixer_registers[address_new_left] & 0xf0
|
|
1498
|
+
const right = this.mixer_registers[address_new_right] >>> 4
|
|
1499
|
+
return left | right
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
MIXER_WRITE_HANDLERS[address_old] = function (
|
|
1503
|
+
this: SB16,
|
|
1504
|
+
data: number,
|
|
1505
|
+
): void {
|
|
1506
|
+
this.mixer_registers[address_old] = data
|
|
1507
|
+
const prev_left = this.mixer_registers[address_new_left]
|
|
1508
|
+
const prev_right = this.mixer_registers[address_new_right]
|
|
1509
|
+
const left = (data & 0xf0) | (prev_left & 0x0f)
|
|
1510
|
+
const right = ((data << 4) & 0xf0) | (prev_right & 0x0f)
|
|
1511
|
+
|
|
1512
|
+
this.mixer_write(address_new_left, left)
|
|
1513
|
+
this.mixer_write(address_new_right, right)
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
function register_mixer_volume(
|
|
1518
|
+
address: number,
|
|
1519
|
+
mixer_source: number,
|
|
1520
|
+
channel: number,
|
|
1521
|
+
): void {
|
|
1522
|
+
MIXER_READ_HANDLERS[address] = SB16.prototype.mixer_default_read
|
|
1523
|
+
|
|
1524
|
+
MIXER_WRITE_HANDLERS[address] = function (this: SB16, data: number): void {
|
|
1525
|
+
this.mixer_registers[address] = data
|
|
1526
|
+
this.bus.send('mixer-volume', [
|
|
1527
|
+
mixer_source,
|
|
1528
|
+
channel,
|
|
1529
|
+
(data >>> 2) - 62,
|
|
1530
|
+
])
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
// Reset.
|
|
1535
|
+
register_mixer_read(0x00, function () {
|
|
1536
|
+
this.mixer_reset()
|
|
1537
|
+
return 0
|
|
1538
|
+
})
|
|
1539
|
+
register_mixer_write(0x00)
|
|
1540
|
+
|
|
1541
|
+
// Legacy Voice Volume Left/Right.
|
|
1542
|
+
register_mixer_legacy(0x04, 0x32, 0x33)
|
|
1543
|
+
|
|
1544
|
+
// Legacy Mic Volume. TODO.
|
|
1545
|
+
//register_mixer_read(0x0A);
|
|
1546
|
+
//register_mixer_write(0x0A, function(data)
|
|
1547
|
+
//{
|
|
1548
|
+
// this.mixer_registers[0x0A] = data;
|
|
1549
|
+
// var prev = this.mixer_registers[0x3A];
|
|
1550
|
+
// this.mixer_write(0x3A, data << 5 | (prev & 0x0F));
|
|
1551
|
+
//});
|
|
1552
|
+
|
|
1553
|
+
// Legacy Master Volume Left/Right.
|
|
1554
|
+
register_mixer_legacy(0x22, 0x30, 0x31)
|
|
1555
|
+
// Legacy Midi Volume Left/Right.
|
|
1556
|
+
register_mixer_legacy(0x26, 0x34, 0x35)
|
|
1557
|
+
// Legacy CD Volume Left/Right.
|
|
1558
|
+
register_mixer_legacy(0x28, 0x36, 0x37)
|
|
1559
|
+
// Legacy Line Volume Left/Right.
|
|
1560
|
+
register_mixer_legacy(0x2e, 0x38, 0x39)
|
|
1561
|
+
|
|
1562
|
+
// Master Volume Left.
|
|
1563
|
+
register_mixer_volume(0x30, MIXER_SRC_MASTER, MIXER_CHANNEL_LEFT)
|
|
1564
|
+
// Master Volume Right.
|
|
1565
|
+
register_mixer_volume(0x31, MIXER_SRC_MASTER, MIXER_CHANNEL_RIGHT)
|
|
1566
|
+
// Voice Volume Left.
|
|
1567
|
+
register_mixer_volume(0x32, MIXER_SRC_DAC, MIXER_CHANNEL_LEFT)
|
|
1568
|
+
// Voice Volume Right.
|
|
1569
|
+
register_mixer_volume(0x33, MIXER_SRC_DAC, MIXER_CHANNEL_RIGHT)
|
|
1570
|
+
// MIDI Volume Left. TODO.
|
|
1571
|
+
//register_mixer_volume(0x34, MIXER_SRC_SYNTH, MIXER_CHANNEL_LEFT);
|
|
1572
|
+
// MIDI Volume Right. TODO.
|
|
1573
|
+
//register_mixer_volume(0x35, MIXER_SRC_SYNTH, MIXER_CHANNEL_RIGHT);
|
|
1574
|
+
// CD Volume Left. TODO.
|
|
1575
|
+
//register_mixer_volume(0x36, MIXER_SRC_CD, MIXER_CHANNEL_LEFT);
|
|
1576
|
+
// CD Volume Right. TODO.
|
|
1577
|
+
//register_mixer_volume(0x37, MIXER_SRC_CD, MIXER_CHANNEL_RIGHT);
|
|
1578
|
+
// Line Volume Left. TODO.
|
|
1579
|
+
//register_mixer_volume(0x38, MIXER_SRC_LINE, MIXER_CHANNEL_LEFT);
|
|
1580
|
+
// Line Volume Right. TODO.
|
|
1581
|
+
//register_mixer_volume(0x39, MIXER_SRC_LINE, MIXER_CHANNEL_RIGHT);
|
|
1582
|
+
// Mic Volume. TODO.
|
|
1583
|
+
//register_mixer_volume(0x3A, MIXER_SRC_MIC, MIXER_CHANNEL_BOTH);
|
|
1584
|
+
|
|
1585
|
+
// PC Speaker Volume.
|
|
1586
|
+
register_mixer_read(0x3b)
|
|
1587
|
+
register_mixer_write(0x3b, function (data) {
|
|
1588
|
+
this.mixer_registers[0x3b] = data
|
|
1589
|
+
this.bus.send('mixer-volume', [
|
|
1590
|
+
MIXER_SRC_PCSPEAKER,
|
|
1591
|
+
MIXER_CHANNEL_BOTH,
|
|
1592
|
+
(data >>> 6) * 6 - 18,
|
|
1593
|
+
])
|
|
1594
|
+
})
|
|
1595
|
+
|
|
1596
|
+
// Output Mixer Switches. TODO.
|
|
1597
|
+
//register_mixer_read(0x3C);
|
|
1598
|
+
//register_mixer_write(0x3C, function(data)
|
|
1599
|
+
//{
|
|
1600
|
+
// this.mixer_registers[0x3C] = data;
|
|
1601
|
+
//
|
|
1602
|
+
// if(data & 0x01) this.bus.send("mixer-connect", [MIXER_SRC_MIC, MIXER_CHANNEL_BOTH]);
|
|
1603
|
+
// else this.bus.send("mixer-disconnect", [MIXER_SRC_MIC, MIXER_CHANNEL_BOTH]);
|
|
1604
|
+
//
|
|
1605
|
+
// if(data & 0x02) this.bus.send("mixer-connect", [MIXER_SRC_CD, MIXER_CHANNEL_RIGHT]);
|
|
1606
|
+
// else this.bus.send("mixer-disconnect", [MIXER_SRC_CD, MIXER_CHANNEL_RIGHT]);
|
|
1607
|
+
//
|
|
1608
|
+
// if(data & 0x04) this.bus.send("mixer-connect", [MIXER_SRC_CD, MIXER_CHANNEL_LEFT]);
|
|
1609
|
+
// else this.bus.send("mixer-disconnect", [MIXER_SRC_CD, MIXER_CHANNEL_LEFT]);
|
|
1610
|
+
//
|
|
1611
|
+
// if(data & 0x08) this.bus.send("mixer-connect", [MIXER_SRC_LINE, MIXER_CHANNEL_RIGHT]);
|
|
1612
|
+
// else this.bus.send("mixer-disconnect", [MIXER_SRC_LINE, MIXER_CHANNEL_RIGHT]);
|
|
1613
|
+
//
|
|
1614
|
+
// if(data & 0x10) this.bus.send("mixer-connect", [MIXER_SRC_LINE, MIXER_CHANNEL_LEFT]);
|
|
1615
|
+
// else this.bus.send("mixer-disconnect", [MIXER_SRC_LINE, MIXER_CHANNEL_LEFT]);
|
|
1616
|
+
//});
|
|
1617
|
+
|
|
1618
|
+
// Input Mixer Left Switches. TODO.
|
|
1619
|
+
//register_mixer_read(0x3D);
|
|
1620
|
+
//register_mixer_write(0x3D);
|
|
1621
|
+
|
|
1622
|
+
// Input Mixer Right Switches. TODO.
|
|
1623
|
+
//register_mixer_read(0x3E);
|
|
1624
|
+
//register_mixer_write(0x3E);
|
|
1625
|
+
|
|
1626
|
+
// Input Gain Left. TODO.
|
|
1627
|
+
//register_mixer_read(0x3F);
|
|
1628
|
+
//register_mixer_write(0x3F);
|
|
1629
|
+
|
|
1630
|
+
// Input Gain Right. TODO.
|
|
1631
|
+
//register_mixer_read(0x40);
|
|
1632
|
+
//register_mixer_write(0x40);
|
|
1633
|
+
|
|
1634
|
+
// Output Gain Left.
|
|
1635
|
+
register_mixer_read(0x41)
|
|
1636
|
+
register_mixer_write(0x41, function (data) {
|
|
1637
|
+
this.mixer_registers[0x41] = data
|
|
1638
|
+
this.bus.send('mixer-gain-left', (data >>> 6) * 6)
|
|
1639
|
+
})
|
|
1640
|
+
|
|
1641
|
+
// Output Gain Right.
|
|
1642
|
+
register_mixer_read(0x42)
|
|
1643
|
+
register_mixer_write(0x42, function (data) {
|
|
1644
|
+
this.mixer_registers[0x42] = data
|
|
1645
|
+
this.bus.send('mixer-gain-right', (data >>> 6) * 6)
|
|
1646
|
+
})
|
|
1647
|
+
|
|
1648
|
+
// Mic AGC. TODO.
|
|
1649
|
+
//register_mixer_read(0x43);
|
|
1650
|
+
//register_mixer_write(0x43);
|
|
1651
|
+
|
|
1652
|
+
// Treble Left.
|
|
1653
|
+
register_mixer_read(0x44)
|
|
1654
|
+
register_mixer_write(0x44, function (data) {
|
|
1655
|
+
this.mixer_registers[0x44] = data
|
|
1656
|
+
data >>>= 3
|
|
1657
|
+
this.bus.send('mixer-treble-left', data - (data < 16 ? 14 : 16))
|
|
1658
|
+
})
|
|
1659
|
+
|
|
1660
|
+
// Treble Right.
|
|
1661
|
+
register_mixer_read(0x45)
|
|
1662
|
+
register_mixer_write(0x45, function (data) {
|
|
1663
|
+
this.mixer_registers[0x45] = data
|
|
1664
|
+
data >>>= 3
|
|
1665
|
+
this.bus.send('mixer-treble-right', data - (data < 16 ? 14 : 16))
|
|
1666
|
+
})
|
|
1667
|
+
|
|
1668
|
+
// Bass Left.
|
|
1669
|
+
register_mixer_read(0x46)
|
|
1670
|
+
register_mixer_write(0x46, function (data) {
|
|
1671
|
+
this.mixer_registers[0x46] = data
|
|
1672
|
+
data >>>= 3
|
|
1673
|
+
this.bus.send('mixer-bass-right', data - (data < 16 ? 14 : 16))
|
|
1674
|
+
})
|
|
1675
|
+
|
|
1676
|
+
// Bass Right.
|
|
1677
|
+
register_mixer_read(0x47)
|
|
1678
|
+
register_mixer_write(0x47, function (data) {
|
|
1679
|
+
this.mixer_registers[0x47] = data
|
|
1680
|
+
data >>>= 3
|
|
1681
|
+
this.bus.send('mixer-bass-right', data - (data < 16 ? 14 : 16))
|
|
1682
|
+
})
|
|
1683
|
+
|
|
1684
|
+
// IRQ Select.
|
|
1685
|
+
register_mixer_read(0x80, function () {
|
|
1686
|
+
switch (this.irq) {
|
|
1687
|
+
case SB_IRQ2:
|
|
1688
|
+
return 0x1
|
|
1689
|
+
case SB_IRQ5:
|
|
1690
|
+
return 0x2
|
|
1691
|
+
case SB_IRQ7:
|
|
1692
|
+
return 0x4
|
|
1693
|
+
case SB_IRQ10:
|
|
1694
|
+
return 0x8
|
|
1695
|
+
default:
|
|
1696
|
+
return 0x0
|
|
1697
|
+
}
|
|
1698
|
+
})
|
|
1699
|
+
register_mixer_write(0x80, function (bits) {
|
|
1700
|
+
if (bits & 0x1) this.irq = SB_IRQ2
|
|
1701
|
+
if (bits & 0x2) this.irq = SB_IRQ5
|
|
1702
|
+
if (bits & 0x4) this.irq = SB_IRQ7
|
|
1703
|
+
if (bits & 0x8) this.irq = SB_IRQ10
|
|
1704
|
+
})
|
|
1705
|
+
|
|
1706
|
+
// DMA Select.
|
|
1707
|
+
register_mixer_read(0x81, function () {
|
|
1708
|
+
let ret = 0
|
|
1709
|
+
switch (this.dma_channel_8bit) {
|
|
1710
|
+
case SB_DMA0:
|
|
1711
|
+
ret |= 0x1
|
|
1712
|
+
break
|
|
1713
|
+
case SB_DMA1:
|
|
1714
|
+
ret |= 0x2
|
|
1715
|
+
break
|
|
1716
|
+
// Channel 2 is hardwired to floppy disk.
|
|
1717
|
+
case SB_DMA3:
|
|
1718
|
+
ret |= 0x8
|
|
1719
|
+
break
|
|
1720
|
+
}
|
|
1721
|
+
switch (this.dma_channel_16bit) {
|
|
1722
|
+
// Channel 4 cannot be used.
|
|
1723
|
+
case SB_DMA5:
|
|
1724
|
+
ret |= 0x20
|
|
1725
|
+
break
|
|
1726
|
+
case SB_DMA6:
|
|
1727
|
+
ret |= 0x40
|
|
1728
|
+
break
|
|
1729
|
+
case SB_DMA7:
|
|
1730
|
+
ret |= 0x80
|
|
1731
|
+
break
|
|
1732
|
+
}
|
|
1733
|
+
return ret
|
|
1734
|
+
})
|
|
1735
|
+
register_mixer_write(0x81, function (bits) {
|
|
1736
|
+
if (bits & 0x1) this.dma_channel_8bit = SB_DMA0
|
|
1737
|
+
if (bits & 0x2) this.dma_channel_8bit = SB_DMA1
|
|
1738
|
+
if (bits & 0x8) this.dma_channel_8bit = SB_DMA3
|
|
1739
|
+
if (bits & 0x20) this.dma_channel_16bit = SB_DMA5
|
|
1740
|
+
if (bits & 0x40) this.dma_channel_16bit = SB_DMA6
|
|
1741
|
+
if (bits & 0x80) this.dma_channel_16bit = SB_DMA7
|
|
1742
|
+
})
|
|
1743
|
+
|
|
1744
|
+
// IRQ Status.
|
|
1745
|
+
register_mixer_read(0x82, function () {
|
|
1746
|
+
let ret = 0x20
|
|
1747
|
+
for (let i = 0; i < 16; i++) {
|
|
1748
|
+
ret |= i * this.irq_triggered[i]
|
|
1749
|
+
}
|
|
1750
|
+
return ret
|
|
1751
|
+
})
|
|
1752
|
+
|
|
1753
|
+
//
|
|
1754
|
+
// FM registration
|
|
1755
|
+
//
|
|
1756
|
+
|
|
1757
|
+
function register_fm_write(
|
|
1758
|
+
addresses: number[],
|
|
1759
|
+
handler?: (
|
|
1760
|
+
this: SB16,
|
|
1761
|
+
data: number,
|
|
1762
|
+
register: number,
|
|
1763
|
+
address: number,
|
|
1764
|
+
) => void,
|
|
1765
|
+
): void {
|
|
1766
|
+
if (!handler) {
|
|
1767
|
+
handler = SB16.prototype.fm_default_write
|
|
1768
|
+
}
|
|
1769
|
+
for (let i = 0; i < addresses.length; i++) {
|
|
1770
|
+
FM_HANDLERS[addresses[i]] = handler
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
|
|
1774
|
+
function between(start: number, end: number): number[] {
|
|
1775
|
+
const a: number[] = []
|
|
1776
|
+
for (let i = start; i <= end; i++) {
|
|
1777
|
+
a.push(i)
|
|
1778
|
+
}
|
|
1779
|
+
return a
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
const SB_FM_OPERATORS_BY_OFFSET = new Uint8Array(32)
|
|
1783
|
+
SB_FM_OPERATORS_BY_OFFSET[0x00] = 0
|
|
1784
|
+
SB_FM_OPERATORS_BY_OFFSET[0x01] = 1
|
|
1785
|
+
SB_FM_OPERATORS_BY_OFFSET[0x02] = 2
|
|
1786
|
+
SB_FM_OPERATORS_BY_OFFSET[0x03] = 3
|
|
1787
|
+
SB_FM_OPERATORS_BY_OFFSET[0x04] = 4
|
|
1788
|
+
SB_FM_OPERATORS_BY_OFFSET[0x05] = 5
|
|
1789
|
+
SB_FM_OPERATORS_BY_OFFSET[0x08] = 6
|
|
1790
|
+
SB_FM_OPERATORS_BY_OFFSET[0x09] = 7
|
|
1791
|
+
SB_FM_OPERATORS_BY_OFFSET[0x0a] = 8
|
|
1792
|
+
SB_FM_OPERATORS_BY_OFFSET[0x0b] = 9
|
|
1793
|
+
SB_FM_OPERATORS_BY_OFFSET[0x0c] = 10
|
|
1794
|
+
SB_FM_OPERATORS_BY_OFFSET[0x0d] = 11
|
|
1795
|
+
SB_FM_OPERATORS_BY_OFFSET[0x10] = 12
|
|
1796
|
+
SB_FM_OPERATORS_BY_OFFSET[0x11] = 13
|
|
1797
|
+
SB_FM_OPERATORS_BY_OFFSET[0x12] = 14
|
|
1798
|
+
SB_FM_OPERATORS_BY_OFFSET[0x13] = 15
|
|
1799
|
+
SB_FM_OPERATORS_BY_OFFSET[0x14] = 16
|
|
1800
|
+
SB_FM_OPERATORS_BY_OFFSET[0x15] = 17
|
|
1801
|
+
|
|
1802
|
+
function get_fm_operator(register: number, offset: number): number {
|
|
1803
|
+
return register * 18 + SB_FM_OPERATORS_BY_OFFSET[offset]
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
register_fm_write([0x01], function (_bits, register, _address) {
|
|
1807
|
+
this.fm_waveform_select_enable[register] = _bits & 1
|
|
1808
|
+
this.fm_update_waveforms()
|
|
1809
|
+
})
|
|
1810
|
+
|
|
1811
|
+
// Timer 1 Count.
|
|
1812
|
+
register_fm_write([0x02])
|
|
1813
|
+
|
|
1814
|
+
// Timer 2 Count.
|
|
1815
|
+
register_fm_write([0x03])
|
|
1816
|
+
|
|
1817
|
+
register_fm_write([0x04], function (_bits, register, _address) {
|
|
1818
|
+
switch (register) {
|
|
1819
|
+
case 0:
|
|
1820
|
+
// if(bits & 0x80)
|
|
1821
|
+
// {
|
|
1822
|
+
// // IQR Reset
|
|
1823
|
+
// }
|
|
1824
|
+
// else
|
|
1825
|
+
// {
|
|
1826
|
+
// // Timer masks and on/off
|
|
1827
|
+
// }
|
|
1828
|
+
break
|
|
1829
|
+
case 1:
|
|
1830
|
+
// Four-operator enable
|
|
1831
|
+
break
|
|
1832
|
+
}
|
|
1833
|
+
})
|
|
1834
|
+
|
|
1835
|
+
register_fm_write([0x05], function (bits, register, address) {
|
|
1836
|
+
if (register === 0) {
|
|
1837
|
+
// No registers documented here.
|
|
1838
|
+
this.fm_default_write(bits, register, address)
|
|
1839
|
+
} else {
|
|
1840
|
+
// OPL3 Mode Enable
|
|
1841
|
+
}
|
|
1842
|
+
})
|
|
1843
|
+
|
|
1844
|
+
register_fm_write([0x08], function (_bits, _register, _address) {
|
|
1845
|
+
// Composite sine wave on/off
|
|
1846
|
+
// Note select (keyboard split selection method)
|
|
1847
|
+
})
|
|
1848
|
+
|
|
1849
|
+
register_fm_write(between(0x20, 0x35), function (_bits, register, address) {
|
|
1850
|
+
const _operator = get_fm_operator(register, address - 0x20)
|
|
1851
|
+
// Tremolo
|
|
1852
|
+
// Vibrato
|
|
1853
|
+
// Sustain
|
|
1854
|
+
// KSR Envelope Scaling
|
|
1855
|
+
// Frequency Multiplication Factor
|
|
1856
|
+
})
|
|
1857
|
+
|
|
1858
|
+
register_fm_write(between(0x40, 0x55), function (_bits, register, address) {
|
|
1859
|
+
const _operator = get_fm_operator(register, address - 0x40)
|
|
1860
|
+
// Key Scale Level
|
|
1861
|
+
// Output Level
|
|
1862
|
+
})
|
|
1863
|
+
|
|
1864
|
+
register_fm_write(between(0x60, 0x75), function (_bits, register, address) {
|
|
1865
|
+
const _operator = get_fm_operator(register, address - 0x60)
|
|
1866
|
+
// Attack Rate
|
|
1867
|
+
// Decay Rate
|
|
1868
|
+
})
|
|
1869
|
+
|
|
1870
|
+
register_fm_write(between(0x80, 0x95), function (_bits, register, address) {
|
|
1871
|
+
const _operator = get_fm_operator(register, address - 0x80)
|
|
1872
|
+
// Sustain Level
|
|
1873
|
+
// Release Rate
|
|
1874
|
+
})
|
|
1875
|
+
|
|
1876
|
+
register_fm_write(between(0xa0, 0xa8), function (_bits, _register, address) {
|
|
1877
|
+
const _channel = address - 0xa0
|
|
1878
|
+
// Frequency Number (Lower 8 bits)
|
|
1879
|
+
})
|
|
1880
|
+
|
|
1881
|
+
register_fm_write(between(0xb0, 0xb8), function (_bits, _register, _address) {
|
|
1882
|
+
// Key-On
|
|
1883
|
+
// Block Number
|
|
1884
|
+
// Frequency Number (Higher 2 bits)
|
|
1885
|
+
})
|
|
1886
|
+
|
|
1887
|
+
register_fm_write([0xbd], function (_bits, _register, _address) {
|
|
1888
|
+
// Tremelo Depth
|
|
1889
|
+
// Vibrato Depth
|
|
1890
|
+
// Percussion Mode
|
|
1891
|
+
// Bass Drum Key-On
|
|
1892
|
+
// Snare Drum Key-On
|
|
1893
|
+
// Tom-Tom Key-On
|
|
1894
|
+
// Cymbal Key-On
|
|
1895
|
+
// Hi-Hat Key-On
|
|
1896
|
+
})
|
|
1897
|
+
|
|
1898
|
+
register_fm_write(between(0xc0, 0xc8), function (_bits, _register, _address) {
|
|
1899
|
+
// Right Speaker Enable
|
|
1900
|
+
// Left Speaker Enable
|
|
1901
|
+
// Feedback Modulation Factor
|
|
1902
|
+
// Synthesis Type
|
|
1903
|
+
})
|
|
1904
|
+
|
|
1905
|
+
register_fm_write(between(0xe0, 0xf5), function (_bits, register, address) {
|
|
1906
|
+
const _operator = get_fm_operator(register, address - 0xe0)
|
|
1907
|
+
// Waveform Select
|
|
1908
|
+
})
|
|
1909
|
+
|
|
1910
|
+
//
|
|
1911
|
+
// Helpers
|
|
1912
|
+
//
|
|
1913
|
+
|
|
1914
|
+
function audio_normalize(
|
|
1915
|
+
value: number,
|
|
1916
|
+
amplitude: number,
|
|
1917
|
+
offset: number,
|
|
1918
|
+
): number {
|
|
1919
|
+
return audio_clip(value / amplitude + offset, -1, 1)
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
function audio_clip(value: number, low: number, high: number): number {
|
|
1923
|
+
return (
|
|
1924
|
+
+(value < low) * low +
|
|
1925
|
+
+(value > high) * high +
|
|
1926
|
+
+(low <= value && value <= high) * value
|
|
1927
|
+
)
|
|
1928
|
+
}
|