July 29, 2021, 11:24:17

### Author Topic: Creating ZX SOUND  (Read 3239 times)

#### col

• Hero Member
• Posts: 586
##### Re: Creating ZX SOUND
« Reply #15 on: February 05, 2021, 09:59:51 »
'Sounds' interesting - pardon the pun

Does this snippet of code help you at all?https://skoolkit.ca/disassemblies/manic_miner/asm/37596.html

#### Baggey

• Full Member
• Posts: 135
##### Re: Creating ZX SOUND
« Reply #16 on: February 05, 2021, 16:45:59 »
Ok. Very interesting site. Manic Miner Disassembly! Thankyou Col

Code: [Select]
`37596 LD A,(IY+0) Pick up the next byte of tune data from the table at 33902 "THE START OF OUR DATA STRING"37599 CP 255         Has the tune finished?37601 RET Z         Return (with the zero flag set) if so37602 LD C,A         Copy the first byte of data for this note (which determines the duration) to C37603 LD B,0         Initialise B, which will be used as a delay counter in the note-producing loop37605 XOR A         Set A=0 (for no apparent reasaon)37606 LD D,(IY+1) Pick up the second byte of data for this note37609 LD A,D         Copy it to A37610 CALL 37675 Calculate the attribute file address for the corresponding piano key37613 LD (HL),80         Set the attribute byte for the piano key to 80 (INK 0: PAPER 2: BRIGHT 1)37615 LD E,(IY+2) Pick up the third byte of data for this note37618 LD A,E         Copy it to A37619 CALL 37675 Calculate the attribute file address for the corresponding piano key37622 LD (HL),40         Set the attribute byte for the piano key to 40 (INK 0: PAPER 5: BRIGHT 0)37624 OUT (254),A Produce a sound based on the frequency parameters in the second and third bytes of data for this note (copied into D and E)37626 DEC D37627 JR NZ,3763437629 LD D,(IY+1)37632 XOR 2437634 DEC E37635 JR NZ,3764237637 LD E,(IY+2)37640 XOR 2437642 DJNZ 3762437644 DEC C37645 JR NZ,3762437647 CALL 37687 Check whether ENTER or the fire button is being pressed37650 RET NZ         Return (with the zero flag reset) if it is37651 LD A,(IY+1) Pick up the second byte of data for this note37654 CALL 37675 Calculate the attribute file address for the corresponding piano key37657 LD (HL),56         Set the attribute byte for the piano key back to 56 (INK 0: PAPER 7: BRIGHT 0)37659 LD A,(IY+2) Pick up the third byte of data for this note37662 CALL 37675 Calculate the attribute file address for the corresponding piano key37665 LD (HL),56         Set the attribute byte for the piano key back to 56 (INK 0: PAPER 7: BRIGHT 0)37667 INC IY         Move IY along to the data for the next note in the tune37669 INC IY37671 INC IY37673 JR 37596         Jump back to play the next note`
"The Blue Danube"
Code: [Select]
`80,128,129,80,102,103,80,86,87,50,86,87,50,171,203,50,43,51,50,43,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,96,86,50,171,192,50,43,48,50,43,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,192,50,38,48,50,38,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,203,50,38,51,50,38,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,171,50,32,43,50,32,43,50,128,171,50,43,51,50,43,51,50,128,171,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,152,50,32,38,50,32,38,50,128,152,50,38,48,50,38,48,50,0,0,114,115,50,114,115,50,96,97,50,76,77,50,76,153,50,76,77,50,76,77,50,76,153,50,91,92,50,86,87,50,51,205,50,51,52,50,51,52,50,51,205,50,64,65,50,102,103,100,102,103,50,114,115,100,76,77,50,86,87,50,128,203,25,128,0,25,128,129,50,128,203,255`
Ive read through the Music playing code. And in a nut shell I think, from the Speakers point of view. We are literaly pulsing on and off. Within four loops.

Just looking at the first three bytes that are picked up.

There Are 4 Loops in play.

In a nut shell C=80 first byte and Pitch/Duration ie an outer loop executed 80 times.

