Skip to content

Commit

Permalink
nullsound: z80 fixes for mame / real hardware
Browse files Browse the repository at this point in the history
* Init didn't configure z80 ports appropriately [1] so NMI were not
received by the sound driver, and no sound was emitted on Mame.
This is likely the same issue on real hardware.

* Sound commands 01/03 didn't mute the ADPCMa channel correcly, this
one is fixed. More improvement will follow in another commit to mute
all channels (SSG, ADPCMa/b, FM).

* The write to YM2610 was unecessarily waiting for YM2610 status.
This is now changed in favour of busy waiting on Z80 to allow the
YM2610 to settle between writes. This follows the information from
neogeodev wiki [1], but could not be tested on real hardware. This is
working on Mame and GnGeo.

[1] https://wiki.neogeodev.org/index.php?title=Z80_port_map
[2] https://wiki.neogeodev.org/index.php?title=Z80/YM2610_interface

Closes #64
  • Loading branch information
dciabrin committed Jun 6, 2023
1 parent c69ad54 commit 859fc94
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 72 deletions.
71 changes: 60 additions & 11 deletions nullsound/driver/nullsound.s
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
;;;
;;; nullsound - modular sound driver
;;; Copyright (c) 2018-2020 Damien Ciabrini
;;; Copyright (c) 2018-2023 Damien Ciabrini
;;; This file is part of ngdevkit
;;;
;;; ngdevkit is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -40,15 +40,35 @@
;;;
.area START (ABS)
di
;; at boot, stay idle in RAM until the 68k triggers a sound
;; command with a NMI to initialize and start the driver
prepare_wait_in_ram_opcode
ret

_rom_info:
nullsound_id
jp init_z80_and_wait


;;; Restart handlers. An custom jump-table to the most called functions
;;; in this Z80 ROM. Accessed with the `RST` opcode, which is slightly
;;; faster and space-efficient than a regular `CALL` opcode.
.org 0x0008
ret ; unused
.org 0x0010
ret ; unused
.org 0x0018
ret ; unused
.org 0x0020
ret ; unused
.org 0x0028
ret ; unused
.org 0x0030
ret ; unused


;;; INT handler for the two interrupts triggered by the YM2610
;;; (fixed address 0x0038 when the Z80 uses Interrupt Mode 1)
.org 0x0038
di
;; TODO
ei
reti

;;; NMI handler (fixed address 0x0066 in the z80)
;;; NMI handler (fixed address 0x0066 in the Z80)
.org 0x0066
;; common driver commands
ex af, af'
Expand All @@ -64,14 +84,18 @@ _end_nmi::
retn


_rom_info:
nullsound_id


;; the rest of the code is relative, to allow linking objects
;; set the location counter arbitrary far to not overwrite START
.area CODE
. = . + 0x0100

;;; the jump table to the a sound command requested via NMI
;;; the jump table to a sound command requested via NMI
;;; each entry is "jp <address>" (3 bytes)
;;; max 128 entries allowed in the sound driver
cmd_jmptable:
;; common/reserved sound commands
jp snd_command_nop
Expand All @@ -82,7 +106,7 @@ cmd_jmptable:
user_jmptable
cmd_jmptable_padding:
;; fill remaining commands if any
.rept 256-((cmd_jmptable_padding - cmd_jmptable)/3)
.rept 128-((cmd_jmptable_padding - cmd_jmptable)/3)
jp snd_command_nop
.endm

Expand All @@ -93,6 +117,31 @@ cmd_jmptable_padding:
.include "ym2610.def"
.include "ym2610.s"


;;; This performs a very minimal Z80 initialization to quickly
;;; mute the ym2610 and to stay idle in RAM until the 68k sets up
;;; the proper Z80 ROM and triggers the sound driver initialization

init_z80_and_wait:
;; Configure the Z80 for interrupt mode 1 (fixed handler @ 0x0038)
im 1
;; On the platform, the Z80 only receives NMIs once the Z80 ports
;; mapped to bankswitching have been written to.
xor a
out (PORT_ENABLE_NMI), a
;; Mute sound
call snd_reset_ym2610
;; At this point, prepare to stay idle in RAM. This allows multi-slot
;; MVS cabinets to switch game and map the game's sound ROM in the
;; Z80 address space.
prepare_wait_in_ram_opcodes
;; Returns to RAM and busy-loop until a NMI is triggered and resumes
;; the driver's initialization
ret




;; Reserved commands (to init/reset the driver)
.include "nullsound_commands.s"

