Commit | Line | Data |
---|---|---|
1b261ae5 JH |
1 | /* |
2 | * sound/386bsd/soundcard.c | |
3 | * | |
1d43110e | 4 | * Soundcard driver for FreeBSD. |
1b261ae5 | 5 | * |
1d43110e JH |
6 | * Copyright by Hannu Savolainen 1993 |
7 | * | |
8 | * Redistribution and use in source and binary forms, with or without | |
9 | * modification, are permitted provided that the following conditions | |
10 | * are met: | |
11 | * 1. Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * 2. Redistributions in binary form must reproduce the above copyright | |
14 | * notice, this list of conditions and the following disclaimer in the | |
15 | * documentation and/or other materials provided with the distribution. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
27 | * SUCH DAMAGE. | |
28 | * | |
1b261ae5 JH |
29 | */ |
30 | ||
31 | #include "sound_config.h" | |
32 | ||
33 | #ifdef CONFIGURE_SOUNDCARD | |
34 | ||
35 | #include "dev_table.h" | |
36 | ||
37 | int __timeout_val = 0; | |
38 | int __process_aborting = 0; | |
39 | ||
40 | u_int snd1mask; | |
41 | u_int snd2mask; | |
42 | u_int snd3mask; | |
43 | u_int snd4mask; | |
44 | u_int snd5mask; | |
45 | ||
46 | struct sbc_device | |
47 | { | |
48 | int usecount; | |
49 | }; | |
50 | ||
51 | #define FIX_RETURN(ret) {if ((ret)<0) return -(ret); else return 0;} | |
52 | ||
53 | static struct sbc_device sbc_devices[SND_NDEVS]; | |
54 | static int timer_running = 0; | |
55 | ||
56 | static int in_use = 0; /* Total # of open device files (excluding | |
57 | * minor 0) */ | |
58 | ||
59 | static int soundcards_installed = 0; /* Number of installed | |
60 | * soundcards */ | |
61 | static int soundcard_configured = 0; | |
62 | extern char *snd_raw_buf[MAX_DSP_DEV][DSP_BUFFCOUNT]; | |
63 | extern unsigned long snd_raw_buf_phys[MAX_DSP_DEV][DSP_BUFFCOUNT]; | |
64 | extern int snd_raw_count[MAX_DSP_DEV]; | |
65 | ||
66 | static struct fileinfo files[SND_NDEVS]; | |
67 | ||
68 | int sndprobe (struct isa_device *dev); | |
69 | int sndattach (struct isa_device *dev); | |
70 | int sndopen (dev_t dev, int flags); | |
71 | int sndclose (dev_t dev, int flags); | |
72 | int sndioctl (dev_t dev, int cmd, caddr_t arg, int mode); | |
73 | int sndread (int dev, struct uio *uio); | |
74 | int sndwrite (int dev, struct uio *uio); | |
75 | int sndselect (int dev, int rw); | |
76 | static void sound_mem_init(void); | |
77 | ||
fde1aeb2 | 78 | long |
1b261ae5 JH |
79 | get_time() |
80 | { | |
81 | extern struct timeval time; | |
82 | ||
83 | return(time.tv_usec + (time.tv_sec*1000000)); | |
84 | } | |
85 | ||
86 | ||
87 | int | |
88 | sndread (int dev, struct uio *buf) | |
89 | { | |
90 | int count = buf->uio_resid; | |
91 | ||
92 | dev = minor (dev); | |
93 | ||
94 | DEB (printk ("sound_read(dev=%d, count=%d)\n", dev, count)); | |
95 | ||
1d43110e | 96 | switch (dev & 0x0f) /* It really has to be 0x0f */ |
1b261ae5 JH |
97 | { |
98 | case SND_DEV_AUDIO: | |
99 | FIX_RETURN (audio_read (dev, &files[dev], buf, count)); | |
100 | break; | |
101 | ||
102 | case SND_DEV_DSP: | |
103 | case SND_DEV_DSP16: | |
104 | FIX_RETURN (dsp_read (dev, &files[dev], buf, count)); | |
105 | break; | |
106 | ||
107 | case SND_DEV_SEQ: | |
108 | FIX_RETURN (sequencer_read (dev, &files[dev], buf, count)); | |
109 | break; | |
110 | ||
111 | #ifndef EXCLUDE_CHIP_MIDI | |
112 | case CMIDI_DEV_PRO: | |
113 | FIX_RETURN (CMIDI_read (dev, &files[dev], buf, count)); | |
114 | ||
115 | break; | |
116 | #endif | |
117 | ||
118 | ||
119 | #ifndef EXCLUDE_MPU401 | |
120 | case SND_DEV_MIDIN: | |
121 | FIX_RETURN (MIDIbuf_read (dev, &files[dev], buf, count)); | |
122 | #endif | |
123 | ||
124 | default: | |
125 | ; | |
126 | } | |
127 | ||
128 | FIX_RETURN (-EPERM); | |
129 | } | |
130 | ||
131 | int | |
132 | sndwrite (int dev, struct uio *buf) | |
133 | { | |
134 | int count = buf->uio_resid; | |
135 | ||
136 | DEB (printk ("sound_write(dev=%d, count=%d)\n", dev, count)); | |
137 | ||
138 | dev = minor (dev); | |
139 | ||
1d43110e | 140 | switch (dev & 0x0f) /* It really has to be 0x0f */ |
1b261ae5 JH |
141 | { |
142 | ||
143 | case SND_DEV_SEQ: | |
144 | FIX_RETURN (sequencer_write (dev, &files[dev], buf, count)); | |
145 | break; | |
146 | ||
147 | case SND_DEV_AUDIO: | |
148 | FIX_RETURN (audio_write (dev, &files[dev], buf, count)); | |
149 | break; | |
150 | ||
151 | case SND_DEV_DSP: | |
152 | case SND_DEV_DSP16: | |
153 | FIX_RETURN (dsp_write (dev, &files[dev], buf, count)); | |
154 | break; | |
155 | ||
156 | #ifndef EXCLUDE_CHIP_MIDI | |
157 | case CMIDI_DEV_PRO: | |
158 | FIX_RETURN (CMIDI_write (dev, &files[dev], buf, count)); | |
159 | break; | |
160 | #endif | |
161 | ||
162 | default: | |
163 | FIX_RETURN (-EPERM); | |
164 | } | |
165 | ||
166 | FIX_RETURN (count); | |
167 | } | |
168 | ||
169 | int | |
170 | sndopen (dev_t dev, int flags) | |
171 | { | |
172 | int retval; | |
173 | ||
174 | dev = minor (dev); | |
175 | ||
176 | /* printf("SND: Minor number is now : %ld\n",dev); */ | |
177 | ||
178 | DEB (printk ("sound_open(dev=%d) : usecount=%d\n", dev, sbc_devices[dev].usecount)); | |
179 | ||
180 | if ((dev >= SND_NDEVS) || (dev < 0)) | |
181 | { | |
182 | printk ("Invalid minor device %d\n", dev); | |
183 | FIX_RETURN (-ENODEV); | |
184 | } | |
185 | ||
186 | if (!soundcard_configured && dev) | |
187 | { | |
188 | printk ("SoundCard Error: The soundcard system has not been configured\n"); | |
189 | FIX_RETURN (-ENODEV); | |
190 | } | |
191 | ||
192 | files[dev].mode = 0; | |
193 | ||
194 | if (flags & FREAD && flags & FWRITE) | |
195 | files[dev].mode = OPEN_READWRITE; | |
196 | else if (flags & FREAD) | |
197 | files[dev].mode = OPEN_READ; | |
198 | else if (flags & FWRITE) | |
199 | files[dev].mode = OPEN_WRITE; | |
200 | ||
1d43110e | 201 | switch (dev & 0x0f) /* It has to be 0x0f. Trust me */ |
1b261ae5 JH |
202 | { |
203 | case SND_DEV_CTL: | |
204 | if (!soundcards_installed) | |
205 | if (soundcard_configured) | |
206 | { | |
207 | printk ("Soundcard not installed\n"); | |
208 | FIX_RETURN (-ENODEV); | |
209 | } | |
210 | break; | |
211 | ||
212 | case SND_DEV_SEQ: | |
213 | if ((retval = sequencer_open (dev, &files[dev])) < 0) | |
214 | FIX_RETURN (retval); | |
215 | break; | |
216 | ||
217 | /** UWM stuff **/ | |
218 | ||
219 | #ifndef EXCLUDE_CHIP_MIDI | |
220 | case CMIDI_DEV_PRO: | |
221 | FIX_RETURN ( CMIDI_open (dev, &files[dev]) ); | |
222 | break; | |
223 | #endif | |
224 | ||
225 | ||
226 | #ifndef EXCLUDE_MPU401 | |
227 | case SND_DEV_MIDIN: | |
228 | if ((retval = MIDIbuf_open (dev, &files[dev])) < 0) | |
229 | FIX_RETURN (retval); | |
230 | break; | |
231 | #endif | |
232 | ||
233 | case SND_DEV_AUDIO: | |
234 | if ((retval = audio_open (dev, &files[dev])) < 0) | |
235 | FIX_RETURN (retval); | |
236 | break; | |
237 | ||
238 | case SND_DEV_DSP: | |
239 | if ((retval = dsp_open (dev, &files[dev], 8)) < 0) | |
240 | FIX_RETURN (retval); | |
241 | break; | |
242 | ||
243 | case SND_DEV_DSP16: | |
244 | if ((retval = dsp_open (dev, &files[dev], 16)) < 0) | |
245 | FIX_RETURN (retval); | |
246 | break; | |
247 | ||
248 | default: | |
249 | printk ("Invalid minor device %d\n", dev); | |
250 | FIX_RETURN (-ENODEV); | |
251 | } | |
252 | ||
253 | sbc_devices[dev].usecount++; | |
254 | in_use++; | |
255 | ||
256 | FIX_RETURN (0); | |
257 | } | |
258 | ||
259 | int | |
260 | sndclose (dev_t dev, int flags) | |
261 | { | |
262 | ||
263 | dev = minor (dev); | |
264 | ||
265 | DEB (printk ("sound_release(dev=%d)\n", dev)); | |
266 | ||
1d43110e | 267 | switch (dev & 0x0f) /* Has to be 0x0f */ |
1b261ae5 JH |
268 | { |
269 | case SND_DEV_SEQ: | |
270 | sequencer_release (dev, &files[dev]); | |
271 | break; | |
272 | ||
273 | #ifndef EXCLUDE_CHIP_MIDI | |
274 | case CMIDI_DEV_PRO: | |
275 | CMIDI_close (dev, &files[dev]); | |
276 | break; | |
277 | #endif | |
278 | ||
279 | #ifndef EXCLUDE_MPU401 | |
280 | case SND_DEV_MIDIN: | |
281 | MIDIbuf_release (dev, &files[dev]); | |
282 | break; | |
283 | #endif | |
284 | ||
285 | case SND_DEV_AUDIO: | |
286 | audio_release (dev, &files[dev]); | |
287 | break; | |
288 | ||
289 | case SND_DEV_DSP: | |
290 | case SND_DEV_DSP16: | |
291 | dsp_release (dev, &files[dev]); | |
292 | break; | |
293 | ||
294 | default:; | |
295 | } | |
296 | ||
297 | sbc_devices[dev].usecount--; | |
298 | in_use--; /* If not control port */ | |
299 | ||
300 | FIX_RETURN (0); | |
301 | } | |
302 | ||
303 | int | |
304 | sndioctl (dev_t dev, int cmd, caddr_t arg, int mode) | |
305 | { | |
306 | dev = minor (dev); | |
307 | ||
308 | DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); | |
309 | ||
310 | switch (dev & 0x0f) | |
311 | { | |
312 | ||
313 | case SND_DEV_CTL: | |
314 | if (!num_mixers) | |
315 | FIX_RETURN (-ENODEV); | |
316 | ||
317 | if (dev >= num_mixers) | |
318 | FIX_RETURN (-ENODEV); | |
319 | ||
320 | FIX_RETURN (mixer_devs[dev]->ioctl (dev, cmd, (unsigned int) arg)); | |
321 | break; | |
322 | ||
323 | case SND_DEV_SEQ: | |
324 | FIX_RETURN (sequencer_ioctl (dev, &files[dev], cmd, (unsigned int) arg)); | |
325 | break; | |
326 | ||
327 | case SND_DEV_AUDIO: | |
328 | FIX_RETURN (audio_ioctl (dev, &files[dev], cmd, (unsigned int) arg)); | |
329 | break; | |
330 | ||
331 | case SND_DEV_DSP: | |
332 | case SND_DEV_DSP16: | |
333 | FIX_RETURN (dsp_ioctl (dev, &files[dev], cmd, (unsigned int) arg)); | |
334 | break; | |
335 | ||
336 | #ifndef EXCLUDE_MPU401 | |
337 | case SND_DEV_MIDIN: | |
338 | FIX_RETURN (MIDIbuf_ioctl (dev, &files[dev], cmd, (unsigned int) arg)); | |
339 | break; | |
340 | #endif | |
341 | ||
342 | default: | |
343 | FIX_RETURN (-EPERM); | |
344 | break; | |
345 | } | |
346 | ||
347 | FIX_RETURN (-EPERM); | |
348 | } | |
349 | ||
350 | int | |
351 | sndselect (int dev, int rw) | |
352 | { | |
353 | dev = minor (dev); | |
354 | ||
355 | DEB (printk ("sound_ioctl(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg)); | |
356 | ||
357 | FIX_RETURN (0); | |
358 | } | |
359 | ||
46c2d4f5 | 360 | static unsigned short |
1d43110e | 361 | ipri_to_irq (unsigned short ipri) |
1b261ae5 JH |
362 | { |
363 | /* | |
364 | * Converts the ipri (bitmask) to the corresponding irq number | |
365 | */ | |
366 | int irq; | |
367 | ||
368 | for (irq = 0; irq < 16; irq++) | |
369 | if (ipri == (1 << irq)) | |
370 | return irq; | |
371 | ||
372 | return -1; /* Invalid argument */ | |
373 | } | |
374 | ||
375 | int | |
376 | sndprobe (struct isa_device *dev) | |
377 | { | |
378 | struct address_info hw_config; | |
379 | ||
380 | hw_config.io_base = dev->id_iobase; | |
381 | hw_config.irq = ipri_to_irq (dev->id_irq); | |
382 | hw_config.dma = dev->id_drq; | |
383 | ||
384 | ||
385 | return sndtable_probe (dev->id_unit, &hw_config); | |
386 | } | |
387 | ||
388 | int | |
389 | sndattach (struct isa_device *dev) | |
390 | { | |
391 | int i; | |
392 | static int dsp_initialized = 0; | |
393 | static int midi_initialized = 0; | |
394 | static int seq_initialized = 0; | |
395 | static int generic_midi_initialized = 0; | |
fde1aeb2 | 396 | unsigned long mem_start = 0xefffffffUL; |
1b261ae5 JH |
397 | struct address_info hw_config; |
398 | ||
399 | hw_config.io_base = dev->id_iobase; | |
400 | hw_config.irq = ipri_to_irq (dev->id_irq); | |
401 | hw_config.dma = dev->id_drq; | |
402 | ||
403 | if (dev->id_unit) /* Card init */ | |
404 | if (!sndtable_init_card (dev->id_unit, &hw_config)) | |
405 | { | |
406 | printf (" <Driver not configured>"); | |
407 | return FALSE; | |
408 | } | |
409 | ||
410 | /* | |
411 | * Init the high level sound driver | |
412 | */ | |
413 | ||
414 | if (!(soundcards_installed = sndtable_get_cardcount ())) | |
415 | { | |
416 | printf (" <No such hardware>"); | |
417 | return FALSE; /* No cards detected */ | |
418 | } | |
419 | ||
1d43110e JH |
420 | printf("\n"); |
421 | ||
1b261ae5 JH |
422 | #ifndef EXCLUDE_AUDIO |
423 | soundcard_configured = 1; | |
424 | if (num_dspdevs) | |
425 | sound_mem_init (); | |
426 | #endif | |
427 | ||
428 | if (num_dspdevs && !dsp_initialized) /* Audio devices present */ | |
429 | { | |
430 | dsp_initialized = 1; | |
431 | mem_start = DMAbuf_init (mem_start); | |
432 | mem_start = audio_init (mem_start); | |
433 | mem_start = dsp_init (mem_start); | |
434 | } | |
435 | ||
436 | /** UWM stuff **/ | |
437 | ||
438 | #ifndef EXCLUDE_CHIP_MIDI | |
439 | ||
440 | if (!generic_midi_initialized) | |
441 | { | |
442 | generic_midi_initialized = 1; | |
443 | mem_start = CMIDI_init (mem_start); | |
444 | } | |
445 | ||
446 | #endif | |
447 | ||
448 | #ifndef EXCLUDE_MPU401 | |
449 | if (num_midis && !midi_initialized) | |
450 | { | |
451 | midi_initialized = 1; | |
452 | mem_start = MIDIbuf_init (mem_start); | |
453 | } | |
454 | #endif | |
455 | ||
456 | if ((num_midis + num_synths) && !seq_initialized) | |
457 | { | |
458 | seq_initialized = 1; | |
459 | mem_start = sequencer_init (mem_start); | |
460 | } | |
461 | ||
462 | for (i = 0; i < SND_NDEVS; i++) | |
463 | { | |
464 | sbc_devices[i].usecount = 0; | |
465 | } | |
466 | ||
467 | return TRUE; | |
468 | } | |
469 | ||
470 | void | |
471 | tenmicrosec (void) | |
472 | { | |
473 | int i; | |
474 | ||
475 | for (i = 0; i < 16; i++) | |
476 | inb (0x80); | |
477 | } | |
478 | ||
479 | #ifdef EXCLUDE_GUS | |
480 | void | |
481 | gusintr (int unit) | |
482 | { | |
81bef19f | 483 | return; |
1b261ae5 JH |
484 | } |
485 | #endif | |
486 | ||
487 | void | |
488 | request_sound_timer (int count) | |
489 | { | |
490 | static int current = 0; | |
491 | int tmp = count; | |
492 | ||
493 | if (count < 0) | |
4c45483e | 494 | timeout ((timeout_func_t)sequencer_timer, 0, -count); |
1b261ae5 JH |
495 | else |
496 | { | |
497 | ||
498 | if (count < current) | |
499 | current = 0; /* Timer restarted */ | |
500 | ||
501 | count = count - current; | |
502 | ||
503 | current = tmp; | |
504 | ||
505 | if (!count) | |
506 | count = 1; | |
507 | ||
4c45483e | 508 | timeout ((timeout_func_t)sequencer_timer, 0, count); |
1b261ae5 JH |
509 | } |
510 | timer_running = 1; | |
511 | } | |
512 | ||
513 | void | |
514 | sound_stop_timer (void) | |
515 | { | |
516 | if (timer_running) | |
fde1aeb2 | 517 | untimeout ((timeout_func_t)sequencer_timer, 0); /* XXX should fix */ |
1b261ae5 JH |
518 | timer_running = 0; |
519 | } | |
520 | ||
521 | #ifndef EXCLUDE_AUDIO | |
522 | static void | |
523 | sound_mem_init (void) | |
524 | { | |
525 | int i, dev; | |
526 | unsigned long dma_pagesize; | |
527 | static unsigned long dsp_init_mask = 0; | |
528 | ||
529 | for (dev = 0; dev < num_dspdevs; dev++) /* Enumerate devices */ | |
530 | if (!(dsp_init_mask & (1 << dev))) /* Not already done */ | |
531 | if (sound_buffcounts[dev] > 0 && sound_dsp_dmachan[dev] > 0) | |
532 | { | |
533 | dsp_init_mask |= (1 << dev); | |
534 | ||
535 | if (sound_dma_automode[dev]) | |
536 | { | |
1d43110e | 537 | sound_dma_automode[dev] = 0; /* Not possible with FreeBSD */ |
1b261ae5 JH |
538 | } |
539 | ||
540 | if (sound_buffcounts[dev] == 1) | |
541 | { | |
542 | sound_buffcounts[dev] = 2; | |
543 | sound_buffsizes[dev] /= 2; | |
544 | } | |
545 | ||
546 | if (sound_buffsizes[dev] > 65536) /* Larger is not possible (yet) */ | |
547 | sound_buffsizes[dev] = 65536; | |
548 | ||
3caa27e8 | 549 | #if 0 |
1b261ae5 JH |
550 | if (sound_dsp_dmachan[dev] > 3 && sound_buffsizes[dev] > 65536) |
551 | dma_pagesize = 131072; /* 128k */ | |
552 | else | |
3caa27e8 AC |
553 | dma_pagesize = 65536; |
554 | #endif | |
555 | dma_pagesize = 4096; /* use bounce buffer */ | |
1b261ae5 JH |
556 | |
557 | /* More sanity checks */ | |
558 | ||
559 | if (sound_buffsizes[dev] > dma_pagesize) | |
560 | sound_buffsizes[dev] = dma_pagesize; | |
fde1aeb2 | 561 | sound_buffsizes[dev] &= ~0xfff; /* Truncate to n*4k */ |
1b261ae5 JH |
562 | if (sound_buffsizes[dev] < 4096) |
563 | sound_buffsizes[dev] = 4096; | |
564 | ||
565 | /* Now allocate the buffers */ | |
566 | ||
567 | for (snd_raw_count[dev] = 0; snd_raw_count[dev] < sound_buffcounts[dev]; snd_raw_count[dev]++) | |
568 | { | |
569 | /* | |
570 | * The DMA buffer allocation algorithm hogs memory. We allocate | |
571 | * a memory area which is two times the requires size. This | |
572 | * guarantees that it contains at least one valid DMA buffer. | |
573 | * | |
574 | * This really needs some kind of finetuning. | |
575 | */ | |
576 | char *tmpbuf = malloc (2*sound_buffsizes[dev], M_DEVBUF, M_NOWAIT); | |
577 | unsigned long addr, rounded; | |
578 | ||
579 | if (tmpbuf == NULL) | |
580 | { | |
581 | printk ("snd: Unable to allocate %d bytes of buffer\n", | |
582 | 2 * sound_buffsizes[dev]); | |
583 | return; | |
584 | } | |
585 | ||
586 | addr = kvtop (tmpbuf); | |
587 | /* | |
588 | * Align the start address | |
589 | */ | |
590 | rounded = (addr & ~(dma_pagesize - 1)) + dma_pagesize; | |
591 | ||
592 | snd_raw_buf[dev][snd_raw_count[dev]] = | |
593 | &tmpbuf[rounded - addr]; /* Compute offset */ | |
594 | /* | |
595 | * Use virtual address as the physical address, since | |
596 | * isa_dmastart performs the phys address computation. | |
597 | */ | |
598 | snd_raw_buf_phys[dev][snd_raw_count[dev]] = | |
599 | (unsigned long) snd_raw_buf[dev][snd_raw_count[dev]]; | |
600 | } | |
601 | } /* for dev */ | |
602 | ||
603 | } | |
604 | ||
605 | #endif | |
606 | ||
607 | struct isa_driver snddriver = | |
608 | {sndprobe, sndattach, "snd"}; | |
609 | ||
610 | int | |
611 | snd_ioctl_return (int *addr, int value) | |
612 | { | |
613 | if (value < 0) | |
614 | return value; /* Error */ | |
615 | suword (addr, value); | |
616 | return 0; | |
617 | } | |
618 | ||
619 | #endif |