MattelAquarius Posted April 26, 2011 Share Posted April 26, 2011 The technical details are beyond my knowledge base, but I'm enjoying the discussion, and the sounds! Quote Link to comment Share on other sites More sharing options...
GroovyBee Posted April 26, 2011 Share Posted April 26, 2011 You might find this interesting :- http://www.romanblack.com/BTc_alg.htm I'm not sure if it can be applied to the Aquarius sound system tho. Quote Link to comment Share on other sites More sharing options...
chjmartin2 Posted April 26, 2011 Author Share Posted April 26, 2011 You might find this interesting :- http://www.romanblack.com/BTc_alg.htm I'm not sure if it can be applied to the Aquarius sound system tho. Beautiful! That website is my original inspiration for the 1 Bit DAC! Quote Link to comment Share on other sites More sharing options...
chjmartin2 Posted April 27, 2011 Author Share Posted April 27, 2011 I just tried the PacMr with the digitized sound and I can hear a high level buzz which means he is using a much faster oscillation that I can achieve. This is probably because he preconverted the audio to either a high speed 1 bit or 4 bit PWM. Anyway, how would I go about getting to the code? Quote Link to comment Share on other sites More sharing options...
mvdsteenoven Posted April 27, 2011 Share Posted April 27, 2011 Anyway, how would I go about getting to the code? Personally I use dz80 to disassemble code. PacMr_OhSucks is a cracked version of PacMr; so I have disassembled PacMr and PacMr_OhSucks. Next I used WinMerge from sourceforge to compare the files, and found: Address# PacMr OhSucks 3c06 call $3cf8 call $5200 Next at address $5200 5200 f5 push af 5201 c5 push bc 5202 d5 push de 5203 e5 push hl 5204 213052 ld hl,$5230 5207 0efc ld c,$fc 5209 23 inc hl 520a 7e ld a,(hl) 520b 57 ld d,a 520c 0608 ld b,$08 520e cb12 rl d 5210 1eff ld e,$ff 5212 3002 jr nc,$5216 5214 1efe ld e,$fe 5216 ed59 out (c),e 5218 3e0a ld a,$0a 521a 3d dec a 521b 20fd jr nz,$521a 521d 10ef djnz $520e 521f 7c ld a,h 5220 fe60 cp $60 5222 c20552 jp nz,$5205 5225 e1 pop hl 5226 d1 pop de 5227 c1 pop bc 5228 f1 pop af 5229 c9 ret All code after address $5230 is the voice and should be considered raw bytes (.db). As you can see it is basically the same code as the quick type program. You can find the "raw" disassembled files here: PacOh.zip It has not been beautified; you'll have to find the DATA (.db) lines yourself and add .db or defb to it manually. Regs Martin Quote Link to comment Share on other sites More sharing options...
chjmartin2 Posted April 28, 2011 Author Share Posted April 28, 2011 Can you tell what he is doing? I wish that I had some documentation. I think because he is using the Aquarius as the recording device that he doesn't have to worry about how to shape the wave. Is it as trivial as sampling the 1's and 0's from the cassette port? It doesn't look it. It looks like he is introducing 1's into the mix. I know that his sounds much much better than any that I have been able to do. I wonder if I could try to get him an email... This first piece of code records from microphone into memory; ; ; Disassembled by: ; DASMx object code disassembler ; (c) Copyright 1996-1998 Conquest Consultants ; Version 1.20 (Apr 2 1998) ; ; File: LOAD.BIN ; Date: Wed Apr 20 19:16:28 2011 ; CPU: Zilog Z80 (Z80 family) ; ; org $3E80 ; ld hl,$3F00 ld c,$FC L3E85: ld b,$08 L3E87: in a,(c) out (c),a cp $FF rl d ld a,$0A L3E91: dec a jr nz,L3E91 djnz L3E87 ld a,d inc hl ld (hl),a ld ($3002),hl ld a,h cp $7D jp nz,L3E85 ret This second part plays the recorded sound; ; ; Disassembled by: ; DASMx object code disassembler ; (c) Copyright 1996-1998 Conquest Consultants ; Version 1.20 (Apr 2 1998) ; ; File: SPEECH.bin ; Date: Wed Apr 20 19:15:23 2011 ; CPU: Zilog Z80 (Z80 family) ; ; org $3E80 ; ld hl,$3F00 ld c,$FC L3E85: inc hl ld a,(hl) ld d,a ld ($3002),hl ld b,$08 L3E8D: rl d ld e,$FF jr nc,L3E95 ld e,$FE L3E95: out (c),e ld a,$0A L3E99: dec a jr nz,L3E99 djnz L3E8D ld a,h cp $7D jp nz,L3E85 ret Quote Link to comment Share on other sites More sharing options...
MattelAquarius Posted April 28, 2011 Share Posted April 28, 2011 I wonder if I could try to get him an email... Harrold Spier is rightfully proud of his work, and has shown at Yahoo Group, in the past, to discuss all things Aquarius. Perhaps a "Seeking Harrold" post over there, will bring him out. In fact, a renewed open invite for the group to stop by this forum, is probably overdue. Quote Link to comment Share on other sites More sharing options...
mvdsteenoven Posted April 28, 2011 Share Posted April 28, 2011 Can you tell what he is doing? I have added some comments to it. Recording; org $3E80 MAIN: ld hl,$3F00 ; Memory Location to ROM Sample (= 16128) ld c,$FC ; I/O port for sound replay: ld b,$08 ; Sub-Samples subsamp: in a,(c) ; Read from I/O port out (c),a ; and send it right back (playback) ; bit 7 in register A will be set if there is sound ; all other bits will be set by default ; register A will have the value 255 ($FF) or 254 ($FE) cp $FF ; Compare register A with 255 ($FF) to see if bit 7 has been set ; Carrier bit will be set if register A has a lower value ; The carrier bit will rotate left into register D to bit 0 rl d ; c -> bit 0, bit 0 -> bit 1, ....., bit 6 -> bit 7, bit 7 -> c ; register D becomes a collection of 8x carrier bits ld a,$0A ; Size of the delay loop DELAY: dec a ; decrease delay counter a = a - 1 jr nz,DELAY ; repeat until register A hits zero djnz subsamp ; b = b - 1, jump if b is not zero ld a,d ; store the collected carrier bits into register A inc hl ; next memory location ld (hl),a ; store the collected carrier bits into memory ld ($3002),hl ; show the HL register in upperleft corner on screen ld a,h ; store the upper H register into A ; If register H is 125 ($7D) then memory address location cp $7D ; 32000 ($7D00) has been reached (= end address) jp nz,replay ; Repeat until memory address location 32000 ($7D00) is reached ret ; Back to BASIC Play sound org $3E80 MAIN: ld hl,$3F00 ; Memory Location to ROM Sample (= 16128) ld c,$FC ; I/O port for sound replay: inc hl ; next memory location ld a,(hl) ; fetch the collected carrier bits from memory address ld d,a ; store collected carrier bits into register D ld ($3002),hl ; show the HL register in upperleft corner on screen ld b,$08 ; Sub-Samples subsamp: ; The 7th bit of register D will rotate left into the carrier flag rl d ; c -> bit 0, bit 0 -> bit 1, ....., bit 6 -> bit 7, bit 7 -> c ld e,$FF ; default value 255 ($FF), bit 7 for I/O has been set jr nc,Output ; Was the carrier set during the "rl d" command ld e,$FE ; carrier was set -> reset bit 7 for I/O Output: out (c),e ; sending I/O ld a,$0A ; Size of the delay loop DELAY: dec a ; decrease delay counter a = a - 1 jr nz,DELAY ; repeat until register A hits zero djnz subsamp ; b = b - 1, jump if b is not zero ld a,h ; store the upper H register into A ; If register H is 125 ($7D) then memory address location cp $7D ; 32000 ($7D00) has been reached jp nz,replay ; Repeat until memory address location 32000 ($7D00) is reached ret ; Back to BASIC Basically the same thing as your code, but Harrold is using the carrier flag to determine if bit 7 has been set. Regs, Martin Quote Link to comment Share on other sites More sharing options...
mvdsteenoven Posted April 30, 2011 Share Posted April 30, 2011 I have modified the speech program by Harrold Spier. It nows records sound into an array so it can be CSAVED* to tape and reloaded. Voice.txt Enjoy! Regs, Martin Quote Link to comment Share on other sites More sharing options...
chjmartin2 Posted April 30, 2011 Author Share Posted April 30, 2011 I have modified the speech program by Harrold Spier. It nows records sound into an array so it can be CSAVED* to tape and reloaded. Voice.txt Enjoy! Regs, Martin Martin, This is fantastic because now I can play a wave file in the emulator, use the speech program to "sample" it, save to a CAQ, convert the CAQ to binary (I think you showed me how to do that with the bitmap converter) and then play it back on a real Aquarius. I can record that sound and load the waveform next to the original, I may be able to get a sense of how to better encode the 1 bit samples for the Aquarius. Chris Quote Link to comment Share on other sites More sharing options...
chjmartin2 Posted April 30, 2011 Author Share Posted April 30, 2011 I have modified the speech program by Harrold Spier. It nows records sound into an array so it can be CSAVED* to tape and reloaded. Voice.txt Enjoy! Regs, Martin Martin, This is fantastic because now I can play a wave file in the emulator, use the speech program to "sample" it, save to a CAQ, convert the CAQ to binary (I think you showed me how to do that with the bitmap converter) and then play it back on a real Aquarius. I can record that sound and load the waveform next to the original, I may be able to get a sense of how to better encode the 1 bit samples for the Aquarius. Chris I have come a long way, and I am now used to using Hex Editors and assembler and such. Not very good at it, but also not lost. Anyway, I can't seem to find the format of the CAQ file lying around. I want to take the CAQ file that I created with your save option and then find and strip out the sound sample. How did you encode the data and, what is the format of the CAQ file? I looked back and didn't seem to see the detail I needed. Quote Link to comment Share on other sites More sharing options...
mvdsteenoven Posted April 30, 2011 Share Posted April 30, 2011 How did you encode the data and, what is the format of the CAQ file? To know the format of the CAQ you will need to know how the Aquarius BASIC stores an array into memory. It reserves 4 bytes for every element whenever you create an array. So when you create an array like DIM A(10) it will hold 11 elements, starting from zero [ 0..10 ] and BASIC reserves 11*4 = 44 bytes of memory. Now the CAQ file starts with 12 times the CHAR(255), used for synchronisation. Next is the name of the game, for an array this is ###### (6 times #) The array data starts directly after this header. The voice array has 4201 elements (15 DIMA(4200)); the next 16804 bytes is the array data. BASIC adds an additional 15 bytes of zero after the array data, but this not used for loading the array. BASIC will check the size of the array and keeps loading from tape till it hits the end of the array in memory, not till the end of the array on tape. The data is encoded using Harrold his program. It is 1 bit audio sample like you created in message #1 and #2. There is a 10times DELAY per bit (like in your message #1 is 50times DELAY). While you are sending bits directly to the I/O, Harolds program is using a flag to determine if it needs a 1 or 0. When the flag is 1 it sends a 0 and an 0 flag means sending a 1. Hope this helps. Regs Martin Quote Link to comment Share on other sites More sharing options...
chjmartin2 Posted June 19, 2011 Author Share Posted June 19, 2011 Here is the latest on my efforts to implement a better sound digital audio playback program for the Aquarius. I have exhausted my efforts in spite of knowing that a better sounding encoder could be developing, but frustrated in the fact that I know not enough about noise shaping or digital filters to appropriately implement the noise shape and dithering that I want. From trial and error I tried to get the best result, but, I know it could be optimized if I could do the math rather then just randomly poking around. So, here is the theory of the program. We will start with the FreeBasic code for the audio encoder: Screenres 1500,900,32: CLS LOCATE 1, 1 FILENAME$ = "forever.raw" TOGGLE = 0 PAUSER = 0 randomize timer samples = 355546 dim audio(samples+10) as UBYTE, audioout(samples+10) as UBYTE, naudio(samples+10) as UBYTE, ebitout(samples+10) as UBYTE 'input "How many levels?",levels totalbitcount=samples ebitcount=int(totalbitcount/8) if totalbitcount mod 8>0 then ebitcount=ebitcount+1 dim bitaudioout(ebitcount) as UBYTE OPEN filename$ FOR BINARY as #1 get #1,,audio() close #1 bitcounter=0:outputbit$="":ebitcounter=0 for j=1 to samples naudio(j)=audio(j) pset (j/(samples/1500),100+audio(j)),rgb(audio(j),255,audio(j)/2) 'Clip Check 'if audio(j)>255 then audio(j)=255 'if audio(j)<0 then audio(j)=0 if audio(j)>127 then audioout(j)=1 ebitout(j)=255 else audioout(j)=0 ebitout(j)=0 end if ' Distortion Shaping - 4th order 'locate 55,1:print "audio(j): ";audio(j) 'locate 56,1:print "audioout(j): ";audioout(j) derror=naudio(j)-(255*audioout(j)) 'locate 57,1:print "derror: ";derror 'locate 58,1:print "naudio(j): ";naudio(j) 'Noise Shaping & Dither Filter Block 'audio(j)=audio(j)+int(rnd(1)*32-16) audio(j+1)=audio(j+1)+int(derror*.5)+int(rnd(1)*32-16) audio(j+2)=audio(j+2)+int(derror*.1)+int(rnd(1)*10-5) audio(j+3)=audio(j+3)+int(derror*.1)+int(rnd(1)*10-5) 'audio(j+4)=audio(j+4)+int(derror*.1)+int(rnd(1)*16- 'audio(j+5)=audio(j+5)+int(derror*.1)+int(rnd(1)*16- 'audio(j+6)=audio(j+6)+int(derror*.1) 'audio(j+7)=audio(j+7)+int(derror*.1) audio(j+=audio(j++int(derror*.1)+int(rnd(1)*10-5) audio(j+9)=audio(j+9)+int(derror*.1)+int(rnd(1)*10-5) audio(j+10)=audio(j+10)+int(derror*.1)+int(rnd(1)*10-5) pset (j/(samples/1500),400+audioout(j)*25),rgb(audio(j)/2,255,audio(j)) 'locate 65,1:print "audio: ";audio(j);" - audioout: ";audioout(j) bitcounter=bitcounter+1 outputbit$=outputbit$+str$(audioout(j)) if bitcounter/8=int(bitcounter/8) then ebitcounter=ebitcounter+1 bitaudioout(ebitcounter)=128*val(mid$(outputbit$,1,1))+64*val(mid$(outputbit$,2,1))+32*val(mid$(outputbit$,3,1))+16*val(mid$(outputbit$,4,1))+8*val(mid$(outputbit$,5,1))+4*val(mid$(outputbit$,6,1))+2*val(mid$(outputbit$,7,1))+1*val(mid$(outputbit$,8,1)) locate 50,1:print outputbit$;" - ";bitaudioout(ebitcounter) outputbit$="" end if 'pset (bitcounter/(totalbitcount/1500),400+levels+50+0),rgb(255,255,255) for c=1 to pauser:next c next j locate 70,1 print "bitcounter: ";bitcounter print "ebitcounter: ";ebitcounter print "ebitcount: ";ebitcount print "totalbitcount: ";totalbitcount print "samples: ";samples input ggg$ open "OUTBIT.BIN" for BINARY AS #1 put #1,,bitaudioout() close #1 open "OUTPUT.RAW" for BINARY AS #1 put #1,,ebitout() close #1 This program is not set up for general use, but what it does is take a RAW UNSIGNED 8-BIT PCM file and convert it to a 1-BIT Audio File. The required input is a 50,700 Hz file. You may wonder why 50,700 Hz, given the output program I have put together, it can toggle the sound output port at that frequency. This means the Nyquist frequency (highest frequency we can represent on the Aquarius speaker is 25,350 Hz. This may not be entirely valid as I can still hear the output if I set the output to 10101010 - so I think that I am sending a signal to the audio output faster than the circuit can respond and therefore introduce Either way, this method, maxing out the rate, seemed to yield the best result. http://en.wikipedia.org/wiki/Noise_shaping I tried the best I could to use the above article to implement noise shaping. The problem that I have is that I do not know how to move more of the noise energy above the audible range. I am sure it could be done better. Check out this section of the Noise Shaping article: http://en.wikipedia.org/wiki/Noise_shaping#Noise_shaping_and_1-bit_converters It says that in a 1 bit encoding scheme, you can use noise shaping to move the noise above the audible range. In thery the Aquarius can play back frequencies at 1 bit greater than 25,000 Hz, but in practice I haven't been able to get there. I also am not sure if I have optimized the output frequency, as my player introduces no delay between subsequent samples aside from the built-in delay to extract the data bits related to the audio bit stream. Here is the code to the player: ; Program to Create Sound Using a 1-BIT Playback at 50,700 Hz ; Must be run on Actual Aquarius with Bank-Switching Capable Cartridge ; Requires 56k of Audio Data to be loaded before execution code .org $E000 .db $b6, $b7, $24, $2a, $8d, $9c, $42, $b0 .db $53, $6c, $90, $64, $89, $a8, $f9, $70 BANKMAIN: ld de,$c000 ; load the target address into register DE ld a,0 ; load the target bank number into the accumulator ld (de),a ; write the bank number into the cartridge space call main ld de,$c000 ; load the target address into register DE ld a,1 ; load the target bank number into the accumulator ld (de),a ; write the bank number into the cartridge space call main ld de,$c000 ; load the target address into register DE ld a,2 ; load the target bank number into the accumulator ld (de),a ; write the bank number into the cartridge space call main ld de,$c000 ; load the target address into register DE ld a,3 ; load the target bank number into the accumulator ld (de),a ; write the bank number into the cartridge space call main ld de,$c000 ; load the target address into register DE ld a,4 ; load the target bank number into the accumulator ld (de),a ; write the bank number into the cartridge space call main ld de,$c000 ; load the target address into register DE ld a,5 ; load the target bank number into the accumulator ld (de),a ; write the bank number into the cartridge space call main ld de,$c000 ; load the target address into register DE ld a,6 ; load the target bank number into the accumulator ld (de),a ; write the bank number into the cartridge space call main halt main: ld hl, $C000 ; Memory Location to ROM Sample ld bc, 8192 ; Number of Samples replay: ld d, 8 ; Sub-Samples ld e, (hl) ; load sample into e ;ld ($3002),hl ; show the HL register in upperleft corner of screen subsamp: rl e ; 7th bit of register E will rotate left into the carrier flag ld a, $FF ; default value 255 bit 7 for i/o has been set jp nc, Output ; Was the carrier set during the "rl d" command ld a, $FE ; Carrier was set -> reset bit 7 for I/O Output: out ($FC),a ; sending I/O ; ld a, 10 ; delay loop ;Delay: ; dec a ; decrease a ; jr nz, Delay ; repeast until a is zero dec d ; countdown ld a, d ; check on d jp nz, subsamp ; repeat until 8 samples inc hl dec bc ; Decrement Sample Counter ld a, c ; Load C or b ; Is BC Zero jp nz, replay ; If it isn't zero, keep playing FINISH: ret ; Zero-Fill Remainder of Cartridge ROM .org $FFFF .byte $00 .end Here now we present three audio files. First is the original recording, second is my first attempt to reproduce the audio on the Aquarius and third is the above implementation with Noise Shaping and Dithering. seriousBLEEP.wav Wave1.wav Serious_Bleep.wav Lastly, for the select few that could actually run a 64K Rom on their Aquarius, here is the ROM Image. 1bitbank.bin Quote Link to comment Share on other sites More sharing options...
MattelAquarius Posted June 19, 2011 Share Posted June 19, 2011 Vastly improved! I loved it. Thanks. Quote Link to comment Share on other sites More sharing options...
chjmartin2 Posted November 25, 2011 Author Share Posted November 25, 2011 Darn... I had just wrote a great post about my latest work on Digitized audio on the Aquarius and lost it. We'll just have to write it shorter this time. Here is my latest work: It is recorded at 50,700 Hz and uses a variety of techniques to try to mask/hide/reduce the apparent noise. It is only a 1 bit buzzer and trying to use PWM has proven problematic. I utilized several techniques that are used on Delta Sigma converters along with noise shaping and dithering. The Aquarius isn't going to replace your media player any time soon, but I am pretty happy with the output. It is the song Pumped up Kicks by Foster the People. The audio file has been trimmed down to under 2 meg so I could post it. It was captured off of a real Aquarius, playing the audio using a 1 Meg EPROM and 8k bankswitching on Jay's Aquaricart. The next step is to combine this work with http://www.atariage....nce-video-loop/ and create syncronized audio and video playback. I will have to reduce the playback rate, and reoptimize for a lower sampling rate, so we'll see how I do. Once I am done with that, sadly, I will say goodbye to the Aquarius and move on to another machine. I have now spent a year with this little guy and I will miss him dearly. The only other way I could think of to get a better output from this machine would be to send a series of known 1 and 0's to the audio output bit and then sample the resulting audio playback, then go ahead and create a time dependent transfer function for the computer. I tried to do this, but I could not get an R-Squared high enough to have confidence in the result. I may not have the right equipment or knowledge. Thanks, Chris 2 Quote Link to comment Share on other sites More sharing options...
MattelAquarius Posted November 26, 2011 Share Posted November 26, 2011 Chris, I can't wait to check out your latest results. Unfortunately, I get an error message when I click the link: "You don't have permission to view the file." Please consider staying with the Aquarius. You still haven't made your Aquarius game, so it is not yet time! Quote Link to comment Share on other sites More sharing options...
chjmartin2 Posted November 26, 2011 Author Share Posted November 26, 2011 Try this... audio_30.wav 1 Quote Link to comment Share on other sites More sharing options...
MattelAquarius Posted November 26, 2011 Share Posted November 26, 2011 Blown away, once again. Thank you for the awesome sample. It looks like we'll soon need a program to connect to iTunes, if you keep this up. Quote Link to comment Share on other sites More sharing options...
chjmartin2 Posted November 26, 2011 Author Share Posted November 26, 2011 Ok, here is the opening theme song to the great cartoon Thundercats. It was played on a real Aquarius using 1 bit at 6,830 Hz. I think this is the best I can do at this low a bit rate, there is a lot of noise, but I think when combined with video the illusion will be relatively convincing. The audio was captured at 44.1 kHz, 16 bit and then converted to MP3 for uploading. I listened to the MP3 carefully to ensure that it is a faithful reproduction of what I am hearing out of the machine. Enjoy. Tcats_6.8k.mp3 1 Quote Link to comment Share on other sites More sharing options...
jaybird3rd Posted November 27, 2011 Share Posted November 27, 2011 I'm happy to see your progress, and I'm glad that my SuperCart board was helpful (apologies again for taking so long to get it out to you). I too would encourage you to stay with the Aquarius; I'd love to see you try your hand at a game or two. I'm sure the work you've done so far will prove useful to future Aquarius projects. Quote Link to comment Share on other sites More sharing options...
chjmartin2 Posted January 14, 2012 Author Share Posted January 14, 2012 (edited) I am pretty proud of what I have finally accomplished today. Unfortunately, football is calling in a few minutes, so no more work until tomorrow. However, I wanted to post my player code and two audio samples. Only using 64k ROM at the moment, so not a lot of audio. I'll be doing some full or almost full song conversions and posting them later. Let's start with the code: ; Program to Create Sound Using a 4-BIT PWM at 9,724 Hz ; Must be run on Actual Aquarius with Bank-Switching Capable Cartridge ; Requires 56k of Audio Data to be loaded before execution code .org $E000 .db $b6, $b7, $24, $2a, $8d, $9c, $42, $b0 .db $53, $6c, $90, $64, $89, $a8, $f9, $70 init: ; Load the Data Table into the Screen ld hl, PWMDATA ; load memory location PWM Data into HL ld de, 12328 ; use the screen memory (good a place as any) ld bc, 256 ; 256 Bytes dude ldir ; load it up bankmain: ld de, $c000 ; load the target address into register DE ld a, 0 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 1 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 2 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 3 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 4 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 5 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 6 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 7 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 8 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 9 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 10 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 11 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 12 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 13 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 14 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 15 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 16 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 17 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 18 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 19 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 20 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 21 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 22 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 23 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 24 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 25 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 26 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 27 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 28 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 29 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 30 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 31 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 32 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 33 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 34 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 35 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 36 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 37 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 38 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 39 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 40 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 41 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 42 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 43 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 44 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 45 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 46 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 47 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 48 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 49 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 50 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 51 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 52 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 53 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 54 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 55 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 56 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 57 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 58 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 59 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 60 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 61 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 62 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 63 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 64 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 65 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 66 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 67 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 68 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 69 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 70 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 71 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 72 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 73 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 74 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 75 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 76 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 77 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 78 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 79 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 80 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 81 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 82 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 83 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 84 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 85 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 86 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 87 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 88 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 89 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 90 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 91 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 92 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 93 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 94 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 95 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 96 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 97 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 98 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 99 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 100 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 101 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 102 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 103 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 104 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 105 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 106 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 107 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 108 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 109 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 110 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 111 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld a, 112 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 113 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 114 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 115 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 116 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 117 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld a, 118 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 119 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 120 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 121 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld a, 122 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 123 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 124 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld a, 125 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space; call main ; call the drawing program ld de, $c000 ; load the target address into register DE ld a, 126 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ld a, 127 ; load the target bank number into the accumulator ld (de), a ; write the bank number into the cartridge space call main ; call the drawing program ;jp bankmain halt main: ;ld (13248), hl ; put it into the screen location for the sample ld bc, 8192 ; number of bytes of samples ld hl, $C000 ; (10) Memory Location to ROM Sample push hl ; (11) put the rom location on the stack replay: pop hl ; (10) get the Rom memory location from the stack ld a, (hl) ; (7) load sample into a ex af, af' ; (4) original sample into the alt register ld a, (hl) ; (7) load sample into e inc hl ; (6) Move up the Rom sample location push hl ; (11) put the rom location back on the stack and 240 ; we need the first 4 bit value first, 240='11110000' ld e,a ; get the result back into e ld hl, 12328 ; load the start of the screen (that is where the PWM data is) ld d, 0 ; put zero into d to make sure you are just adding the 8 bit values add hl, de ; add e to hl which moves to the right pwm memory location ld a, (hl) ; (7) load first byte of PWM model into A out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample inc hl ; (6) move up memory location ld a, (hl) ; (7) load second pwm model out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (11) send out the sample ex af, af' ; (4) original sample back into accumulator and 15 ; now we need the trailing 4 bit value, 15='00001111' rlca ; rotate accumulator left one bit rlca ; rotate accumulator left one bit rlca ; rotate accumulator left one bit rlca ; rotate accumulator left one bit ld e,a ; put the value back into e ld hl, 12328 ; load the start of the screen (that is where the PWM data is) ld d, 0 ; put zero into d to make sure you are just adding the 8 bit values add hl, de ; add e to hl which moves to the right pwm memory location ld a, (hl) ; (7) load first byte of PWM model into A out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample inc hl ; (6) move up memory location ld a, (hl) ; (7) load second pwm model out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample rlca ; (4) rotate the PWM model left out ($fc), a ; (12) send out the sample dec bc ; (6) Reduce the byte counter ld a, b ; grab the counter or c ; check if it is zero jp nz, replay ; do it again until we are done! FINISH: pop hl ; (10) clean up the stack ;jp FINISH ret PWMDATA: .db 0,0,255,255,255,255,255,255,254,254,254,254,254,254,254,254 .db 1,0,255,255,255,255,255,255,254,254,254,254,254,254,254,254 .db 8,16,255,255,255,255,255,254,254,254,254,254,254,254,254,254 .db 17,16,255,255,255,255,255,254,254,254,254,254,254,254,254,254 .db 17,17,255,255,255,255,254,254,254,254,254,254,254,254,254,254 .db 36,146,255,255,255,255,254,254,254,254,254,254,254,254,254,254 .db 74,164,255,255,255,254,254,254,254,254,254,254,254,254,254,254 .db 42,170,255,255,255,254,254,254,254,254,254,254,254,254,254,254 .db 85,85,255,255,254,254,254,254,254,254,254,254,254,254,254,254 .db 85,213,255,255,254,254,254,254,254,254,254,254,254,254,254,254 .db 213,213,255,254,254,254,254,254,254,254,254,254,254,254,254,254 .db 221,213,255,254,254,254,254,254,254,254,254,254,254,254,254,254 .db 221,245,254,254,254,254,254,254,254,254,254,254,254,254,254,254 .db 221,247,254,254,254,254,254,254,254,254,254,254,254,254,254,254 .db 223,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254 .db 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254 ; Zero-Fill Remainder of Cartridge ROM .org $FFFF .byte $00 .end For the educated eye, you may want to look at few cool things (at least I think they are cool) I did to get faster playback. I precalcuated my Pulse Width Modulation Values and loaded them to the screen. Although I load a full 16 bytes per sample, I am really only using the first two. (It makes it so I can address the memory with a simple add of the sample level against the start of the screen memory.) Because I have two samples per byte, I need to pull the first 4 bits and then the second 4 bits to play the sample. I don't want to load from memory twice (ouch, 10 t-states to load to the HL register and then 7 to load the value into accumulator) so I go ahead and store the accumulator in the shadow register (4 t-states for the exchange and 7 t-states for the exta load = 11 versus 17.) On the other side, the ex af,af' only takes up 4 t-states instead of using a pop (10) or loading it back from memory (7) - all in that storage takes 15 t-states instead of 24 sending to memory. I use BC as my byte counter and because I unrolled the playback loop I don't have any need for the b and c registers at all. There are a bunch more that I can't think of right now, but it is a pretty fast piece of code. Let's simplify what we are doing. We are taking an audio file and downconverting it to 9,724 samples per second at 16 different levels (4-bit). We are then packing each simple into either the first 4 bits of each byte or the last 4 bits, so, in 56k of data we would have 112k samples. The player then loads a table with a series of 1's and 0's representing each of the 16 levels. The lowest level has 0 1's and the highest has 16 ones. The player pulls the sample, uses it as a screen memory offset and pulls the right PWM model from the table for that sample - then it sends out the 1's and 0's to the bit 0 of the sound output port. Effectively, although our audio sample rate is 9,724 samples per second, we are in fact, sending a WHOPPING 155,584 samples per second to the sound output bit. The Aquarius is moving data from the rom memory to the sound output bit effectively one time every 6.4 * 10^-6 seconds. The clock runs at 3.54 Megahertz, so we are taking 1 sample to the output bit per 23 clock cycles. I think that is pretty good. The theoretical maximum is to hard-code the output, by loading $FC to register c and then using OUT ©, n at 12 t-states. That math works out to 376,648 Hertz, but you wouldn't be able to store very much - we're getting 41% of the max with 33 million samples, to get up to 100% I'd only be able to store 1 million samples. Do the math, we're talking a 1 megabyte rom and 214 seconds versus 2.8 seconds. Now - some samples. (I will post vid caps from tv later when i make longer samples.) I have one more experiment to do with this to try to improve the quality - but I really am hitting the max of what I think I can get done using just the buzzer and the Z80 as a software DAC. borntobaby.mp3 seriousblout.mp3 Edited January 14, 2012 by chjmartin2 Quote Link to comment Share on other sites More sharing options...
Aquaman Posted January 15, 2012 Share Posted January 15, 2012 Sounds impressive!!!! Keep up the great work you are doing! Quote Link to comment Share on other sites More sharing options...
chjmartin2 Posted February 21, 2012 Author Share Posted February 21, 2012 I am not sure if anybody is even watching this thread, but I can tell you that this topic is still very much on my mind. I have gone back and dusted off some of my memory related to the Signals and Systems class I had to take in college. I have determined that the Aquarius does not output a pure square wave, in fact, it outputs a very interesting looking wave when the output bit of the sound port is set high. It sends out a mirror image of that signal when it is set low. I have analyzed that response and characterized it at 192,000 Hz. Since the Aquarius is essentially a circuit, the behavior of the outputs will behave in a predictable manner. The sound output goes high, that sets the voltage high which goes through capacitor and two resistors (in a meaningful circuit) and to the sound output which is then RF modulated to channel 3. That output can be captured and modeled. The Aquarius should behave like a linear-time invariant system. The practical upshot of this is that any output sound from the Aquarius is really just a time-delayed summation of either the high pulse response or the low pulse response. Since I know that I can only send one of two pulses, then I should be able to predict at a very high accuracy the Aquarius output for a given input signal (series of 1's and 0's to the sound port.) If I can model and predict then I can also optimize. Even more interesting is that if I model the signal over time and model the response over time then I am not limited by Nyquist, well, not entirely. I can shape the wave using an optimized series of pulse with a VARIABLE frequency rate. I'd have to store the delays in the data stream, OR, I can write the converter based on the time limitations of my playback code. There would be a longer delay at the end of a loop of samples versus while inside the loop for example. I am not sure how this will all work out, but I do know that I have sampled the pulse responses, lined them up, created a problem to play overlapping pulses and demonstrated how that could be modeled in software. I am absolutely positive that I will achieve a much higher Signal to Noise ratio than I have. I have also created (which is not posted) a player than can play back 1 bit audio at 170k Hertz. If I have 170,000 samples per second and pulse-wave shaping, along with noise dither then I have to be able to get some really high quality sound out of the buzzer. Stay tuned. (If anybody is still reading!) Quote Link to comment Share on other sites More sharing options...
MattelAquarius Posted February 22, 2012 Share Posted February 22, 2012 Still reading and still fascinated! As always, I look forward to seeing, and hearing more! Quote Link to comment Share on other sites More sharing options...
intvnut Posted February 22, 2012 Share Posted February 22, 2012 I am not sure if anybody is even watching this thread, but I can tell you that this topic is still very much on my mind. I have gone back and dusted off some of my memory related to the Signals and Systems class I had to take in college. I have determined that the Aquarius does not output a pure square wave, in fact, it outputs a very interesting looking wave when the output bit of the sound port is set high. It sends out a mirror image of that signal when it is set low. I have analyzed that response and characterized it at 192,000 Hz. Is the circuit anything more complicated than an RC low-pass filter hooked to the output bit from the PLA? I don't have an Aquarius schematic, otherwise I'd look. If it's just a series resistor plus parallel resistor to ground, then that forms a simple single-pole low-pass filter which should be easy to model. Harmonics above the corner frequency 1/(R*C) will get attenuated by 20dB for every factor of 10 increase in frequency. If the circuit is more complex than that, it still shouldn't be too hard to model based on whatever passives are in it. BTW, is the waveform truly symmetric? I believe a TTL output may transition low faster than it transitions high. You could model that as two separate circuits, one with a larger series resistor. CMOS, on the other hand, is likely more balanced. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.