Expand Down
19 changes: 7 additions & 12 deletions nullsound/driver/nullsound_commands.s
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
;;;
;;; nullsound - modular sound driver
;;; Copyright (c) 2020 Damien Ciabrini
;;; Copyright (c) 2020-2023 Damien Ciabrini
;;; This file is part of ngdevkit
;;;
;;; ngdevkit is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -33,22 +33,17 @@ snd_command_nop:
;;; the new ROM.
snd_command_01_prepare_for_rom_switch:
di
;; signal we're preparing the reset
;; Acknowledge command to the 68k
xor a
out (PORT_TO_68K), a
out (PORT_FROM_68K), a
;; reset the ym2610
call snd_reset_ym2610
;; ;; build a jmp-to-self instruction in RAM
;; ld bc, #0xfe18 ; jr <self>
;; ld hl, #0xfffe
;; ld sp, hl
;; push bc
;; push hl
prepare_wait_in_ram_opcode
prepare_wait_in_ram_opcodes
;; signal the 68k that we're ready
ld a, #1
out (0x0c), a
out (PORT_TO_68K), a
;; return from NMI in top of RAM and loop there
retn

Expand All @@ -73,11 +68,11 @@ snd_command_03_reset_driver:
;;; mute the ym2610 and reset its master volume
snd_reset_ym2610:
ld b, #REG_ADPCM_A_START_STOP
ld a, #0x80 ; stop all channels
call ym2610_set_register_ports_6_7
ld c, #0x80 ; stop all channels
call ym2610_write_port_b
ld b, #REG_ADPCM_A_MASTER_VOLUME
ld c, #0x3f ; loudest
call ym2610_set_register_ports_6_7
call ym2610_write_port_b
ret


Expand Down
20 changes: 10 additions & 10 deletions nullsound/driver/sfx_adpcma.s
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
;;;
;;; nullsound - modular sound driver
;;; Copyright (c) 2020 Damien Ciabrini
;;; Copyright (c) 2020-2023 Damien Ciabrini
;;; This file is part of ngdevkit
;;;
;;; ngdevkit is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -75,12 +75,12 @@ sfx_adpcm_a_configure:
ld a, CHANNEL(ix)
or #0x80
ld c, a
call ym2610_set_register_ports_6_7
call ym2610_write_port_b
ld b, #REG_ADPCM_PLAYBACK_MASK
ld c, CHANNEL(ix)
call ym2610_set_register_ports_4_5
call ym2610_write_port_a
ld c, #0
call ym2610_set_register_ports_4_5
call ym2610_write_port_a
;; prepare the sample addresses
ld a, #REG_ADPCM_A1_ADDR_START_LSB
ld b, CHANNEL(ix)
Expand All @@ -94,37 +94,37 @@ _sfx_adpcm_a_addr:
_sfx_adpcm_a_addr_end:
ld b, a
ld c, START_LSB(ix)
call ym2610_set_register_ports_6_7
call ym2610_write_port_b

;; sample start MSB
add a, #8
ld b, a
ld c, START_MSB(ix)
call ym2610_set_register_ports_6_7
call ym2610_write_port_b

;; sample stop LSB
add a, #8
ld b, a
ld c, STOP_LSB(ix)
call ym2610_set_register_ports_6_7
call ym2610_write_port_b

;; sample stop MSB
add a, #8
ld b, a
ld c, STOP_MSB(ix)
call ym2610_set_register_ports_6_7
call ym2610_write_port_b

;; channel volume
ld a, #(REG_ADPCM_A1_VOL-1)
add a, CHANNEL(ix)
ld b, a
ld c, VOLUME(ix)
call ym2610_set_register_ports_6_7
call ym2610_write_port_b

;; play channel
ld b, #REG_ADPCM_A_START_STOP
ld c, CHANNEL(ix)
call ym2610_set_register_ports_6_7
call ym2610_write_port_b

;; yield to the mainloop
ld bc, #VOLUME
Expand Down
67 changes: 38 additions & 29 deletions nullsound/driver/ym2610.s
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
;;;
;;; nullsound - modular sound driver
;;; Copyright (c) 2020 Damien Ciabrini
;;; Copyright (c) 2020-2023 Damien Ciabrini
;;; This file is part of ngdevkit
;;;
;;; ngdevkit is free software: you can redistribute it and/or modify
Expand All @@ -18,53 +18,62 @@



