Files

336 lines
11 KiB
ArmAsm

;==============================================================================
; @file opl3.s
; @brief OPL3/AY/SAA register operations for SDCC Z80 (sdcccall(1))
;
; Порты TSConfig для OPL3:
; #C4 = Bank 0 Address #C5 = Bank 0 Data
; #C6 = Bank 1 Address #C7 = Bank 1 Data
;==============================================================================
.module opl3
.area _CODE
;----------------------------------------------------------------------
; opl3_write_b0(uint8_t addr, uint8_t data) — Bank 0
;----------------------------------------------------------------------
.globl _opl3_write_b0
_opl3_write_b0::
ld c, #0xC4
jr _opl3_write
;----------------------------------------------------------------------
; opl3_write_b1(uint8_t addr, uint8_t data) — Bank 1
;----------------------------------------------------------------------
.globl _opl3_write_b1
_opl3_write_b1::
ld c, #0xC6
_opl3_write:
out (c), a ; addr (B don't-care for C4-C7)
ld hl, #2
add hl, sp
ld a, (hl)
inc c
out (c), a ; data
ret
;----------------------------------------------------------------------
; tsconf_out(uint16_t port, uint8_t val)
;----------------------------------------------------------------------
.globl _tsconf_out
_tsconf_out::
ld b, d
ld c, e
ld hl, #2
add hl, sp
ld a, (hl)
out (c), a
ret
;----------------------------------------------------------------------
; opl3_init() — OPL3 enable + wavesel + reset (falls through)
;----------------------------------------------------------------------
.globl _opl3_init
_opl3_init::
ld a, #0x05
ld c, #0xC6
out (c), a ; B don't-care for C4-C7
ld a, #0x01 ; NEW bit only (OPL3 enable)
inc c
out (c), a
ld a, #0x01
ld c, #0xC4
out (c), a
ld a, #0x20
inc c
out (c), a
; fall through to opl3_reset
;----------------------------------------------------------------------
; opl3_reset() — Full OPL3 silence: zero ALL significant registers
; Both banks: Key-Off (B0-B8=0), Total Level max atten (40-55=3F),
; Percussion off (BD=0), F-Num (A0-A8=0), Feedback (C0-C8=0),
; AM/Vib/Mult (20-35=0), AR/DR (60-75=0), SL/RR (80-95=0),
; Waveform (E0-F5=0).
; Uses _opl3_zero_range and _opl3_fill_range helpers.
;----------------------------------------------------------------------
.globl _opl3_reset
_opl3_reset::
; ── Bank 0 ──
ld c, #0xC4
call _opl3_reset_bank
; Percussion off: BD = 0 (Bank 0 only)
ld a, #0xBD
out (c), a
inc c
xor a, a
out (c), a
dec c
; ── Bank 1 ──
ld c, #0xC6
; fall through to _opl3_reset_bank
;----------------------------------------------------------------------
; _opl3_reset_bank — zero all significant regs of one OPL3 bank
; C = addr port (0xC4 or 0xC6)
; Destroys: A, B, D, E
;----------------------------------------------------------------------
_opl3_reset_bank:
; KeyOff: B0-B8 = 0
ld d, #0xB0
ld e, #9
call _opl3_zero_range
; Total Level max attenuation: 40-55 = 0x3F
ld d, #0x40
ld e, #22
ld a, #0x3F
call _opl3_fill_range
; F-Num lo: A0-A8 = 0
ld d, #0xA0
ld e, #9
call _opl3_zero_range
; Feedback/Output: C0-C8 = 0
ld d, #0xC0
ld e, #9
call _opl3_zero_range
; AM/Vib/Mult: 20-35 = 0
ld d, #0x20
ld e, #22
call _opl3_zero_range
; AR/DR: 60-75 = 0
ld d, #0x60
ld e, #22
call _opl3_zero_range
; SL/RR: 80-95 = 0
ld d, #0x80
ld e, #22
call _opl3_zero_range
; Waveform: E0-F5 = 0
ld d, #0xE0
ld e, #22
jr _opl3_zero_range ; tail-call → ret
;----------------------------------------------------------------------
; _opl3_zero_range — fill E consecutive regs starting at D with 0
; C = port, D = start reg, E = count
;----------------------------------------------------------------------
_opl3_zero_range:
xor a, a
; fall through
;----------------------------------------------------------------------
; _opl3_fill_range — fill E consecutive regs starting at D with value A
; C = port (0xC4 for Bank0, 0xC6 for Bank1)
; D = start register, E = count, A = value to write
; Destroys: A, B, D, E
;----------------------------------------------------------------------
_opl3_fill_range:
ld b, a ; B = value to write
_opl3_fr_lp:
ld a, d
out (c), a ; write register address
inc c
ld a, b
out (c), a ; write value
dec c
inc d
dec e
jr nz, _opl3_fr_lp
ret
;----------------------------------------------------------------------
; Общая подпрограмма: silence текущего AY чипа. C уже = 0xFD.
; Reg 7 = 0x3F (mixer off), Regs 8-10 = 0 (volume off)
;----------------------------------------------------------------------
_ay_silence_core:
ld b, #0xFF
ld a, #7
out (c), a
ld b, #0xBF
ld a, #0x3F
out (c), a
ld d, #8
_ayc_lp:
ld b, #0xFF
ld a, d
out (c), a
ld b, #0xBF
xor a, a
out (c), a
inc d
ld a, d
cp a, #11
jr c, _ayc_lp
ret
;----------------------------------------------------------------------
; ay_write_reg(uint8_t reg, uint8_t val) — write AY chip 1 register
; reg in A, val on stack
;----------------------------------------------------------------------
.globl _ay_write_reg
_ay_write_reg::
ld c, #0xFD
ld b, #0xFF
out (c), a ; select register
ld hl, #2
add hl, sp
ld a, (hl)
ld b, #0xBF
out (c), a ; write value
ret
;----------------------------------------------------------------------
; ay2_write_reg(uint8_t reg, uint8_t val) — write AY chip 2 register
;----------------------------------------------------------------------
.globl _ay2_write_reg
_ay2_write_reg::
ld c, #0xFD
ld b, #0xFF
push af ; save reg
ld a, #0xFE
out (c), a ; select chip 2
pop af
ld b, #0xFF
out (c), a ; select register
ld hl, #2
add hl, sp
ld a, (hl)
ld b, #0xBF
out (c), a ; write value
ld b, #0xFF
ld a, #0xFF
out (c), a ; switch back to chip 1
ret
;----------------------------------------------------------------------
; saa_write_reg(uint8_t reg, uint8_t val) — SAA1099 chip 1
; addr=#00FF, data=#01FF
;----------------------------------------------------------------------
.globl _saa_write_reg
_saa_write_reg::
ld c, #0xFF
ld b, #0x00
out (c), a ; addr = reg
ld hl, #2
add hl, sp
ld a, (hl)
inc b ; B = 0x01
out (c), a ; data = val
ret
;----------------------------------------------------------------------
; saa2_write_reg(uint8_t reg, uint8_t val) — SAA1099 chip 2
; addr=#02FF, data=#03FF
;----------------------------------------------------------------------
.globl _saa2_write_reg
_saa2_write_reg::
ld c, #0xFF
ld b, #0x02
out (c), a ; addr = reg
ld hl, #2
add hl, sp
ld a, (hl)
inc b ; B = 0x03
out (c), a ; data = val
ret
;----------------------------------------------------------------------
; ay_silence() — AY chip 1 (FFFD/BFFD)
;----------------------------------------------------------------------
.globl _ay_silence
_ay_silence::
ld c, #0xFD
jr _ay_silence_core
;----------------------------------------------------------------------
; ay2_silence() — TurboSound chip 2: select → silence → switch back
;----------------------------------------------------------------------
.globl _ay2_silence
_ay2_silence::
ld c, #0xFD
ld b, #0xFF
ld a, #0xFE
out (c), a ; select chip 2
call _ay_silence_core
ld b, #0xFF
ld a, #0xFF
out (c), a ; switch back to chip 1
ret
;----------------------------------------------------------------------
; Общая подпрограмма: silence SAA чипа. C=0xFF, B=addr port hi.
; Data port = B+1 (inc b / dec b).
; Reset (0x1C=02), disable (0x1C=00), amplitude regs 0x00-0x05=0.
;----------------------------------------------------------------------
_saa_silence_core:
ld a, #0x1C
out (c), a ; addr
inc b
ld a, #0x02
out (c), a ; data: reset
dec b
ld a, #0x1C
out (c), a ; addr
inc b
xor a, a
out (c), a ; data: sound off
dec b
ld d, #0
_saac_lp:
ld a, d
out (c), a ; addr: reg N
inc b
xor a, a
out (c), a ; data: 0
dec b
inc d
ld a, d
cp a, #6
jr c, _saac_lp
ret
;----------------------------------------------------------------------
; saa_silence() — SAA1099 chip 1 (addr=#00FF, data=#01FF)
;----------------------------------------------------------------------
.globl _saa_silence
_saa_silence::
ld c, #0xFF
ld b, #0x00
jr _saa_silence_core
;----------------------------------------------------------------------
; saa2_silence() — SAA1099 chip 2 (addr=#02FF, data=#03FF)
;----------------------------------------------------------------------
.globl _saa2_silence
_saa2_silence::
ld c, #0xFF
ld b, #0x02
jr _saa_silence_core