From 8fa3a7442da94b8dbceb13c724c697b5ba46091d Mon Sep 17 00:00:00 2001 From: jduraes Date: Sat, 6 Dec 2025 16:50:14 +0000 Subject: [PATCH] Add VGMINFO.COM utility for CP/M This utility scans .VGM files in the current directory and displays which sound chips they use. Useful for determining VGM compatibility with RomWBW's VGM player. Features: - Scans all .VGM files in current directory - Detects SN76489, YM2612, YM2151, and AY-3-8910 chips - Displays results in a formatted table - Supports dual-chip configurations Could you please integrate this into the RomWBW build process alongside the existing VGM player utilities? --- Source/Apps/VGM/vgminfo.asm | 539 ++++++++++++++++++++++++++++++++++++ 1 file changed, 539 insertions(+) create mode 100644 Source/Apps/VGM/vgminfo.asm diff --git a/Source/Apps/VGM/vgminfo.asm b/Source/Apps/VGM/vgminfo.asm new file mode 100644 index 00000000..f07c0443 --- /dev/null +++ b/Source/Apps/VGM/vgminfo.asm @@ -0,0 +1,539 @@ +;------------------------------------------------------------------------------ +; VGM File Info Display for CP/M +;------------------------------------------------------------------------------ +; +; Scans all .VGM files in current directory and displays chip information +; in a formatted table +; +; (c) 2025 Joao Miguel Duraes +; Licensed under the MIT License +; +; Version: 1.0 - 06-Dec-2025 +; +; Assemble with: +; TASM -80 -b vgminfo.asm vgminfo.com +; +;------------------------------------------------------------------------------ + +;------------------------------------------------------------------------------ +; CP/M definitions +;------------------------------------------------------------------------------ + +BOOT .equ 0000H ; boot location +BDOS .equ 0005H ; bdos entry point +FCB .equ 005CH ; file control block +FCBCR .equ FCB + 20H ; fcb current record +BUFF .equ 0080H ; DMA buffer + +PRINTF .equ 9 ; BDOS print string function +OPENF .equ 15 ; BDOS open file function +CLOSEF .equ 16 ; BDOS close file function +READF .equ 20 ; BDOS sequential read function +SETDMA .equ 26 ; BDOS set DMA address +SFIRST .equ 17 ; BDOS search first +SNEXT .equ 18 ; BDOS search next + +CR .equ 0DH ; carriage return +LF .equ 0AH ; line feed + +;------------------------------------------------------------------------------ +; VGM Header offsets +;------------------------------------------------------------------------------ + +VGM_IDENT .equ 00H ; "Vgm " identifier +VGM_VERSION .equ 08H ; Version +VGM_DATAOFF .equ 34H ; VGM data offset (relative to 0x34) + +;------------------------------------------------------------------------------ +; VGM Command codes (subset) +;------------------------------------------------------------------------------ + +VGM_PSG1_W .equ 050H ; PSG (SN76489) write +VGM_PSG2_W .equ 030H ; PSG #2 write +VGM_YM26121_W .equ 052H ; YM2612 port 0 write +VGM_YM26122_W .equ 053H ; YM2612 port 1 write +VGM_YM26123_W .equ 0A2H ; YM2612 #2 port 0 write +VGM_YM26124_W .equ 0A3H ; YM2612 #2 port 1 write +VGM_YM21511_W .equ 054H ; YM2151 write +VGM_YM21512_W .equ 0A4H ; YM2151 #2 write +VGM_AY_W .equ 0A0H ; AY-3-8910 write +VGM_ESD .equ 066H ; End of sound data +VGM_WNS .equ 061H ; Wait n samples +VGM_W735 .equ 062H ; Wait 735 samples +VGM_W882 .equ 063H ; Wait 882 samples + +;------------------------------------------------------------------------------ +; Program Start +;------------------------------------------------------------------------------ + + .ORG 100H + +START: LD SP, STACK ; Setup stack + + ; Display header + LD DE, MSG_HEADER + CALL PRTSTR + LD DE, MSG_DIVIDER + CALL PRTSTR + + ; Setup search for *.VGM files + LD DE, SEARCH_FCB + LD C, SFIRST + CALL BDOS + CP 0FFH ; No files found? + JP Z, NO_FILES + +FILE_LOOP: + ; A contains directory entry index (0-3) + ; Each entry is 32 bytes, so multiply by 32 + AND 03H ; Mask to 0-3 + RLCA + RLCA + RLCA + RLCA + RLCA ; Multiply by 32 + LD L, A + LD H, 0 + LD DE, BUFF + ADD HL, DE ; HL now points to directory entry + + ; Copy filename from directory entry to our FCB + INC HL ; Skip user number + LD DE, FILE_FCB+1 ; Destination + LD BC, 11 ; 8+3 filename + LDIR + + ; Open and process this file + CALL PROCESS_FILE + + ; Search for next file + LD DE, SEARCH_FCB + LD C, SNEXT + CALL BDOS + CP 0FFH + JP NZ, FILE_LOOP + + ; Done + LD DE, MSG_DIVIDER + CALL PRTSTR + JP BOOT ; Exit to CP/M + +NO_FILES: LD DE, MSG_NOFILES + CALL PRTSTR + JP BOOT + +;------------------------------------------------------------------------------ +; Process a VGM file - read header and display info +;------------------------------------------------------------------------------ + +PROCESS_FILE: + ; Reset FCB + XOR A + LD (FILE_FCB), A ; Default drive + LD (FILE_FCB+12), A ; Clear extent + LD (FILE_FCB+32), A ; Clear current record + + ; Open file + LD DE, FILE_FCB + LD C, OPENF + CALL BDOS + CP 0FFH + RET Z ; Can't open, skip + + ; Set DMA to our buffer for first block + LD DE, VGMBUF + LD C, SETDMA + CALL BDOS + + ; Read first 128 bytes (header) + LD DE, FILE_FCB + LD C, READF + CALL BDOS + OR A + JR NZ, READ_DONE ; EOF or error + + ; Read second 128 bytes (to allow scanning right after header) + LD DE, VGMBUF+128 + LD C, SETDMA + CALL BDOS + LD DE, FILE_FCB + LD C, READF + CALL BDOS + + ; Read third 128 bytes + LD DE, VGMBUF+256 + LD C, SETDMA + CALL BDOS + LD DE, FILE_FCB + LD C, READF + CALL BDOS + + ; Read fourth 128 bytes + LD DE, VGMBUF+384 + LD C, SETDMA + CALL BDOS + LD DE, FILE_FCB + LD C, READF + CALL BDOS + +READ_DONE: + + ; Restore DMA + LD DE, BUFF + LD C, SETDMA + CALL BDOS + + ; Close file + LD DE, FILE_FCB + LD C, CLOSEF + CALL BDOS + + ; Check if valid VGM + LD HL, VGMBUF + LD A, (HL) + CP 'V' + RET NZ + INC HL + LD A, (HL) + CP 'g' + RET NZ + INC HL + LD A, (HL) + CP 'm' + RET NZ + INC HL + LD A, (HL) + CP ' ' + RET NZ + + ; Display filename (exactly 8 chars from FCB) + LD HL, FILE_FCB+1 + LD B, 8 +PRINT_NAME: LD A, (HL) + CALL PRTCHR + INC HL + DJNZ PRINT_NAME + + ; Add 2-space gap + LD A, ' ' + CALL PRTCHR + LD A, ' ' + CALL PRTCHR + + +PAD_DONE: + + ; Check and display chip info + CALL CHECK_CHIPS + + ; New line + CALL CRLF + + RET + +;------------------------------------------------------------------------------ +; Check which chips are used by scanning VGM command stream (first ~100 cmds) +;------------------------------------------------------------------------------ + +CHECK_CHIPS: + ; Initialize chip flags + XOR A + LD (CHIP_FLAGS), A + + ; Compute absolute data start within VGMBUF window + LD HL, (VGMBUF+VGM_DATAOFF) + LD A, H + OR L + JR NZ, GOT_OFFSET + LD HL, 000CH ; Default for VGM < 1.50 (0x40-0x34) +GOT_OFFSET: LD DE, VGMBUF+VGM_DATAOFF + ADD HL, DE ; HL = VGMBUF + 0x34 + offset + + ; Constrain to our 256-byte buffer + LD DE, VGMBUF + SBC HL, DE ; HL = offset from VGMBUF base + ADD HL, DE ; restore HL absolute inside VGMBUF + + ; Scan up to 100 commands or until EOD + LD C, 100 +SCAN_LOOP: LD A, (HL) + INC HL + + CP VGM_ESD + JP Z, SCAN_DONE + + CP VGM_PSG1_W + JP NZ, CHK_PSG2 + LD A, (CHIP_FLAGS) + OR 01H ; bit 0 = SN #1 + LD (CHIP_FLAGS), A + INC HL ; Skip data byte + JP SCAN_NEXT + +CHK_PSG2: CP VGM_PSG2_W + JP NZ, CHK_YM2612 + LD A, (CHIP_FLAGS) + OR 02H ; bit 1 = SN #2 + LD (CHIP_FLAGS), A + INC HL + JP SCAN_NEXT + +CHK_YM2612: CP VGM_YM26121_W + JR Z, GOT_YM2612_1 + CP VGM_YM26122_W + JR Z, GOT_YM2612_1 + CP VGM_YM26123_W + JR Z, GOT_YM2612_2 + CP VGM_YM26124_W + JP NZ, CHK_YM2151 +GOT_YM2612_2: LD A, (CHIP_FLAGS) + OR 08H ; bit 3 = YM2612 #2 + LD (CHIP_FLAGS), A + INC HL + INC HL ; Skip 2 data bytes + JP SCAN_NEXT +GOT_YM2612_1: LD A, (CHIP_FLAGS) + OR 04H ; bit 2 = YM2612 #1 + LD (CHIP_FLAGS), A + INC HL + INC HL + JP SCAN_NEXT + +CHK_YM2151: CP VGM_YM21511_W + JR Z, GOT_YM2151_1 + CP VGM_YM21512_W + JP NZ, CHK_AY + LD A, (CHIP_FLAGS) + OR 20H ; bit 5 = YM2151 #2 + LD (CHIP_FLAGS), A + INC HL + INC HL + JP SCAN_NEXT +GOT_YM2151_1: LD A, (CHIP_FLAGS) + OR 10H ; bit 4 = YM2151 #1 + LD (CHIP_FLAGS), A + INC HL + INC HL + JP SCAN_NEXT + +CHK_AY: CP VGM_AY_W + JP NZ, CHK_WAIT + LD A, (HL) ; Get register/chip byte + BIT 7, A ; Bit 7 = chip 2? + JR Z, GOT_AY1 + LD A, (CHIP_FLAGS) + OR 80H ; bit 7 = AY #2 + LD (CHIP_FLAGS), A + INC HL + INC HL + JP SCAN_NEXT +GOT_AY1: LD A, (CHIP_FLAGS) + OR 40H ; bit 6 = AY #1 + LD (CHIP_FLAGS), A + INC HL + INC HL ; Skip 2 data bytes + JP SCAN_NEXT + +CHK_WAIT: CP VGM_WNS + JR NZ, CHK_W735 + INC HL + INC HL ; Skip 2-byte wait value + JP SCAN_NEXT + +CHK_W735: CP VGM_W735 + JR Z, SCAN_NEXT + CP VGM_W882 + JR Z, SCAN_NEXT + + ; Unknown command or short wait 0x70-0x7F -> just continue + CP 70H + JR C, SCAN_NEXT + CP 80H + JR NC, SCAN_NEXT + +SCAN_NEXT: DEC C + JP NZ, SCAN_LOOP + +SCAN_DONE: ; Display chips found + LD B, 0 ; Chip counter + LD A, (CHIP_FLAGS) + LD C, A ; Save flags + + ; SN76489 + AND 03H ; bits 0-1 + JP Z, NO_SN + LD A, B + OR A + CALL NZ, PRINT_COMMA + LD A, C + AND 03H + CP 03H ; Both chips? + JR Z, SN_DUAL + LD DE, MSG_SN76489 + CALL PRTSTR + JR SN_DONE +SN_DUAL: LD DE, MSG_SN76489X2 + CALL PRTSTR +SN_DONE: INC B +NO_SN: + ; YM2612 + LD A, C + AND 0CH ; bits 2-3 + JR Z, NO_YM2612 + LD A, B + OR A + CALL NZ, PRINT_COMMA + LD A, C + AND 0CH + CP 0CH ; Both chips? + JR Z, YM2612_DUAL + LD DE, MSG_YM2612 + CALL PRTSTR + JR YM2612_DONE +YM2612_DUAL: LD DE, MSG_YM2612X2 + CALL PRTSTR +YM2612_DONE: INC B +NO_YM2612: + ; YM2151 + LD A, C + AND 30H ; bits 4-5 + JR Z, NO_YM2151 + LD A, B + OR A + CALL NZ, PRINT_COMMA + LD A, C + AND 30H + CP 30H ; Both chips? + JR Z, YM2151_DUAL + LD DE, MSG_YM2151 + CALL PRTSTR + JR YM2151_DONE +YM2151_DUAL: LD DE, MSG_YM2151X2 + CALL PRTSTR +YM2151_DONE: INC B +NO_YM2151: + ; AY-3-8910 + LD A, C + AND 0C0H ; bits 6-7 + JR Z, NO_AY + LD A, B + OR A + CALL NZ, PRINT_COMMA + LD A, C + AND 0C0H + CP 0C0H ; Both chips? + JR Z, AY_DUAL + LD DE, MSG_AY8910 + CALL PRTSTR + JR AY_DONE +AY_DUAL: LD DE, MSG_AY8910X2 + CALL PRTSTR +AY_DONE: INC B +NO_AY: + ; None + LD A, B + OR A + RET NZ + LD DE, MSG_UNKNOWN + CALL PRTSTR + RET + +PRINT_COMMA: LD A, ',' + CALL PRTCHR + LD A, ' ' + CALL PRTCHR + RET + + +;------------------------------------------------------------------------------ +; Print string pointed to by DE (terminated by 0) +;------------------------------------------------------------------------------ + +PRTSTR: LD A, (DE) + OR A + RET Z + CALL PRTCHR + INC DE + JR PRTSTR + +;------------------------------------------------------------------------------ +; Print character in A +;------------------------------------------------------------------------------ + +PRTCHR: PUSH BC + PUSH DE + PUSH HL + LD E, A + LD C, 2 + CALL BDOS + POP HL + POP DE + POP BC + RET + +;------------------------------------------------------------------------------ +; Print CR/LF +;------------------------------------------------------------------------------ + +CRLF: LD A, CR + CALL PRTCHR + LD A, LF + CALL PRTCHR + RET + +;------------------------------------------------------------------------------ +; Messages +;------------------------------------------------------------------------------ + +MSG_HEADER: .DB CR, LF + .DB "VGM Music Chip Scanner v1.0 - 06-Dec-2025", CR, LF + .DB "(c)2025 Joao Miguel Duraes - MIT License", CR, LF + .DB CR, LF + .DB "Filename Chips Used", CR, LF + .DB 0 + +MSG_DIVIDER: .DB "======== =====================", CR, LF + .DB 0 + +MSG_NOFILES: .DB "No .VGM files found in current directory", CR, LF + .DB 0 + +MSG_SN76489: .DB "SN76489", 0 +MSG_SN76489X2: .DB "2xSN76489", 0 +MSG_YM2612: .DB "YM2612", 0 +MSG_YM2612X2: .DB "2xYM2612", 0 +MSG_YM2151: .DB "YM2151", 0 +MSG_YM2151X2: .DB "2xYM2151", 0 +MSG_AY8910: .DB "AY-3-8910", 0 +MSG_AY8910X2: .DB "2xAY-3-8910", 0 +MSG_UNKNOWN: .DB "Unknown/None", 0 + +;------------------------------------------------------------------------------ +; Data area +;------------------------------------------------------------------------------ + +; Search FCB for *.VGM +SEARCH_FCB: .DB 0 ; Default drive + .DB '?','?','?','?','?','?','?','?' ; Filename (wildcard) + .DB 'V','G','M' ; Extension + .FILL 24, 0 ; Rest of FCB + +; FCB for opening files +FILE_FCB: .DB 0 ; Default drive + .FILL 35, 0 ; Rest of FCB + +DIR_CODE: .DB 0 ; Directory code from search +CHIP_FLAGS: .DB 0 ; Detected chip flags + ; bit0 SN76489 #1, bit1 SN76489 #2 + ; bit2 YM2612 #1, bit3 YM2612 #2 + ; bit4 YM2151 #1, bit5 YM2151 #2 + ; bit6 AY #1, bit7 AY #2 + +; Buffer for VGM header + first data sector (256 bytes) +VGMBUF: .FILL 512, 0 + +; Stack space + .FILL 64, 0 +STACK: .DW 0 + + .END