B=0 The second loop which will always be executed 256 times and always starts at zero. Next inner loop of which the 2nd and 3rd Bytes are counted down.

The Third Loop which is D=128 (on first run) and the second byte is another timed loop within this.
The Fourth Loop which is E=129 (on first run) and the third byte.

Now the Third byte effects the initial condition of the speaker being on. When it goes through the Call 37675 it will come back with A=240 (on first Run).

This Long sentence may not be right! But I think this could give an out of sync delay of the (on/off). Depending on how different D and E are on First run? This would mean putting the third byte of every three bytes in the call and calculating the A value. On each start of the Note. But i think it could be ignored and assumed to be on unless its a silent note or pause. The human Ear might not hear this mistake as it would be so fast!? If the Third does need to be calculated its Subtracted by 8 then Rotated three bits to the right 3 times looping around on its self. Then Complementing the 1's and 0's. Then Oring with 224. So it might seem it does.

So Bit 5 of A is a 1 so when the OUT(254),240 is used the Piezo is turned on as bit 5 is a 1. So speaker is now on.

Now, D=128 and E=129 they are both decreased by one. When D=0 it will become D=128 again and the speaker turned off! and if E=0 it will become E=129 again and speaker turned off also.  NOTE the XOR 24 does the Speaker and the Mic bits not sure why?

The D and E loop are played exacatly 256 times or until B=0. Which will loop for 256 times again playing D and E. Then Fist Outer Loop decreases C=80 by 1 and does it all over again until C=0. This is the Pitch Loop.

Then we pick up the next three bytes in the TUNE and do the exact same thing all over again untill we hit 255 the tune terminator or start playing whole tune again.

One iteration of this should produce the first note.

Hope ive not made any errors and this makes sence. These loops and Z80 instructions can be done in Blitzmax.
But how the loops relate to timming of Playing through sound channels at sample's per second etc. God only knows

Kind Regards Baggey

« Last Edit: February 05, 2021, 20:00:26 by Baggey »
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

#### Scaremonger

• Full Member
• Posts: 230
##### Re: Creating ZX SOUND
« Reply #17 on: February 06, 2021, 09:53:37 »
@col : Thanks, that is so useful.

@baggy : Your run through looks good. Can't wait to get some free time to work on it.

#### col

• Hero Member
• Posts: 586
##### Re: Creating ZX SOUND
« Reply #18 on: February 06, 2021, 15:18:03 »
If you wanted a truly authentic reproduction of the tune I would think you could do it with some math.

Using the Z80 manual you could get the TState timings for each instruction. Knowing the 3.5Mhz that the Z80 is running at you can work out the on/off rate of the piezo speaker for each frequency.

The Xor 24 is probably to do with a Speccy side effect. As you may (or may not) know the Ear (speaker) and Mic are both accessed through port 254. The value 24 in 8bit binary in 00011000 with the 5th bit (0001) being the speaker and the 4th bit (1000) for the Mic - The side effect is that if you output to both of these at the same time then it increased the volume of the output.

#### Scaremonger

• Full Member
• Posts: 230
##### Re: Creating ZX SOUND
« Reply #19 on: February 06, 2021, 16:38:21 »

Using the Z80 manual you could get the TState timings for each instruction. Knowing the 3.5Mhz that the Z80 is running at you can work out the on/off rate of the piezo speaker for each frequency.
Thanks; I have already looked up the number of cycles for the OUT instruction on this site but hadn't considered the calling routine itself. @Baggey will probably have a seperate method within his emulator.

The value 24 in 8bit binary in 00011000 with the 5th bit (0001) being the speaker and the 4th bit (1000) for the Mic. The side effect is that if you output to both of these at the same time then it increased the volume of the output.

Interesting that the microphone affects the volume.

Bits xxxxx111 appear to control the border color too.

#### Baggey

• Full Member
• Posts: 135
##### Re: Creating ZX SOUND
« Reply #20 on: February 06, 2021, 18:47:38 »

