mirror of https://github.com/zxdos/zxuno.git
sdk: added `ay.def`, `filestc.def`, `playstc.inc`
This commit is contained in:
parent
d5e10bde8d
commit
76693de85a
|
@ -0,0 +1,20 @@
|
|||
; ay.def - definitions for AY-3-8910/8912/8913 and compatible PSG.
|
||||
;
|
||||
; SPDX-FileCopyrightText: 2021 Ivan Tatarinov <ivan-tat@ya.ru>
|
||||
;
|
||||
; SPDX-License-Identifier: Unlicense
|
||||
|
||||
; Compatible compilers:
|
||||
; SJAsmPlus, <https://github.com/sjasmplus/sjasmplus/>
|
||||
|
||||
ifndef ay_def_included
|
||||
define ay_def_included
|
||||
|
||||
ay_freq_Spectrum: equ 1773400
|
||||
ay_freq_Pentagon: equ 1750000
|
||||
ay_freq_AmstradCPC: equ 1000000
|
||||
|
||||
ay_ctrl_port: equ $fffd
|
||||
ay_data_port: equ $bffd
|
||||
|
||||
endif ; !ay_def_included
|
|
@ -0,0 +1,62 @@
|
|||
; filestc.def - Sound Tracker .STC compiled file structure.
|
||||
;
|
||||
; Authors:
|
||||
; 2021 Ivan Tatarinov <ivan-tat@ya.ru>
|
||||
;
|
||||
; This is free and unencumbered software released into the public domain.
|
||||
; For more information, please refer to <http://unlicense.org>.
|
||||
;
|
||||
; SPDX-FileCopyrightText: 2021 Ivan Tatarinov <ivan-tat@ya.ru>
|
||||
;
|
||||
; SPDX-License-Identifier: Unlicense
|
||||
|
||||
; Compatible compilers:
|
||||
; SJAsmPlus, <https://github.com/sjasmplus/sjasmplus/>
|
||||
|
||||
ifndef filestc_def_included
|
||||
define filestc_def_included
|
||||
|
||||
struct ST_Header ; 27 bytes
|
||||
bDelay ds 1
|
||||
wPositionsOff ds 2
|
||||
wOrnamentsOff ds 2
|
||||
wPatternsOff ds 2
|
||||
sIdentifier ds 18 ; "SONG BY ST COMPILE" by default
|
||||
wSize ds 2 ; not reliable
|
||||
rSamples ds 0 ; ST_Sample * SamplesCnt
|
||||
; Data ds ?
|
||||
ends
|
||||
|
||||
struct ST_Positions
|
||||
bCount ds 1
|
||||
rPositions ds 0 ; ST_PositionRec * bCount
|
||||
ends
|
||||
|
||||
struct ST_PositionRec
|
||||
bPNum ds 1
|
||||
bTransposition ds 1
|
||||
ends
|
||||
|
||||
ST_SampleLength: equ 32 ; power of 2 !
|
||||
ST_SamplePosMask: equ (ST_SampleLength - 1)
|
||||
|
||||
struct ST_Sample ; 99 bytes
|
||||
bNumber ds 1
|
||||
rData ds ST_SampleLength * 3
|
||||
bRepeatPosition ds 1
|
||||
bRepeatLength ds 1
|
||||
ends
|
||||
|
||||
struct ST_Ornament ; 33 bytes
|
||||
bNumber ds 1
|
||||
rData ds ST_SampleLength
|
||||
ends
|
||||
|
||||
struct ST_Pattern ; 7 bytes
|
||||
bNumber ds 1
|
||||
wChannelAOff ds 2
|
||||
wChannelBOff ds 2
|
||||
wChannelCOff ds 2
|
||||
ends
|
||||
|
||||
endif ; !filestc_def_included
|
|
@ -0,0 +1,660 @@
|
|||
; playstc.inc - play Sound Tracker .STC music file on AY-3-891x sound chip.
|
||||
;
|
||||
; Contributor of a binary file:
|
||||
; 2016 Michal B. (a.k.a. Yerzmyey)
|
||||
;
|
||||
; Reverse-engineer:
|
||||
; 2021 Ivan Tatarinov <ivan-tat@ya.ru>
|
||||
;
|
||||
; This is free and unencumbered software released into the public domain.
|
||||
; For more information, please refer to <http://unlicense.org>.
|
||||
;
|
||||
; SPDX-FileCopyrightText: 2021 Ivan Tatarinov <ivan-tat@ya.ru>
|
||||
;
|
||||
; SPDX-FileContributor: 2016 Michal B. (a.k.a. Yerzmyey) (binary file)
|
||||
;
|
||||
; SPDX-License-Identifier: Unlicense
|
||||
|
||||
; Compatible compilers:
|
||||
; SJAsmPlus, <https://github.com/sjasmplus/sjasmplus/>
|
||||
|
||||
module PlaySTC
|
||||
|
||||
include filestc.def
|
||||
include ay.def
|
||||
|
||||
if PLAYSTC_USER_DEFINED_FILE != 1
|
||||
|
||||
; Clear all values for better compression of the final compiled binary file
|
||||
|
||||
STCDelay: equ 0
|
||||
STCDelayCtr: equ 0
|
||||
STCSamplesCnt: equ 0
|
||||
STCPositionsCnt: equ 0
|
||||
STCHeader: equ 0
|
||||
STCPositions: equ 0 - ST_Positions.rPositions
|
||||
STCOrnaments: equ 0
|
||||
STCPatterns: equ 0
|
||||
STCSamples: equ 0
|
||||
STCChannelA: equ 0
|
||||
STCChannelB: equ 0
|
||||
STCChannelC: equ 0
|
||||
|
||||
; internal values
|
||||
_STCOrnamentData: equ 0
|
||||
_STCRepeatLen: equ 0
|
||||
|
||||
else ; PLAYSTC_USER_DEFINED_FILE == 1
|
||||
|
||||
; internal values
|
||||
_STCOrnamentData: equ STCOrnaments + ST_Ornament.rData
|
||||
_STCRepeatLen: equ -1
|
||||
|
||||
endif ; PLAYSTC_USER_DEFINED_FILE == 1
|
||||
|
||||
; Constants
|
||||
|
||||
NOTE_FREQ = 55 ; A note (440 Hz down 3 octaves)
|
||||
|
||||
; Here "numerator" is 1000 * 2 ^ (halftone / 12)
|
||||
macro _PutPeriod numerator, octave
|
||||
dw ((((PLAYSTC_AY_FREQUENCY / 16 * numerator + 1000 / 2) / 1000 + NOTE_FREQ / 2) / NOTE_FREQ) >> octave) - 1
|
||||
endm
|
||||
|
||||
NotePeriods:
|
||||
i = 0
|
||||
while i < 8
|
||||
; Original, but hardcoded and inaccurate:
|
||||
; dw 3832>>i, 3600>>i, 3424>>i, 3200>>i, 3032>>i, 2856>>i, 2696>>i, 2544>>i, 2400>>i, 2272>>i, 2136>>i, 2016>>i
|
||||
; _PutPeriod 2000, i ; A
|
||||
_PutPeriod 1888, i ; A#
|
||||
_PutPeriod 1782, i ; B
|
||||
_PutPeriod 1682, i ; C
|
||||
_PutPeriod 1587, i ; C#
|
||||
_PutPeriod 1498, i ; D
|
||||
_PutPeriod 1414, i ; D#
|
||||
_PutPeriod 1335, i ; E
|
||||
_PutPeriod 1260, i ; F
|
||||
_PutPeriod 1189, i ; F#
|
||||
_PutPeriod 1122, i ; G
|
||||
_PutPeriod 1059, i ; G#
|
||||
_PutPeriod 1000, i ; A
|
||||
; _PutPeriod 944, i ; A#
|
||||
; _PutPeriod 891, i ; B
|
||||
|
||||
i = i + 1
|
||||
endw
|
||||
|
||||
; Variables
|
||||
|
||||
p_rPositions: dw STCPositions + ST_Positions.rPositions
|
||||
p_Ornaments: dw STCOrnaments
|
||||
p_Patterns: dw STCPatterns
|
||||
p_Samples: dw STCSamples
|
||||
b_Delay: db STCDelay
|
||||
b_DelayCtr: db 1 ; 1 = last tick
|
||||
b_PositionsCnt: db STCPositionsCnt
|
||||
p_ChannelA: dw STCChannelA
|
||||
p_ChannelB: dw STCChannelB
|
||||
p_ChannelC: dw STCChannelC
|
||||
b_EmptyChannel: db $FF ; $FF = pattern end mark
|
||||
|
||||
struct _ST_r0
|
||||
bSampleFlag db 0 ; 0=ornament, 1=envelope, 2=?
|
||||
bNoteDelay db 0
|
||||
ends
|
||||
|
||||
struct _ST_r1
|
||||
bSamplePos db 0
|
||||
bNote db 0
|
||||
bDelayCtr db 0
|
||||
pSampleData dw 0
|
||||
pOrnamentData dw _STCOrnamentData
|
||||
bRepeatLen db _STCRepeatLen ; =-1 if no loop
|
||||
ends
|
||||
|
||||
struct _ST_Chn
|
||||
r0 _ST_r0
|
||||
r1 _ST_r1
|
||||
ends
|
||||
|
||||
_PlayState: equ $
|
||||
r_ChnA: _ST_Chn
|
||||
r_ChnB: _ST_Chn
|
||||
r_ChnC: _ST_Chn
|
||||
b_CurPos: db 0 ; 0-31
|
||||
; AY-3-891x registers
|
||||
w_AYPitchA: dw 0 ; R0: Channel A fine pitch (0-255), R1: Channel A coarse pitch (0-15) - 0-4095
|
||||
w_AYPitchB: dw 0 ; R2: Channel B fine pitch (0-255), R3: Channel B coarse pitch (0-15) - 0-4095
|
||||
w_AYPitchC: dw 0 ; R4: Channel C fine pitch (0-255), R5: Channel C coarse pitch (0-15) - 0-4095
|
||||
b_AYNoisePitch: db 0 ; R6: Noise pitch (0-31)
|
||||
b_AYMixer: db 0 ; R7: Mixer [ - - N N N T T T ]
|
||||
b_AYVolumeA: db 0 ; R8: Channel A volume (0-15) and mode flag (bit 4)
|
||||
b_AYVolumeB: db 0 ; R9: Channel B volume (0-15) and mode flag (bit 4)
|
||||
b_AYVolumeC: db 0 ; R10: Channel C volume (0-15) and mode flag (bit 4)
|
||||
w_AYEnvPeriod: dw 0 ; R11: Envelope fine duration (0-255), R12: Envelope coarse duration (0-255)
|
||||
b_AYEnvShape: db 0 ; R13: Envelope shape (0-15)
|
||||
_PlayStateLen: equ $ - _PlayState
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
; In: HL = pointer to STC file data
|
||||
Init:
|
||||
di ; HL = Header
|
||||
ld (p_Header), hl
|
||||
ld a, (hl) ; A = Header->bDelay
|
||||
ld (b_Delay), a
|
||||
inc hl ; HL = & Header->wPositionsOff
|
||||
call ReadPtr ; HL = & Header->wOrnamentsOff
|
||||
; DE = Positions
|
||||
ld a, (de) ; A = Positions->bCount
|
||||
inc de ; DE = & Positions->rPositions
|
||||
inc a ; A = Positions->bCount + 1
|
||||
ld (b_PositionsCnt), a
|
||||
ld (p_rPositions), de
|
||||
call ReadPtr ; HL = & Header->wPatternsOff
|
||||
; DE = Ornaments
|
||||
ld (p_Ornaments), de
|
||||
push de ; push Ornaments
|
||||
call ReadPtr ; HL = & Header->sIdentifier
|
||||
; DE = Patterns
|
||||
ld (p_Patterns), de
|
||||
ld hl, ST_Header.rSamples
|
||||
call GetDataPtr
|
||||
ex de, hl ; HL = & Header->rSamples
|
||||
; DE = Patterns
|
||||
ld (p_Samples), hl
|
||||
ld hl, b_EmptyChannel
|
||||
ld (p_ChannelA), hl
|
||||
ld hl, _PlayState
|
||||
ld de, _PlayState + 1
|
||||
ld bc, _PlayStateLen - 1 ; B = 0
|
||||
ld (hl), b
|
||||
ldir ; clear variables in _PlayState area
|
||||
pop hl ; HL = Ornaments
|
||||
ld bc, ST_Ornament
|
||||
xor a ; A = 0
|
||||
call FindItem ; HL = & Ornaments[.bNumber==0].bNumber
|
||||
dec a ; A = -1
|
||||
ld (r_ChnA.r1.bRepeatLen), a
|
||||
ld (r_ChnB.r1.bRepeatLen), a
|
||||
ld (r_ChnC.r1.bRepeatLen), a
|
||||
ld a, 1 ; 1 = last tick
|
||||
ld (b_DelayCtr), a
|
||||
inc hl ; HL = & Ornaments[.bNumber==0].rData
|
||||
ld (r_ChnA.r1.pOrnamentData), hl
|
||||
ld (r_ChnB.r1.pOrnamentData), hl
|
||||
ld (r_ChnC.r1.pOrnamentData), hl
|
||||
call WriteToAY ; Reset AY
|
||||
ei
|
||||
ret
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
; In: A = number
|
||||
; HL = pointer to a byte
|
||||
; BC = pointer's increment
|
||||
; Out: HL = found pointer
|
||||
FindItem:
|
||||
cp (hl)
|
||||
ret z
|
||||
add hl, bc
|
||||
jp FindItem
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
; In: HL = pointer to a 16-bit offset in file
|
||||
; Out: HL = initial HL + 2
|
||||
; DE = pointer to data
|
||||
ReadPtr:
|
||||
ld e, (hl)
|
||||
inc hl
|
||||
ld d, (hl)
|
||||
inc hl
|
||||
ex de, hl ; HL = (HL)
|
||||
; DE = initial HL + 2
|
||||
; jp GetDataPtr ; no need, it follows
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
; In: HL = relative offset in ST file
|
||||
; Out: HL = initial DE value
|
||||
; DE = pointer to data
|
||||
GetDataPtr:
|
||||
ld bc, STCHeader
|
||||
p_Header: equ $ - 2
|
||||
add hl, bc
|
||||
ex de, hl
|
||||
ret
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
; In: A = position in sample (0-31)
|
||||
; IX = pointer to sample's data
|
||||
; Out: B = $02 * NoToneFlag
|
||||
; C = $10 * NoNoiseFlag
|
||||
; H = noise value (0-31)
|
||||
; L = volume value (0-15)
|
||||
; DE = tone shift value (0-$FFF) + $1000 * DoToneShiftDownFlag
|
||||
ReadSample:
|
||||
ld d, 0 ; D = 0
|
||||
ld e, a ; DE = A = a
|
||||
add a, a ; A = a*2
|
||||
add a, e ; A = a*3 (0-93)
|
||||
ld e, a ; DE = a*3
|
||||
add ix, de ; IX = & Sample->rData[a]
|
||||
; Byte 0: bits 7-4: Tone shift value (bits 11-8)
|
||||
; bits 3-0: Volume (0-15)
|
||||
; Byte 1: bit 7: Noise mask (0=noise on, 1=noise off)
|
||||
; bit 6: Tone mask (0=tone on, 1=tone off)
|
||||
; bit 5: Tone shift direction (0=minus to freq., 1=plus to freq.)
|
||||
; bits 4-0: Noise value (0-31)
|
||||
; Byte 2: Tone shift (LSB)
|
||||
ld a, (ix + 1) ; A = Byte_1
|
||||
bit 7, a ; Noise mask (0=noise on, 1=noise off)
|
||||
ld c, $10 ; C = $10 (no noise flag)
|
||||
jp nz, .LNoNoise
|
||||
ld c, d ; C = 0 (noise enabled)
|
||||
.LNoNoise:
|
||||
bit 6, a ; Tone mask (0=tone on, 1=tone off)
|
||||
ld b, $02 ; B = $02 (no tone flag)
|
||||
jp nz, .LNoTone
|
||||
ld b, d ; B = 0 (tone enabled)
|
||||
.LNoTone:
|
||||
and %00011111 ; Noise value (0-31)
|
||||
ld h, a
|
||||
ld e, (ix + 2) ; E = Tone shift (LSB)
|
||||
ld a, (ix + 0) ; A = Byte_0
|
||||
push af
|
||||
and %11110000 ; A = Byte_0 & $F0
|
||||
.4 rrca ; A = Tone shift value (MSB)
|
||||
ld d, a ; DE = Tone shift value
|
||||
pop af
|
||||
and %00001111 ; A = Volume (0-15)
|
||||
ld l, a ; L = Volume (0-15)
|
||||
bit 5, (ix + 1) ; Tone shift direction (0=minus to freq., 1=plus to freq.)
|
||||
ret z
|
||||
set 4, d ; DE |= $1000 (Tone shift direction)
|
||||
ret
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
NextPattern:
|
||||
ld a, (b_CurPos)
|
||||
ld c, a ; C = b_CurPos
|
||||
ld hl, b_PositionsCnt
|
||||
cp (hl)
|
||||
jp c, .LOk ; if (b_CurPos >= b_PositionsCnt) restart
|
||||
xor a ; position is not available, restart
|
||||
ld c, a ; C = 0
|
||||
.LOk: inc a
|
||||
ld (b_CurPos), a
|
||||
ld l, c
|
||||
ld h, 0
|
||||
add hl, hl ; HL = bOldPosition * ST_PositionRec
|
||||
ld de, (p_rPositions)
|
||||
add hl, de ; HL = p_rPositions[bOldPosition]
|
||||
ld c, (hl) ; C = p_rPositions[bOldPosition].bPNum
|
||||
inc hl
|
||||
ld a, (hl) ; A = p_rPositions[bOldPosition].bTransposition
|
||||
ld (CalcPitch.bTransposition), a
|
||||
ld a, c ; A = p_rPositions[bOldPosition].bPNum
|
||||
ld hl, (p_Patterns)
|
||||
ld bc, ST_Pattern
|
||||
call FindItem
|
||||
inc hl ; HL = & p_Patterns[A].wChannelAOff
|
||||
call ReadPtr
|
||||
ld (p_ChannelA), de
|
||||
call ReadPtr
|
||||
ld (p_ChannelB), de
|
||||
call ReadPtr
|
||||
ld (p_ChannelC), de
|
||||
ret
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
; In: IX = pointer to _ST_Chn.r1 structure
|
||||
; Out: Flag S=1 if counter was restarted
|
||||
NextTick:
|
||||
dec (ix + _ST_r1.bDelayCtr)
|
||||
ret p
|
||||
ld a, (ix - _ST_r0 + _ST_r0.bNoteDelay)
|
||||
ld (ix + _ST_r1.bDelayCtr), a
|
||||
ret
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
Play:
|
||||
ld a, (b_DelayCtr)
|
||||
dec a
|
||||
ld (b_DelayCtr), a
|
||||
jp nz, PlayChannels
|
||||
ld a, (b_Delay)
|
||||
ld (b_DelayCtr), a
|
||||
ld ix, r_ChnA.r1
|
||||
call NextTick
|
||||
jp p, .LContinueB
|
||||
ld hl, (p_ChannelA)
|
||||
ld a, (hl) ; A = event
|
||||
inc a
|
||||
call z, NextPattern ; next pattern if pattern end mark $FF found
|
||||
ld hl, (p_ChannelA)
|
||||
call NextRow
|
||||
ld (p_ChannelA), hl
|
||||
.LContinueB:
|
||||
ld ix, r_ChnB.r1
|
||||
call NextTick
|
||||
jp p, .LContinueC
|
||||
ld hl, (p_ChannelB)
|
||||
call NextRow
|
||||
ld (p_ChannelB), hl
|
||||
.LContinueC:
|
||||
ld ix, r_ChnC.r1
|
||||
call NextTick
|
||||
jp p, PlayChannels
|
||||
ld hl, (p_ChannelC)
|
||||
call NextRow
|
||||
ld (p_ChannelC), hl
|
||||
jp PlayChannels
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
; In: HL = pointer to pattern data
|
||||
; IX = pointer to _ST_Chn.r1 structure
|
||||
NextRow:
|
||||
ld a, (hl) ; read event
|
||||
cp $60
|
||||
jp c, .LNote ; $00-$5F: Note number (0-95), end position
|
||||
cp $70
|
||||
jp c, .LSample ; $60-$6F: Sample number (0-15)
|
||||
cp $80
|
||||
jp c, .LOrnament ; $70-$7F: Ornament number (0-15)
|
||||
jp z, .LPause ; $80: Pause, end position, channel off
|
||||
cp $81
|
||||
jp z, .LEmptyNote ; $81: Empty note, end position
|
||||
cp $82
|
||||
jp z, .LNoEnvAndOrn; $82: Disable envelope and ornament
|
||||
cp $8F
|
||||
jp c, .LEnvelope ; $83-$8E: Envelope number (3-14), read envelope's period LSB, disable ornament
|
||||
; $8F-$A1: Nothing, never used in pattern
|
||||
sub $A1 ; $A2-$FE: Delay (1-93)
|
||||
ld (ix + _ST_r1.bDelayCtr), a
|
||||
ld (ix - _ST_r0 + _ST_r0.bNoteDelay), a
|
||||
inc hl
|
||||
jp NextRow
|
||||
|
||||
.LNote:
|
||||
; $00-$5F: Note number (0-95), end position
|
||||
ld (ix + _ST_r1.bNote), a
|
||||
ld (ix + _ST_r1.bSamplePos), 0
|
||||
ld (ix + _ST_r1.bRepeatLen), ST_SampleLength
|
||||
.LEmptyNote:
|
||||
; $81: Empty note, end position
|
||||
inc hl
|
||||
ret
|
||||
|
||||
.LSample:
|
||||
; $60-$6F: Sample number (0-15)
|
||||
sub $60
|
||||
push hl
|
||||
ld bc, ST_Sample
|
||||
ld hl, (p_Samples)
|
||||
call FindItem
|
||||
; HL = Samples[A]
|
||||
inc hl ; HL = & Samples[A].rData
|
||||
ld (ix + _ST_r1.pSampleData), l
|
||||
ld (ix + _ST_r1.pSampleData + 1), h
|
||||
pop hl
|
||||
inc hl
|
||||
jp NextRow
|
||||
|
||||
.LPause:
|
||||
; $80: Pause, end position, channel off
|
||||
inc hl
|
||||
.LClrRepLen:
|
||||
ld (ix + _ST_r1.bRepeatLen), -1
|
||||
ret
|
||||
|
||||
.LNoEnvAndOrn:
|
||||
; $82: Disable envelope and ornament
|
||||
xor a
|
||||
jr .LSet
|
||||
|
||||
.LOrnament:
|
||||
; $70-$7F: Ornament number (0-15)
|
||||
sub $70
|
||||
.LSet: push hl
|
||||
ld bc, ST_Ornament
|
||||
ld hl, (p_Ornaments)
|
||||
call FindItem
|
||||
inc hl
|
||||
ld (ix + _ST_r1.pOrnamentData), l
|
||||
ld (ix + _ST_r1.pOrnamentData + 1), h
|
||||
ld (ix - _ST_r0 + _ST_r0.bSampleFlag), 0
|
||||
pop hl
|
||||
inc hl
|
||||
jp NextRow
|
||||
|
||||
.LEnvelope:
|
||||
; $83-$8E: Envelope number (3-14), read envelope's period LSB, disable ornament
|
||||
sub $80
|
||||
ld (b_AYEnvShape), a
|
||||
inc hl
|
||||
ld a, (hl) ; A = envelope's period LSB
|
||||
inc hl
|
||||
ld (w_AYEnvPeriod), a
|
||||
ld (ix - _ST_r0 + _ST_r0.bSampleFlag), 1
|
||||
push hl
|
||||
xor a
|
||||
ld bc, ST_Ornament
|
||||
ld hl, (p_Ornaments)
|
||||
call FindItem
|
||||
inc hl
|
||||
ld (ix + _ST_r1.pOrnamentData), l
|
||||
ld (ix + _ST_r1.pOrnamentData + 1), h
|
||||
pop hl
|
||||
jp NextRow
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
; In: IX = pointer to _ST_Chn.r1 structure
|
||||
; Out: C = sample position
|
||||
SetupSample:
|
||||
ld a, (ix + _ST_r1.bRepeatLen)
|
||||
inc a
|
||||
ret z ; if (r1->bRepeatLen == -1) return
|
||||
.2 dec a ; A = r1->bRepeatLen - 1
|
||||
ld (ix + _ST_r1.bRepeatLen), a
|
||||
; r1->bRepeatLen = r1->bRepeatLen - 1
|
||||
push af
|
||||
ld a, (ix + _ST_r1.bSamplePos)
|
||||
ld c, a ; C = r1->bSamplePos
|
||||
inc a
|
||||
and ST_SamplePosMask
|
||||
ld (ix + _ST_r1.bSamplePos), a
|
||||
; r1->bSamplePos = (r1->bSamplePos + 1) & ST_SamplePosMask
|
||||
pop af ; A = r1->bRepeatLen - 1
|
||||
ret nz ; if (r1->bRepeatLen != 1) return
|
||||
ld e, (ix + _ST_r1.pSampleData)
|
||||
ld d, (ix + _ST_r1.pSampleData + 1)
|
||||
ld hl, ST_Sample.bRepeatPosition - ST_Sample.rData
|
||||
add hl, de ; HL = & Sample->bRepeatPosition
|
||||
ld a, (hl)
|
||||
dec a ; A = Sample->bRepeatPosition - 1
|
||||
jp m, NextRow.LClrRepLen ; if (Sample->bRepeatPosition <= 0) reset
|
||||
ld c, a ; C = Sample->bRepeatPosition - 1
|
||||
inc a
|
||||
and ST_SamplePosMask
|
||||
ld (ix + _ST_r1.bSamplePos), a
|
||||
; r1->bSamplePos = Sample->bRepeatPosition & ST_SamplePosMask
|
||||
inc hl ; HL = & Sample->bRepeatLength
|
||||
ld a, (hl)
|
||||
inc a
|
||||
ld (ix + _ST_r1.bRepeatLen), a
|
||||
; r1->bRepeatLen = Sample->bRepeatLength + 1
|
||||
ret
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
; In: C = 0 for noise (no noise otherwise)
|
||||
; H = noise pitch (0-31)
|
||||
SetupNoise:
|
||||
ld a, c
|
||||
or a
|
||||
ret nz
|
||||
ld a, h
|
||||
ld (b_AYNoisePitch), a
|
||||
ret
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
; In: IX = pointer to _ST_Chn.r1 structure
|
||||
; HL = pointer to AY channel volume register's data (R8/R9/R10)
|
||||
SetupEnvShape:
|
||||
ld a, (ix + _ST_r1.bRepeatLen)
|
||||
inc a
|
||||
ret z ; if (r1->bRepeatLen == -1) return
|
||||
ld a, (ix - _ST_r0 + _ST_r0.bSampleFlag)
|
||||
or a
|
||||
ret z ; if (r0->bSampleFlag == 0) return
|
||||
cp 2
|
||||
jp z, .LReset ; if (r0->bSampleFlag == 2) b_AYEnvShape = 0
|
||||
ld (ix - _ST_r0 + _ST_r0.bSampleFlag), 2
|
||||
jp .L2 ; if (r0->bSampleFlag == 1) r0->bSampleFlag = 2
|
||||
.LReset:xor a
|
||||
ld (b_AYEnvShape), a
|
||||
.L2: set 4, (hl) ; volume |= $10 (do not use volume: use envelope)
|
||||
ret
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
; In: IX = pointer to _ST_Chn.r1 structure
|
||||
; L = volume value (0-15)
|
||||
; DE = tone shift value (0-$FFF) + $1000 * DoToneShiftDownFlag
|
||||
; Out: A = volume (0-15)
|
||||
; HL = pitch
|
||||
CalcPitch:
|
||||
ld a, l
|
||||
push af
|
||||
push de
|
||||
ld l, (ix + _ST_r1.pOrnamentData)
|
||||
ld h, (ix + _ST_r1.pOrnamentData + 1)
|
||||
ld de, 0
|
||||
.wCurPos: equ $ - 2
|
||||
add hl, de ; HL = & OrnamentData[wCurPos]
|
||||
ld a, (ix + _ST_r1.bNote)
|
||||
add a, (hl)
|
||||
add a, 0 ; A = r1->bNote + OrnamentData[wCurPos] + bTransposition
|
||||
.bTransposition: equ $ - 1
|
||||
add a, a ; A = (r1->bNote + OrnamentData[wCurPos] + bTransposition) * 2
|
||||
ld e, a
|
||||
ld d, 0
|
||||
ld hl, NotePeriods
|
||||
add hl, de ; HL = & NotePeriods[r1->bNote + OrnamentData[wCurPos] + bTransposition]
|
||||
ld e, (hl)
|
||||
inc hl
|
||||
ld d, (hl)
|
||||
ex de, hl ; HL = NotePeriods[r1->bNote + OrnamentData[wCurPos] + bTransposition]
|
||||
pop de ; DE = tone shift value (0-$FFF) + $1000 * DoToneShiftDownFlag
|
||||
pop af ; A = volume value (0-15)
|
||||
bit 4, d
|
||||
jr z, .LShiftUp
|
||||
res 4, d
|
||||
add hl, de
|
||||
ret
|
||||
.LShiftUp:
|
||||
and a
|
||||
sbc hl, de
|
||||
ret
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
PlayChannels:
|
||||
; Play channel A
|
||||
ld ix, r_ChnA.r1
|
||||
call SetupSample
|
||||
ld a, c
|
||||
ld (CalcPitch.wCurPos), a
|
||||
ld ix, (r_ChnA.r1.pSampleData)
|
||||
call ReadSample
|
||||
ld a, c
|
||||
or b
|
||||
rrca ; A = $08 * NoNoiseFlag | $01 * NoToneFlag
|
||||
ld (b_AYMixer), a
|
||||
ld ix, r_ChnA.r1
|
||||
ld a, (ix + _ST_r1.bRepeatLen)
|
||||
inc a
|
||||
jp z, .L1 ; if (r1->bRepeatLen == -1) skip
|
||||
call SetupNoise
|
||||
call CalcPitch
|
||||
ld (w_AYPitchA), hl
|
||||
.L1: ld hl, b_AYVolumeA
|
||||
ld (hl), a
|
||||
call SetupEnvShape
|
||||
; Play channel B
|
||||
ld ix, r_ChnB.r1
|
||||
call SetupSample
|
||||
ld a, (ix + _ST_r1.bRepeatLen)
|
||||
inc a
|
||||
jp z, .L2 ; if (r1->bRepeatLen == -1) skip
|
||||
ld a, c
|
||||
ld (CalcPitch.wCurPos), a
|
||||
ld ix, (r_ChnB.r1.pSampleData)
|
||||
call ReadSample
|
||||
ld a, (b_AYMixer)
|
||||
or c
|
||||
or b ; A |= $10 * NoNoiseFlag | $02 * NoToneFlag
|
||||
ld (b_AYMixer), a
|
||||
call SetupNoise
|
||||
ld ix, r_ChnB.r1
|
||||
call CalcPitch
|
||||
ld (w_AYPitchB), hl
|
||||
.L2: ld hl, b_AYVolumeB
|
||||
ld (hl), a
|
||||
call SetupEnvShape
|
||||
; Play channel C
|
||||
ld ix, r_ChnC.r1
|
||||
call SetupSample
|
||||
ld a, (ix + _ST_r1.bRepeatLen)
|
||||
inc a
|
||||
jp z, .L3 ; if (r1->bRepeatLen == -1) skip
|
||||
ld a, c
|
||||
ld (CalcPitch.wCurPos), a
|
||||
ld ix, (r_ChnC.r1.pSampleData)
|
||||
call ReadSample
|
||||
ld a, (b_AYMixer)
|
||||
rlc c
|
||||
rlc b
|
||||
or b
|
||||
or c ; A |= $20 * NoNoiseFlag | $04 * NoToneFlag
|
||||
ld (b_AYMixer), a
|
||||
call SetupNoise
|
||||
ld ix, r_ChnC.r1
|
||||
call CalcPitch
|
||||
ld (w_AYPitchC), hl
|
||||
.L3: ld hl, b_AYVolumeC
|
||||
ld (hl), a
|
||||
call SetupEnvShape
|
||||
; Output sound
|
||||
; jp WriteToAY ; no need, it follows
|
||||
|
||||
;-----------------------------------------------------------------------------
|
||||
; Subroutine
|
||||
WriteToAY:
|
||||
ld hl, b_AYEnvShape; start from register 13
|
||||
xor a
|
||||
or (hl)
|
||||
ld a, 13 ; register number
|
||||
jr nz, .LNot0 ; value == 0 ?
|
||||
sub 3 ; skip values and start from register 10
|
||||
.3 dec hl
|
||||
.LNot0: ld c, ay_ctrl_port & $FF
|
||||
.LLoop: ld b, ay_ctrl_port >> 8
|
||||
out (c), a
|
||||
ld b, ay_data_port >> 8 ; LSB address is the same
|
||||
outd
|
||||
dec a
|
||||
jp p, .LLoop
|
||||
ret
|
||||
|
||||
endmodule
|
Loading…
Reference in New Issue