Jump to content
IGNORED

Digitized Sound on Aquarius


Recommended Posts

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?

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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

Link to comment
Share on other sites

  • 1 month later...

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

Link to comment
Share on other sites

  • 5 months later...

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

  • Like 2
Link to comment
Share on other sites

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! ;)

Link to comment
Share on other sites

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

  • Like 1
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

  • 1 month later...

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 by chjmartin2
Link to comment
Share on other sites

  • 1 month later...

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!)

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...