[quoteBits xxxxx111 appear to control the border color too.

The Border color is affected by the first 3 bits. The colour of the border is what's on the keys of the spectrum Keyboard. Incidently xxxxx111=7 is WHITE.

Just had a look over at that site. And he or she has written some of the opcodes differently which is interesting. As ive been thinking of doing this with the 6502. Which was in the "BBC_B". As the litreture for the 6502 is Horrendously documented  There's still alot of Missing opcodes there thou.

I haven't counted all of the Z80 Opcodes In my Emulator. Which i might call "SpecBlitz" or "SpecMax". But there can be upto 1792 instructions  feels like im in the region of at least 1300 right now! I think some of these have never been used as there undocumented.

Good luck with producing some sound. I could have a look at writing a small routine in Blitmax. Just to spit out the value of A for a given value of E if that helps?

Kind Regards Baggey
« Last Edit: February 07, 2021, 07:29:53 by Baggey »
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

#### Scaremonger

• Full Member
• Posts: 230
##### Re: Creating ZX SOUND
« Reply #21 on: February 07, 2021, 00:46:42 »
I could have a look at writing a small routine in Blitmax. Just to spit out the value of A for a give value of E if that helps?

I have done this tonight using the actual ZXSpectrum code converted in-place:

Code: [Select]
`SuperStrict' Manic Miner Audio Output' 16 Bit RegsitersGlobal BC16:ShortGlobal DE16:ShortGlobal HL16:ShortGlobal BC:Byte Ptr = Varptr BC16Global DE:Byte Ptr = Varptr DE16Global HL:Byte Ptr = Varptr HL16Const B:Byte=1, C:Byte=0, D:Byte=1, E:Byte=0, H:Byte=1, L:Byte=0' 8 Bit RegistersGlobal A:ByteGlobal IY:Byte, IX:Byte' FLAGSGlobal F:Byte[8]Const CF:Byte=0Const ZR:Byte=1' Music DataGlobal data:Byte[] = [80,128,129,80,102,103,80,86,87,50,86,87,50,171,203,50,43,51,50,43,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,96,86,50,171,192,50,43,48,50,43,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,192,50,38,48,50,38,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,203,50,38,51,50,38,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,171,50,32,43,50,32,43,50,128,171,50,43,51,50,43,51,50,128,171,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,152,50,32,38,50,32,38,50,128,152,50,38,48,50,38,48,50,0,0,114,115,50,114,115,50,96,97,50,76,77,50,76,153,50,76,77,50,76,77,50,76,153,50,91,92,50,86,87,50,51,205,50,51,52,50,51,52,50,51,205,50,64,65,50,102,103,100,102,103,50,114,115,100,76,77,50,86,87,50,128,203,25,128,0,25,128,129,50,128,203,255]' Direct Code ConversionFunction CALL_37596:Int() While True A = data[IY] '37596 LD A,(IY+0) Pick up the next byte of tune data from the table at 33902 If A=255 '37599 CP 255 Has the tune finished? Return True '37601 RET Z Return (with the zero flag set) if so End If BC[C] = A '37602 LD C,A Copy the first byte of data for this note (which determines the duration) to C BC[B] = 0 '37603 LD B,0 Initialise B, which will be used as a delay counter in the note-producing loop A = 0 '37605 XOR A Set A=0 (for no apparent reasaon) DE[D] = data[IY+1] '37606 LD D,(IY+1) Pick up the second byte of data for this note '##### UPDATE AN ONSCREEN KEYBOARD TO SHOW NOTE 'A = DE[D] '37609 LD A,D Copy it to A 'CALL_37675() '37610 CALL 37675 Calculate the attribute file address for the corresponding piano key 'mem(HL,80) '37613 LD (HL),80 Set the attribute byte for the piano key to 80 (INK 0: PAPER 2: BRIGHT 1) DE[E] = data[IY+2] '37615 LD E,(IY+2) Pick up the third byte of data for this note '##### UPDATE AN ONSCREEN KEYBOARD TO SHOW NOTE 'A = DE[E] '37618 LD A,E Copy it to A 'CALL_37675() '37619 CALL 37675 Calculate the attribute file address for the corresponding piano key 'mem(HL,40) '37622 LD (HL),40 Set the attribute byte for the piano key to 40 (INK 0: PAPER 5: BRIGHT 0)'Print( "A ---"+Right(Hex(A),2)+"  BC-"+Right(Hex(BC16),4)+"  DE-"+Right(Hex(DE16),4)+"  HL-"+Right(Hex(HL16),4) ) Repeat ' until C=0 (at 37645) Repeat ' until B=0 (at 37642) _OUT(254,A) '37624 OUT (254),A Produce a sound based on the frequency parameters in the second and third bytes of data for this note (copied into D and E) DE[D] :- 1 '37626 DEC D If DE[D]=0 '37627 JR NZ,37634 DE[D] = data[IY+1] '37629 LD D,(IY+1) A = A~24 '37632 XOR 24 End If DE[E] :- 1 '37634 DEC E If DE[E]=0 '37635 JR NZ,37642 DE[E] = data[IY+2] '37637 LD E,(IY+2) A = A~24 '37640 XOR 24 End If'Print( "A ---"+Right(Hex(A),2)+"  BC-"+Right(Hex(BC16),4)+"  DE-"+Right(Hex(DE16),4)+"  HL-"+Right(Hex(HL16),4) ) BC[B] :- 1 '37642 DJNZ 37624 Until BC[B]=0 ' ""    "" BC[C] :- 1 '37644 DEC C Until C=0 '37645 JR NZ,37624 '##### KEYBOARD INTERACTION 'If CALL_37687() '37647 CALL 37687 Check whether ENTER or the fire button is being pressed ' Return False '37650 RET NZ Return (with the zero flag reset) if it is 'End If '##### UPDATE AN ONSCREEN KEYBOARD TO SHOW NOTE 'A = data[IY+1] '37651 LD A,(IY+1) Pick up the second byte of data for this note 'CALL_37675() '37654 CALL 37675 Calculate the attribute file address for the corresponding piano key 'mem(HL,56) '37657 LD (HL),56 Set the attribute byte for the piano key back to 56 (INK 0: PAPER 7: BRIGHT 0) '##### UPDATE AN ONSCREEN KEYBOARD TO SHOW NOTE 'A = data[IY+2] '37659 LD A,(IY+2) Pick up the third byte of data for this note 'CALL_37675() '37662 CALL 37675 Calculate the attribute file address for the corresponding piano key 'mem(HL,56) '37665 LD (HL),56 Set the attribute byte for the piano key back to 56 (INK 0: PAPER 7: BRIGHT 0) IY :+ 3 '37667 INC IY Move IY along to the data for the next note in the tune '37669 INC IY '37671 INC IY Wend '37673 JR 37596 Jump back to play the next noteEnd Function' Direct Code Conversion' Calculates which on-screen piano key will be shown pressed' We dont need this, so it has been commented out'Function CALL_37675()' A :- 8 '37675 SUB 8 Compute the piano key index (K) based on the frequency parameter (F), and store it in bits 0-4 of A: K=31-INT((F-8)/8)' _RRCA() '37677 RRCA' _RRCA() '37678 RRCA' _RRCA() '37679 RRCA' A  = ~A '37680 CPL ' A :| 224 '37681 OR 224 A=224+K; this is the LSB' HL[L] = A '37683 LD L,A Set HL to the attribute file address for the piano key' HL[H] = 89 '37684 LD H,89' Return '37686 RET'End Function' Keyboard input' Not required, so has been commented out'Function CALL_37687:Int()' Return False'End Function' Z80 Mnumonic Rotate Bits Right with Carry'Function _RRCA()' Local x:Byte = A & \$01' A = ( A Shr 1 | ( A & \$01 ) Shl 7 )' F[CF] = x'End Function' Z80 MnumonicFunction _OUT( port:Byte, value:Byte) If port=254 ' bitmap of value is xxxSMBBB ' S=Sound M=Microphone B=Border Print( value ) End IfEnd Function' Play MusicIY = 0 ' Set pointer to start of musicCALL_37596()`
Some of the code is not actually needed for the music, for example this piece (and the function it calls) appear to update an onscreen "Piano" to show the keys being played' so you'll find that commented out.
Code: [Select]
`37609 LD A,D         Copy it to A37610 CALL 37675 Calculate the attribute file address for the corresponding piano key37613 LD (HL),80         Set the attribute byte for the piano key to 80 (INK 0: PAPER 2: BRIGHT 1)37618 LD A,E         Copy it to A37619 CALL 37675 Calculate the attribute file address for the corresponding piano key37622 LD (HL),40         Set the attribute byte for the piano key to 40 (INK 0: PAPER 5: BRIGHT 0)37651 LD A,(IY+1) Pick up the second byte of data for this note37654 CALL 37675 Calculate the attribute file address for the corresponding piano key37657 LD (HL),56         Set the attribute byte for the piano key back to 56 (INK 0: PAPER 7: BRIGHT 0)37659 LD A,(IY+2) Pick up the third byte of data for this note37662 CALL 37675 Calculate the attribute file address for the corresponding piano key37665 LD (HL),56         Set the attribute byte for the piano key back to 56 (INK 0: PAPER 7: BRIGHT 0)`
The routine that interrupts the music when a key is pressed is also commented out:
Code: [Select]
`37647 CALL 37687 Check whether ENTER or the fire button is being pressed37650 RET NZ         Return (with the zero flag reset) if it is`
Looking through the code in I think I know why there are two values after the duration. The code is attempting to simulate pressing two keys at once. The inner loop repeats 256 times and decreases both of these values (Register D and E), turning off/on the speaker to mimic a frequency... Pretty impressive.

PS. It doesn't play the music yet.

#### Baggey

• Full Member
• Posts: 135
##### Re: Creating ZX SOUND
« Reply #22 on: February 07, 2021, 06:36:33 »
Well Impressed!

So you dived straight into the Z80 Opcodes Writing an Emulator is a great way of learning the Z80 Opcodes.

Im currently rewriting my opcodes as functions in the quest for more speed rather than using long SELECT/CASE statements.

So Here are RRCA, DJNZ you can ignore the flags! for just simulating the Music Play.

I see you have already got round The "Jump relative instructions"
NOTE the DJNZ and JRNZ Instructions have two differnt timings on wether a TRUE condition or a FALSE condition are met. Again the timing is so fast this might not be recognized by the human ear thou! Oh and when the DJNZ starts which is initialized by B=0 because it started at ZERO it will count 256 times in the loop. Which will cause a small timing addition to the simulated loop. As there are many of these iterations of the loop. So this maybe needed to be be taken into account in the overall timing.
IY and IX are 16 bit register's but their being peeked for a byte ie, LET E=PEEK (IY+1), so it's looking at the address of (IY+1) seeing what's in that memory location and returning the byte to E. There are many UNDOCUMENTED codes on the Z80 and the IX,IY register's are even split into 8 Bit register's and become IYH, IYL, IXH, IXL But that's something else again

Code: BlitzMax
1.
2.   Case 15 ' RRCA
3.         TempCARRY=(A & 1) <> 0
4.         A=(A Shr 1)
5.         If TempCARRY=1 Then A=(A | 128)
6.         '
7.         fSIGN = fSIGN ' Not affected
8.         fZERO = fZERO ' Not affected
9.         fFIVE= ((A & 32) <> 0)
10.         fHALFCARRY = 0
11.         fTHREE = ((A & 8) <> 0)
12.         fPARITY = fPARITY ' Not affected
13.         fADDSUB = 0
14.         fCARRY = TempCARRY
15.         SetFLAGS()
16.         PC:+1 ; TCycles:+4
17.   Case 16 ' DJNZ,d
18.         DisJump=(twosum[PC1]+2) ; B:-1 ; BC=(B Shl 8)+C
19.         If B<>0 Then
20.           PC:+DisJump ; TCycles:+13
21.         Else
22.           PC:+2 ; TCycles:+8
23.         End If
24.   Case 47 ' CPL
25.         A=(A ~ 255)
26.         fSIGN = fSIGN ' Not affected
27.         fZERO = fZERO ' Not affected
28.         fFIVE = ((A & 32) <> 0)
29.         fHALFCARRY = 1
30.         fTHREE = ((A & 8) <> 0)
31.         fPARITY = fPARITY 'Not affected
32.         fADDSUB = 1
33.         fCARRY = fCARRY ' Not affected
34.         SetFLAGS()
35.         PC:+1 ; TCycles:+4
36.

Here is the code for OR and XOR in Method's which are Function's. Again the flags can be ignored for Music play.

Code: BlitzMax
1.   Method Or8(InB:Byte)
2.               ' All Flags affected.
3.               ' Sign Set if Bit 7 is One
4.               ' Zero Set if Answer is zero
5.               ' Halfcarry is Reset
6.               ' Parity Set if EVEN number of One's
7.               ' AddSub is Reset
8.               ' Carry is Reset
9.               A=(A | InB)
10.               '
11.               fSIGN = (A & 128) <> 0
12.               fZERO = (A = 0)
13.               fFIVE = (A & 32) <> 0
14.               fHALFCARRY = 0
15.               fTHREE = (A & 8) <> 0
16.               fPARITY = parity[A]
17.               fADDSUB = 0
18.               fCARRY = 0
19.               SetFLAGS()
20.   End Method
21.
22.   Method Xor8(InB:Byte)
23.                ' All Flags affected.
24.                ' Sign Set if Bit 7 is One
25.                ' Zero Set if Answer is zero
26.                ' Halfcarry is Reset
27.                ' Parity Set if EVEN number of One's
28.                ' AddSub is Reset
29.                ' Carry is Reset
30.                A=(A ~ InB)
31.                '
32.                fSIGN = (A & 128) <> 0
33.                fZERO = (A = 0)
34.                fFIVE = (A & 32) <> 0
35.                fHALFCARRY = 0
36.                fTHREE = (A & 8) <> 0
37.                fPARITY = parity[A]
38.                fADDSUB = 0
39.                fCARRY = 0
40.                SetFLAGS()
41.   End Method
42.

With the Out (254),A Instruction. Just for playing sound the first three bits are for the border. Which can be ignored unless you want some sort of visual effect
The Tcycles are how long the instruction takes. Again how the timing relates to the Samples/rate within the Simulation is beyond me. That's why i started the thread to bring Musical life to my ZX Emulator.

I think the two pulsed loop is a way of simulating two channels at once. Because when you listen to the Tune it sounds kinda distorted or same note out of sync.

Also thinking of timming again.
The Check Routine wether ENTER is being pressed this is going to cause a timming delay.
I think as the CPU runs at 3.5Mhz. One TCycle 'time state' is callculated and then if an instruction takes, say 12,7, or 4 etc this can be taken into account as a pause in the loop.
But once worked out it becomes part of the loop permenantly. Maybe getting sound going. "All be it distorted or out of tune!" and then tweeking the loops for instruction timming might be necessary. Again these pause delays will then become permenant.

Code: [Select]
`37647 CALL 37687 Check whether ENTER or the fire button is being pressed37650 RET NZ         Return (with the zero flag reset) if it is`
Thinking again. if the Z80 CPU runs at 3.5Mhz that's 3.5x10'6. This means that 1 Tcycle will take appronimatly 285 nSes that's 285x10'-9. So every instruction in the LOOPS will need this number setup as a CONSTANT and then multiplied by the individual TCycles of each instruction. Therefore this time can then be worked out on how this effects pitch/duration of the note at Samples/second.

For Example, RRCA has 4 Clock Cycles then 4x285x10'-9=1.14uSecs or 1.14x10'-6 in RealTime. So this needs to be taken into account when working out let's say the bandwith of the note to sound in tune. I try to steer away from the MATH but i think this is one's going to need it. Damm those Cobweb's

Hope that sort of makes's sence with the timming of the Z80 code but again how that effects the music it's self?

I do belive this is going to work.  Happy coding.

Kind Regards Baggey

« Last Edit: February 07, 2021, 08:17:46 by Baggey »
Currently Running a PC that just Aint fast enough!?
ZX Spectrum 48k, NEXT, C64, ORIC Atmos 48K, Enterprise 128K, The SID chip.

Jesus was only famous because of his DAD.

#### col

• Hero Member
• Posts: 586
##### Re: Creating ZX SOUND
« Reply #23 on: February 07, 2021, 10:06:56 »
To save bending your brain too much on the math there is a super handy z80 extension for vscode (if you use it) that will calculate the tstates for you... It can be found here:
https://github.com/theNestruo/z80-asm-meter-vscode

#### Scaremonger

• Full Member
• Posts: 230
##### Re: Creating ZX SOUND
« Reply #24 on: February 07, 2021, 10:34:14 »
To save bending your brain too much on the math there is a super handy z80 extension for vscode (if you use it) that will calculate the tstates for you...

I've recently started looking at vscode on Linux after finding out that there is a BlitzMax extension in development. I'll take a look at that one.

#### iWasAdam

• Hero Member
• Posts: 2354
##### Re: Creating ZX SOUND
« Reply #25 on: February 07, 2021, 12:34:44 »
Very impressed with how all of this is progressing. Excellent work and something to very proud of - emulating chips

#### Scaremonger

• Full Member
• Posts: 230
##### Re: Creating ZX SOUND
« Reply #26 on: February 10, 2021, 07:07:26 »
Just in case anyone is following this and is interested in the TSTATE Cycle data:

Code: [Select]
`TSTATES POINTER 19 37596 FN37596 LD A,(IY+0) PLAY MUSIC7 37599 CP 255 11 37601 RET Z 4 37602 LD C,A 7 37603 LD B,0 4 37605 XOR A 19 37606 LD D,(IY+1) 4 37609 LD A,D 17 37610 CALL 37675 10 37613 LD (HL),80 19 37615 LD E,(IY+2) 4 37618 LD A,E 17 37619 CALL 37675 10 37622 LD (HL),40 11 37624 LOOP OUT (254),A 4 37626 DEC D 12 37627 JR NZ,37634 19 37629 LD D,(IY+1) 7 37632 XOR 24 4 37634 DEC E 12 37635 JR NZ,37642 19 37637 LD E,(IY+2) 7 37640 XOR 24 13 37642 DJNZ 37624 4 37644 DEC C 12 37645 JR NZ,37624 17 37647 CALL 37687 11 37650 RET NZ 19 37651 LD A,(IY+1) 17 37654 CALL 37675 10 37657 LD (HL),56 19 37659 LD A,(IY+2) 17 37662 CALL 37675 10 37665 LD (HL),56 10 37667 INC IY 10 37669 INC IY 10 37671 INC IY 12 37673 JR 37596 7 37675 FN37675 SUB 8 IDENTIFY KEY FROM NOTE4 37677 RRCA 4 37678 RRCA 4 37679 RRCA 4 37680 CPL 7 37681 OR 224 4 37683 LD L,A 7 37684 LD H,89 10 37686 RET 13 37687 FN37687 LD A,(33881) KEYBOARD INPUT7 37690 OR A 12 37691 JR Z,37698 13 37693 IN A,(31) 8 37695 BIT 4,A 11 37697 RET NZ 10 37698 LD BC,49150 12 37701 IN A,(C) 7 37703 AND 1 7 37705 CP 1 10 37707 RET `

#### Scaremonger

• Full Member
• Posts: 230
##### Re: Creating ZX SOUND
« Reply #27 on: February 10, 2021, 08:04:14 »
I'm not sure if I have found a bug in the original code or what, but the music data doesn't seem to be correct after byte 210.

The total length of the music data without the endstop (255) is 285 bytes. The music data is in triplets but 284 is not equally divisable by 3...

This code should explain what I mean:
Code: [Select]
`SuperStrict' Music Data (Blue Danube from Manic Miner)Global data:Byte[] = [80,128,129,80,102,103,80,86,87,50,86,87,50,171,203,50,43,51,50,43,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,96,86,50,171,192,50,43,48,50,43,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,192,50,38,48,50,38,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,203,50,38,51,50,38,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,171,50,32,43,50,32,43,50,128,171,50,43,51,50,43,51,50,128,171,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,152,50,32,38,50,32,38,50,128,152,50,38,48,50,38,48,50,0,0,114,115,50,114,115,50,96,97,50,76,77,50,76,153,50,76,77,50,76,77,50,76,153,50,91,92,50,86,87,50,51,205,50,51,52,50,51,52,50,51,205,50,64,65,50,102,103,100,102,103,50,114,115,100,76,77,50,86,87,50,128,203,25,128,0,25,128,129,50,128,203,255]Print( "DATA LENGTH: "+Len(data) )Local cursor:Int = 0Local duration:IntLocal note:Byte[2]Repeat If data[cursor]=255 cursor=0 ' Reached the end? - Start again ' Read music data duration = data[cursor] note[0]  = data[cursor+1] note[1]  = data[cursor+2] Print( cursor+") "+duration+", "+note[0]+", "+note[1] ) cursor :+ 3Until False`
If you look at the output, you'll see <CURSOR>) <DURATION>, <NOTE-A>, <NOTE-B>. We have already established the first number is the note duration (see below); however if you look at the output after cursor 210 something strange is happening which will mess up the tune.

100 is a Minim (Half Note)
80 is a Dotted Crochet (1/8 + 1/4)
50 is a Crotchet (Quarter note)
25 is a Quaver (Eighth note)

Code: [Select]
`204) 50, 38, 48207) 50, 38, 48210) 50, 0, 0213) 114, 115, 50   <- This is Wrong216) 114, 115, 50219) 96, 97, 50222) 76, 77, 50225) 76, 153, 50228) 76, 77, 50`
« Last Edit: February 10, 2021, 08:05:47 by Scaremonger »

#### col

• Hero Member
• Posts: 586
##### Re: Creating ZX SOUND
« Reply #28 on: February 10, 2021, 08:27:49 »
I would say the data has been copied wrong. Take a look at the original music data and you can see the culprit at address 34115.

Music data:
https://skoolkit.ca/disassemblies/manic_miner/asm/33902.html

#### Scaremonger

• Full Member
• Posts: 230
##### Re: Creating ZX SOUND
« Reply #29 on: February 10, 2021, 12:36:12 »
I would say the data has been copied wrong. Take a look at the original music data and you can see the culprit at address 34115.

Music data:
https://skoolkit.ca/disassemblies/manic_miner/asm/33902.html

@col: Awesome thanks.

The correct Music array should be:

Code: [Select]
`Global data:Byte[] = [80,128,129,80,102,103,80,86,87,50,86,87,50,171,203,50,43,51,50,43,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,96,86,50,171,192,50,43,48,50,43,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,192,50,38,48,50,38,48,50,171,192,50,48,68,50,48,68,50,171,192,50,136,137,50,136,137,50,114,115,50,76,77,50,76,77,50,171,203,50,38,51,50,38,51,50,171,203,50,51,64,50,51,64,50,171,203,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,171,50,32,43,50,32,43,50,128,171,50,43,51,50,43,51,50,128,171,50,128,129,50,128,129,50,102,103,50,86,87,50,64,65,50,128,152,50,32,38,50,32,38,50,128,152,50,38,48,50,38,48,50,0,0,50,114,115,50,114,115,50,96,97,50,76,77,50,76,153,50,76,77,50,76,77,50,76,153,50,91,92,50,86,87,50,51,205,50,51,52,50,51,52,50,51,205,50,64,65,50,102,103,100,102,103,50,114,115,100,76,77,50,86,87,50,128,203,25,128,0,25,128,129,50,128,203,255]`
Si...

SimplePortal 2.3.6 © 2008-2014, SimplePortal