Leírás az exolon.s-ben található Spectrum 128-as AY emulátor kódhoz (2. rész):
A burkológörbe emuláció 1 kHz-es megszakításból történik. Ennek a belépési pontja 0038h címen van:
phase 0038h
push af
in a, (0b4h)
and 02h
jp nz, envelopeInterrupt
ld a, 31h
out (0b4h), a
ei
jp 0000h
dephase
Itt a B4h port 1. bitje alapján dönthető el, hogy 1 kHz-es megszakítás történt-e. Ha igen, akkor azonnal ugrik a burkológörbe emulátor rutinra. Egyébként törli a video megszakítást (5. bit), engedélyezi a video (4. bit) és 1 kHz-es (0. bit) megszakítást, majd a játék megszakítási rutinjára ugrik (a szinte biztosan az elején található PUSH AF utasítást átlépve). Előbb azonban engedélyezi a megszakításokat, hogy az esetleg lassú rutin futása közben is tovább működhessen a burkológörbe emuláció. A JP utasításhoz természetesen az adott játéktól függő címet kell írni a 0000h helyére.
Következik a burkológörbe emulátor rutin, amely így kezdődik:
envelopeInterrupt:
ld a, 13h
out (0b4h), a
if ENV_SRATE_DIV > 1
.lsrd1: ld a, 0 ; *
add a, (256 + (ENV_SRATE_DIV / 2)) / ENV_SRATE_DIV
ld (.lsrd1 + 1), a
jr c, .lsrd2
pop af
ei
ret
.lsrd2:
endif
Törli az 1 kHz-es megszakítást (1. bit), és engedélyezi a video (4. bit) és 1 kHz-es (0. bit) megszakítást. Lehetőség van az 1 kHz-es megszakítás leosztására is, amellyel CPU idő takarítható meg rosszabb minőség árán.
push bc
push hl
.l1: ld hl, 0000h ; * envelope counter
ld bc, 010000h - (((110840 * ENV_SRATE_DIV) + 500) / 1000)
add hl, bc
jr c, .l16 ; * JR if envelope is stopped
.l2: ld bc, 0ffffh ; * envelope frequency
.l3: ld a, 0 ; * envelope state (0 to 15)
.l4: dec a ; * envelope direction (INC A or DEC A)
cp 10h
.l5: jr nc, .l18 ; * envelope mode
.l6: add hl, bc
jr nc, .l4
ld (.l3 + 1), a
ld (.l1 + 1), hl
or low ayVolumeTable
ld l, a
.l7: ld h, high ayVolumeTable
ld a, (hl)
pop hl
jr .l9 ; * envelope enable mode
.l8: ...
.l16: ld (.l1 + 1), hl
pop hl
pop bc
pop af
ei
ret
Itt az .l1-nél található számláló minden burkológörbe megszakításnál csökken. A 110840 az AY frekvenciák számításánál használt konstans (ami 62500 CPC-n), az 1000 az emuláció "órajele" osztás előtt, az 500 pedig annak a fele kerekítéshez. CPC-nél a 300 Hz-es megszakítást használva ezeket át kell írni 300-ra és 150-re. Ha a számláló nem csordul túl (vagy ha a burkológörbe már lefutott és a JR C utasítás helyére JR került), akkor .l16-nál az új érték egyszerűen tárolódik, és a rutin visszatér.
Túlcsordulás esetén a számláló újratöltésére, és a burkológörbe állapot (0-15) frissítésére kerül sor. Érdemes megfigyelni, hogy nagy burkológörbe frekvenciánál a kód egyszerre többet is tud lépni a burkológörbe állapoton; ezért van a frekvencia korlátozása, hogy ez a ciklus ne lassulhasson le nagyon. Ha a burkológörbe állapot is túlcsordul, akkor a módtól függően ezek történhetnek:
.l17: ld l, low (ayVolumeTable + 15) ; envelope modes 11 and 13
defb 01h ; = LD BC, nnnn
.l18: ld l, low ayVolumeTable ; envelope modes 0 to 7, 9, and 15
ld a, 18h ; = JR +nn
ld (.l2 - 2), a ; stop envelope
jp .l7
.l19: and 0fh ; envelope modes 8 and 12
jp .l6
.l20: ld a, (.l4) ; envelope modes 10 and 14
rrca
jr c, .l21 ; direction was DEC A ?
ld a, 3dh
ld (.l4), a ; direction = DEC A
ld a, 15
jp .l6
.l21: ld a, 3ch
ld (.l4), a ; direction = INC A
xor a
jp .l6
Az új kimenetnek megfelelően a DAVE hangerő regisztereket is be kell állítani, tehát a táblázatból olvasott konvertált hangerő a 8-9. regiszterekben beállított módtól függően a DAVE A8h-AFh portjaira kerülhet:
.l8: out (0a8h), a ; envelope on channel A only
if ENABLE_STEREO == 0
out (0ach), a
endif
.l9: pop bc
pop af
ei
ret
.l10: out (0a8h), a ; envelope on channels A and B
if ENABLE_STEREO == 0
out (0ach), a
endif
.l11:
if ENABLE_STEREO != 0
ld c, a ; envelope on channel B only
srl c
srl c
sub c
endif
out (0a9h), a
out (0adh), a
pop bc
pop af
ei
ret
.l12: out (0a8h), a ; envelope on channels A and C
if ENABLE_STEREO == 0
out (0ach), a
endif
.l13:
if ENABLE_STEREO == 0
out (0aah), a
endif
out (0aeh), a ; envelope on channel C only
pop bc
pop af
ei
ret
.l14: out (0a8h), a ; envelope on channels A, B, and C
if ENABLE_STEREO == 0
out (0ach), a
endif
.l15:
if ENABLE_STEREO == 0
out (0aah), a
endif
out (0aeh), a ; envelope on channels B and C
if ENABLE_STEREO != 0
ld c, a
srl c
srl c
sub c
endif
out (0a9h), a
out (0adh), a
pop bc
pop af
ei
ret
Végül az AY emuláció inicializálása:
ayReset:
di
ld hl, ayRegisters - 1
ld bc, 10afh
xor a
.l1: inc hl
out (c), a
ld (hl), a
dec c
djnz .l1
res 3, l ; register 7
ld (hl), 3fh
if NO_ENVELOPE_IRQ == 0
ld (envelopeInterrupt.l3 + 1), a
ld a, 18h ; = JR +nn
ld (envelopeInterrupt.l2 - 2), a
ld hl, MIN_ENV_FREQVAL
ld (envelopeInterrupt.l2 + 1), hl
ld a, low (envelopeInterrupt.l9 - envelopeInterrupt.l8)
ld (envelopeInterrupt.l8 - 1), a
ld hl, irqCodeBegin
ld de, 0038h
ld c, low (irqCodeEnd - irqCodeBegin)
ldir
endif
ld a, 0ch
out (0bfh), a
ld a, 10h
out (0a6h), a ; use 17-bit noise generator
if NO_ENVELOPE_IRQ == 0
ld a, 33h
out (0b4h), a ; enable 1 kHz and video interrupts
ei
endif
ret
Itt az A6h portra írt 10h-t érdemes említeni, amely felcseréli a 7 és 17 bites polinom számlálókat. Így a DAVE első három csatornáján az utóbbi használható.
A CPC-s betöltőben az AY emulátor kissé eltér a frekvencia konverzió módosítása és a 300 Hz-es megszakítás használatának lehetősége miatt. Ezen kívül ott van olyan rutin, amely mind a 14 regisztert írja egyszerre egy táblázatból - ez gyorsabb, mint az ayRegisterWrite-ot 14-szer hívni, és a CPC-s programok gyakran használnak ilyen megoldást 50 Hz-es megszakításból.