;;; ym2610_set_register_ports_4_5
;;; -----------------------------
;;; ym2610_write_port_a
;;; -------------------
;;; IN:
;;; b: register address in ym2610
;;; c: data to set
;;; (all registers are preserved)
ym2610_set_register_ports_4_5::
ym2610_write_port_a::
push af
;; select address register
;; select register address
ld a, b
out (PORT_4_ADDR), a
_wait_address_ready_45:
in a, (PORT_YM2610_STATUS)
bit 7, a
jp nz, _wait_address_ready_45
out (PORT_YM2610_A_ADDR), a
call _ym2610_wait_address_write
;; set data in the selected register
ld a, c
out (PORT_5_VALUE), a
_wait_data_ready_45:
in a, (PORT_YM2610_STATUS)
bit 7, a
jp nz, _wait_data_ready_45
out (PORT_YM2610_A_VALUE), a
call _ym2610_wait_data_write
pop af
ret


;;; ym2610_set_register_ports_6_7
;;; -----------------------------
;;; ym2610_write_port_b
;;; -------------------
;;; IN:
;;; b: register address in ym2610
;;; c: data to set
;;; (all registers are preserved)
ym2610_set_register_ports_6_7::
ym2610_write_port_b::
push af
;; select address register
;; select register address
ld a, b
out (PORT_6_ADDR), a
_wait_address_ready:
in a, (PORT_YM2610_STATUS)
bit 7, a
jp nz, _wait_address_ready
out (PORT_YM2610_B_ADDR), a
call _ym2610_wait_address_write
;; set data in the selected register
ld a, c
out (PORT_7_VALUE), a
_wait_data_ready:
in a, (PORT_YM2610_STATUS)
bit 7, a
jp nz, _wait_data_ready
out (PORT_YM2610_B_VALUE), a
call _ym2610_wait_data_write
pop af
ret


;;; From https://wiki.neogeodev.org/index.php?title=Z80/YM2610_interface
;;; YM2610 requires at least 2.125us before accepting another write
;;; call + nop + ret = 24 T-cycles (6us?)
_ym2610_wait_address_write:
nop
ret


;;; From https://wiki.neogeodev.org/index.php?title=Z80/YM2610_interface
;;; YM2610 requires at least 10.375us before accepting another write
;;; call + pushes + ret = 83 T-cycles (20.75ms?)
_ym2610_wait_data_write:
push bc
push de
push hl
pop hl
pop de
pop bc
ret
11 changes: 6 additions & 5 deletions nullsound/macros/nullsound.def
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
;;;
;;; nullsound - modular sound driver
;;; Copyright (c) 2020 Damien Ciabrini
;;; Copyright (c) 2020-2023 Damien Ciabrini
;;; This file is part of ngdevkit
;;;
;;; ngdevkit is free software: you can redistribute it and/or modify
Expand Down Expand Up @@ -57,13 +57,14 @@
.endm

;; private helper to generate z80 opcode to stay idle in RAM
.macro prepare_wait_in_ram_opcode
;; build a jmp-to-self instruction at the top of RAM
.macro prepare_wait_in_ram_opcodes
;; create a jmp-to-self instruction at the top of the RAM
;; to ensure the 68k can bank-switch z80 code without impact
ld bc, #0xfe18 ; jr <self>
ld (#0xfffe), bc
;; ;; prepare the stack to pop into the jump-to-self in RAM
ld hl, #0xfffe
ld sp, hl
push bc
;; popping the stack will lock the Z80 in RAM
push hl
.endm

Expand Down
12 changes: 7 additions & 5 deletions nullsound/macros/ym2610.def
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
;;; -*- asm -*-
;;; nullsound - modular sound driver
;;; Copyright (c) 2020 Damien Ciabrini
;;; Copyright (c) 2020-2023 Damien Ciabrini
;;; This file is part of ngdevkit
;;;
;;; ngdevkit is free software: you can redistribute it and/or modify
Expand All @@ -26,11 +26,13 @@
.equ PORT_YM2610_STATUS, 0x04
.equ PORT_PLAYBACK_FINISHED, 0x06
;; write ports
.equ PORT_4_ADDR, 0x04
.equ PORT_5_VALUE, 0x05
.equ PORT_6_ADDR, 0x06
.equ PORT_7_VALUE, 0x07
.equ PORT_YM2610_A_ADDR, 0x04
.equ PORT_YM2610_A_VALUE, 0x05
.equ PORT_YM2610_B_ADDR, 0x06
.equ PORT_YM2610_B_VALUE, 0x07
.equ PORT_ENABLE_NMI, 0x08
.equ PORT_TO_68K, 0x0c
.equ PORT_DISABLE_NMI, 0x18

;;; ADPCM playback status
;;; These macros are meant to be used with port 4 and port 5
Expand Down

0 comments on commit 859fc94

Please sign in to comment.