From 69392a6013762a518021fa5ab879806006ca6e81 Mon Sep 17 00:00:00 2001 From: Ivan Tatarinov Date: Sat, 5 Jun 2021 11:41:40 +0300 Subject: [PATCH] utils: added `dmaplayw` utility --- sdk/include/esxdos.def | 102 +++++++++++-- sdk/include/zxuno.def | 83 ++++++---- utils/Makefile | 7 + utils/dmaplayw.asm | 334 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 479 insertions(+), 47 deletions(-) create mode 100644 utils/dmaplayw.asm diff --git a/sdk/include/esxdos.def b/sdk/include/esxdos.def index 2f712c4..1984202 100644 --- a/sdk/include/esxdos.def +++ b/sdk/include/esxdos.def @@ -1,8 +1,11 @@ ; esxdos.def ; -; SPDX-FileCopyrightText: Copyright (C) 2019, 2021 Antonio Villena +; SPDX-FileCopyrightText: Copyright (C) 2017 AZXUNO association ; -; SPDX-License-Identifier: GPL-3.0-only +; SPDX-FileContributor: Copyright (C) 2019, 2021 Antonio Villena +; SPDX-FileContributor: 2021 Ivan Tatarinov +; +; SPDX-License-Identifier: GPL-3.0-or-later ; Compatible compilers: ; SJAsmPlus, @@ -10,19 +13,88 @@ ifndef esxdos_def_included define esxdos_def_included - define FA_READ 0x01 - define FA_WRITE 0x02 - define FA_OPEN_AL 0x08 - define M_GETSETDRV 0x89 - define F_OPEN 0x9a - define F_CLOSE 0x9b - define F_READ 0x9d - define F_WRITE 0x9e - define F_FSTAT 0xa1 + define HOOK_BASE 128 + define MISC_BASE HOOK_BASE+8 + define FSYS_BASE MISC_BASE+16 - macro esxdos dato - rst $08 - defb dato - endm + define DISK_STATUS HOOK_BASE+0 ; hookcodes for block devices + define DISK_READ HOOK_BASE+1 + define DISK_WRITE HOOK_BASE+2 + define DISK_IOCTL HOOK_BASE+3 + define DISK_INFO HOOK_BASE+4 + + define M_DOSVERSION MISC_BASE+0 + define M_GETSETDRV MISC_BASE+1 + define M_DRIVEINFO MISC_BASE+2 + define M_TAPEIN MISC_BASE+3 + define M_TAPEOUT MISC_BASE+4 + define M_GETHANDLE MISC_BASE+5 + define M_GETDATE MISC_BASE+6 + + define F_MOUNT FSYS_BASE+0 + define F_UMOUNT FSYS_BASE+1 + define F_OPEN FSYS_BASE+2 + define F_CLOSE FSYS_BASE+3 + define F_SYNC FSYS_BASE+4 + define F_READ FSYS_BASE+5 + define F_WRITE FSYS_BASE+6 + define F_SEEK FSYS_BASE+7 + define F_FGETPOS FSYS_BASE+8 + define F_FSTAT FSYS_BASE+9 + define F_FTRUNCATE FSYS_BASE+10 + define F_OPENDIR FSYS_BASE+11 + define F_READDIR FSYS_BASE+12 + define F_TELLDIR FSYS_BASE+13 + define F_SEEKDIR FSYS_BASE+14 + define F_REWINDDIR FSYS_BASE+15 + define F_GETCWD FSYS_BASE+16 + define F_CHDIR FSYS_BASE+17 + define F_MKDIR FSYS_BASE+18 + define F_RMDIR FSYS_BASE+19 + define F_STAT FSYS_BASE+20 + define F_UNLINK FSYS_BASE+21 + define F_TRUNCATE FSYS_BASE+22 + define F_CHMOD FSYS_BASE+23 + define F_RENAME FSYS_BASE+24 + define F_GETFREE FSYS_BASE+25 + + define FA_READ %00000001 ; Read access + define FA_WRITE %00000010 ; Write access + define FA_OPEN_EX %00000000 ; Open if exists, else error + define FA_OPEN_AL %00001000 ; Open if exists, if not create + define FA_CREATE_NEW %00000100 ; Create if not exists, if exists error + define FA_CREATE_AL %00001100 ; Create if not exists, else open and truncate + define FA_USE_HEADER %01000000 ; Use +3DOS header (passed in DE) + + ; Errors: + define EOK 1 + define ENONSENSE 2 + define ESTEND 3 + define EWRTYPE 4 + define ENOENT 5 ; No such file or directory + define EIO 6 ; I/O error + define EINVAL 7 ; Invalid file name + define EACCES 8 ; Access Denied + define ENOSPC 9 ; No space left on device + define ENXIO 10 ; Request beyond the limits of the device + define ENODRV 11 ; No such drive + define ENFILE 12 ; Too many files open in system + define EBADF 13 ; Bad file descriptor + define ENODEV 14 ; No such device + define EOVERFLOW 15 + define EISDIR 16 + define ENOTDIR 17 + define EEXIST 18 + define EPATH 19 ; Invalid path + define ENOSYS 20 + define ENAMETOOLONG 21 + define ENOCMD 22 + define EINUSE 23 + define ERDONLY 24 + + macro esxdos func + rst $08 + db func + endm endif ; !esxdos_def_included diff --git a/sdk/include/zxuno.def b/sdk/include/zxuno.def index 979fbe9..e1f3cbc 100644 --- a/sdk/include/zxuno.def +++ b/sdk/include/zxuno.def @@ -2,6 +2,8 @@ ; ; SPDX-FileCopyrightText: Copyright (C) 2019, 2021 Antonio Villena ; +; SPDX-FileContributor: 2021 Ivan Tatarinov +; ; SPDX-License-Identifier: GPL-3.0-only ; Compatible compilers: @@ -10,38 +12,55 @@ ifndef zxuno_def_included define zxuno_def_included - define zxuno_port $fc3b - define zxuno_data $fd3b - define master_conf 0 - define master_mapper 1 - define flash_spi 2 - define flash_cs 3 - define scan_code 4 - define key_stat 5 - define joy_conf 6 - define key_map 7 - define nmi_event 8 - define mouse_data 9 - define mouse_status 10 - define scandbl_ctrl 11 - define raster_line 12 - define raster_ctrl 13 - define dev_control 14 - define core_addr $fc - define core_boot $fd - define cold_boot $fe - define core_id $ff + define zxuno_port $fc3b ; ZX-UNO ZXI register address + define zxuno_data $fd3b ; ZX-UNO ZXI register data + define master_conf 0 + define master_mapper 1 + define flash_spi 2 + define flash_cs 3 + define scan_code 4 + define key_stat 5 + define joy_conf 6 + define key_map 7 + define nmi_event 8 + define mouse_data 9 + define mouse_status 10 + define scandbl_ctrl 11 + define raster_line 12 + define raster_ctrl 13 + define dev_control 14 + define dev_control2 15 + define newreg 16 + define dma_ctrl $a0 ; ZX-UNO register to start/stop DMA + define dma_src $a1 ; ZX-UNO register to set DMA source + define dma_dst $a2 ; ZX-UNO register to set DMA destination + define dma_pre $a3 ; ZX-UNO register to set DMA prescaler + define dma_len $a4 ; ZX-UNO register to set DMA length + define dma_prob $a5 + define dma_stat $a6 + define ad724 $fb + define core_addr $fc + define core_boot $fd + define cold_boot $fe + define core_id $ff - define SPI_PORT $eb - define OUT_PORT $e7 - define MMC_0 $fe ; D0 LOW = SLOT0 active - define CMD0 $40 - define CMD1 $41 - define CMD8 $48 - define SET_BLOCKLEN $50 - define READ_SINGLE $51 - define CMD41 $69 - define CMD55 $77 - define CMD58 $7a + ; MMC/SDC interface: + define SPI_PORT $eb + define OUT_PORT $e7 + define MMC_0 $fe ; D0 LOW = SLOT0 active + + ; MMC/SDC commands: + define CMD0 $40+0 ; GO_IDLE_STATE + define CMD1 $40+1 ; SEND_OP_COND (MMC) + define CMD8 $40+8 ; SEND_IF_COND + define SET_BLOCKLEN $40+16 + define READ_SINGLE $40+17 ; READ_SINGLE_BLOCK + define WRITE_BLOCK $40+24 + define CMD41 $40+41 ; SD_SEND_OP_COND (SDC) + define CMD55 $40+55 ; APP_CMD + define CMD58 $40+58 ; READ_OCR + + ; SpecDrum interface: + define specdrum_port $ffdf ; Specdrum I/O port in 16-bit format for DMA destination endif ; !zxuno_def_included diff --git a/utils/Makefile b/utils/Makefile index 9b63475..34e45c5 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -49,6 +49,7 @@ BINS=\ BACKUP\ CORCLEAN\ COREBIOS\ + DMAPLAYW\ ROMSBACK\ ROMSUPGR\ UPGR16M\ @@ -133,6 +134,12 @@ build/COREBIOS: $(srcdir)/corebios.asm\ | build $(AS) $(AFLAGS) --raw=$@ $< +build/DMAPLAYW: $(srcdir)/dmaplayw.asm\ + $(INCLUDEDIR)/zxuno.def\ + $(INCLUDEDIR)/esxdos.def\ + | build + $(AS) $(AFLAGS) --raw=$@ $< + build/ROMSBACK: $(srcdir)/romsback.asm\ $(INCLUDEDIR)/zxuno.def\ $(INCLUDEDIR)/esxdos.def\ diff --git a/utils/dmaplayw.asm b/utils/dmaplayw.asm new file mode 100644 index 0000000..99a97c7 --- /dev/null +++ b/utils/dmaplayw.asm @@ -0,0 +1,334 @@ +; dmaplayw.asm - play an audio file using the SpecDrum and DMA at 15.625 kHz. +; +; Copyright (C) 2017 AZXUNO association +; Contributors: +; 2021 Ivan Tatarinov +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; +; SPDX-FileCopyrightText: Copyright (C) 2017 AZXUNO association +; +; SPDX-FileContributor: 2021 Ivan Tatarinov +; +; SPDX-License-Identifier: GPL-3.0-or-later + +; Compatible compilers: +; SJAsmPlus, + +; output DMAPLAYW + + define PROGRAM "dmaplayw" + define VERSION "0.1" + + include "zxuno.def" + include "esxdos.def" + + define CPU_FREQ 3500000 + define SPECDRUM_FREQ 15625 + + ; Prescaler for timed DMA. The count goes from 0 to 223, that is, 224 cycles + define DMA_PRESCALER CPU_FREQ / SPECDRUM_FREQ - 1 + + org $2000 ; entry point of ESXDOS program + +;----------------------------------------------------------------------------- +; Subroutine +; In: HL = pointer to the command line arguments string (ASCIIZ) + +Main ld a, h + or l + jp nz, Init + ld hl, aUsage +.PrintLoop ld a, (hl) + or a + ret z ; Return on string end + rst $10 + inc hl + jr .PrintLoop + +;----------------------------------------------------------------------------- +; Subroutine +; In: HL = pointer to the command line arguments (filename) + +GetFileName ld de, BufferFileName +.CheckCharacter ld a, (hl) + or a + jr z, .End + cp " " + jr z, .End + cp ":" + jr z, .End + cp 13 + jr z, .End + ldi + jr .CheckCharacter +.End xor a + ld (de), a + inc de ; DE remains pointing to the buffer that is needed in OPEN, I don't know what for + ret + +; 01234567890123456789012345678901 +aUsage db " .", PROGRAM, " audiofile.wav", 13 + db 13 + db "Plays an audio file using the", 13 + db "SpecDrum and DMA at 15.625 kHz", 13, 0 + +;----------------------------------------------------------------------------- +; Subroutine +; In: HL = pointer to the command line arguments string (ASCIIZ) + +Init call GetFileName ; results DE = buffer for OPEN + xor a + esxdos M_GETSETDRV ; A = current unit + ld b, FA_READ ; B = opening mode + ld hl, BufferFileName ; HL = pointer to file name (ASCIIZ) + esxdos F_OPEN + ret c ; Return on error + ld (FHandle), a + ld l, 0 ; SEEK_START + ld bc, 0 + ld de, 44 ; Skip 44 bytes from start (WAV header) + esxdos F_SEEK + ret c ; Return on error + ld hl, ScreenAddr + ld de, ScreenAddr+1 + ld bc, 31 + ld (hl), 255 + ldir ; This line will be deleted on the first wave plot + + ld hl, DMABuffer + ld de, DMABuffer+1 + ld bc, DMABuffer_Len-1 + ld (hl), 0 + ldir ; Clear DMA buffer + + ; Prepare to play + di + ld hl, DMABuffer + ld bc, zxuno_port + ld a, dma_src + out (c), a + inc b ; BC = zxuno_data + out (c), l + out (c), h + dec b ; BC = zxuno_port + ld a, dma_dst + out (c), a + inc b ; BC = zxuno_data + ld hl, specdrum_port + out (c), l + out (c), h + dec b ; BC = zxuno_port + ld a, dma_pre + out (c), a + inc b ; BC = zxuno_data + ld hl, DMA_PRESCALER + out (c), l + out (c), h + dec b ; BC = zxuno_port + ld a, dma_len + out (c), a + inc b ; BC = zxuno_data + ld hl, DMABuffer_Len + out (c), l + out (c), h + dec b ; BC = zxuno_port + ld a, dma_prob + out (c), a + inc b ; BC = zxuno_data + ld hl, DMABuffer + out (c), l + out (c), h + dec b ; BC = zxuno_port + ld a, dma_ctrl + out (c), a + inc b ; BC = zxuno_data + ld a, %00000111 ; Mem to I/O, redisparable, timed, source address is checked + out (c), a + dec b ; BC = zxuno_port +.PlayLoop ld bc, $7ffe ; SPACE halfrow + in a, (c) + and %00000001 + jp z, .ExitPlay + ld bc, zxuno_port + ld a, dma_stat + out (c), a + inc b ; BC = zxuno_data +.StillInSecondHalf + in a, (c) + bit 7, a + jr z, .StillInSecondHalf + dec b ; BC = zxuno_port + ld a, dma_prob + out (c), a + inc b ; BC = zxuno_data + ld hl, DMABuffer + DMABuffer_Len/2 + out (c), l + out (c), h + dec b ; BC = zxuno_port + ld a, dma_stat + out (c), a + inc b ; BC = zxuno_data + in a, (c) + + ; Fill second half of buffer with audio data + ld hl, DMABuffer + DMABuffer_Len/2 + ld bc, DMABuffer_Len/2 + ld a, (FHandle) + esxdos F_READ + jp c, .ExitPlay ; End read on error + ld a, b + or c + jp z, .ExitPlay ; End read on end of data + + ld hl, DMABuffer + DMABuffer_Len/2 + call PlotWave + + ld bc, zxuno_port + ld a, dma_stat + out (c), a + inc b ; BC = zxuno_data +.StillInFirstHalf + in a, (c) + bit 7, a + jr z, .StillInFirstHalf + + dec b ; BC = zxuno_port + ld a, dma_prob + out (c), a + inc b ; BC = zxuno_data + ld hl, DMABuffer + out (c), l + out (c), h + dec b ; BC = zxuno_port + ld a, dma_stat + out (c), a + inc b ; BC = zxuno_data + in a, (c) + + ; Fill first half of buffer with audio data + ld hl, DMABuffer + ld bc, DMABuffer_Len/2 + ld a, (FHandle) + esxdos F_READ + jp c, .ExitPlay ; End read on error + ld a, b + or c + jp z, .ExitPlay ; End read on end of data + + ld hl, DMABuffer + call PlotWave + + jp .PlayLoop + +.ExitPlay ld bc, zxuno_port + ld a, dma_ctrl + out (c), a + inc b ; BC = zxuno_data + xor a + out (c), a + dec b ; BC = zxuno_port + + ld a, (FHandle) + esxdos F_CLOSE + + or a ; Clear carry flag + ei + ret + +FHandle db 0 + +;----------------------------------------------------------------------------- +; Subroutine + +PlotWave ld de, BufferCleared + ld c, 0 +.Loop ld a, (de) + ld b, a + call Plot + ld a, (hl) + srl a + add a, 24 + ld b, a + call Plot + ld a, b + ld (de), a + inc hl + inc de + inc c + jp nz, .Loop + ret + +;----------------------------------------------------------------------------- +; Subroutine +; In: B = y +; C = x + +Plot push bc + push de + push hl + ld e, b + ld d, 0 ; DE = y + sla e + rl d ; DE = DE*2 + ld hl, DirScan + add hl, de ; HL = pointer to the address of the first pixel of Y + ld e, (hl) + inc hl + ld d, (hl) + ex de, hl ; HL = dir first pixel row Y + ld d, c ; save X coordinate in D + ld a, c + .3 srl a + ld c, a + ld b, 0 + add hl, bc ; HL contains the address to paint the pixel + ld a, d ; restore X coordinate + and %00000111 + ld de, DirBits + add a, e + ld e, a + ld a, d + adc a, 0 + ld d, a + ld a, (de) + xor (hl) + ld (hl), a + pop hl + pop de + pop bc + ret + +DirBits +i = %10000000 + while i > 0 + db i +i = i / 2 + endw + +DirScan +y = 0 + while y < 192 + dw ScreenAddr + (256 * (y & 7)) + (32 * ((y / 8) & 7)) + (64 * 32 * (y / 64)) +y = y + 1 + endw + +BufferCleared ds 256 + +BufferFileName: equ $ ; Rest of RAM for filename + +ScreenAddr: equ $4000 + +DMABuffer: equ $8000 ; DMA buffer start address (circular play buffer) +DMABuffer_Len: equ 2048 ; DMA buffer length