base:noise_waveform
no way to compare when less than two revisions
Differences
This shows you the differences between two versions of the page.
— | base:noise_waveform [2015-04-17 04:33] (current) – created - external edit 127.0.0.1 | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Examination of SID noise waveform ====== | ||
+ | |||
+ | ===== Sampling the waveform ===== | ||
+ | |||
+ | The waveforms of the SID in the c64 and c128 can be examined, because the SID provides a 8-bit output register of the waveform of voice 3 in register $1b. | ||
+ | |||
+ | The exact waveform can also be examined from the " | ||
+ | |||
+ | To examine the data, we want to be able to sample the output in a consistent way. First step is to establish the sampling-rate to be used. Of course the sampling-rate must depend on the frequency used in registers $0e and $0f, so our job is to determ ine the dependency between the frequency and the waveform " | ||
+ | |||
+ | To this end, we want to read the waveform output of register $1b as fast as possible. With a REU it is possible to sample the value every cycle, and with the program " | ||
+ | < | ||
+ | sta $d412 ;Start waveform | ||
+ | stx $df01 ;Start sampling | ||
+ | </ | ||
+ | The STA and STX instructions take 4 cycles, but the data in the table suggests that the sampling is delayed only 3 cycles. Furthermore the first value count is probably 1.5 times longer than the waveform. | ||
+ | |||
+ | Table 1: Frequency, wavelength and initial delay | ||
+ | < | ||
+ | Frequency | ||
+ | ---------------------------------------------- | ||
+ | $ffff | ||
+ | $C000 | ||
+ | $AAAA | ||
+ | $8000 | ||
+ | $6000 | ||
+ | $4000 | ||
+ | $3222 | ||
+ | $3000 | ||
+ | $1000 | ||
+ | $0100 | ||
+ | </ | ||
+ | This leads to the conclusion that the frequencies can generated with a loop like this for the noise waveform: | ||
+ | < | ||
+ | void Frequency-generator(long freq) { | ||
+ | long delay=0x180000; | ||
+ | long cycle=0; | ||
+ | for (;;;) { /* Repeat forever */ | ||
+ | delay= delay-freq; | ||
+ | if (delay0) { | ||
+ | delay= delay+0x100000; | ||
+ | waveform_output= calculate new value for waveform; | ||
+ | }; | ||
+ | waveform[cycle]= waveform_output; | ||
+ | cycle= cycle+1; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | Furthermore, | ||
+ | |||
+ | Notice that the above program is not garanteed to be 100% correct, since I haven' | ||
+ | |||
+ | ===== The noise-waveform ===== | ||
+ | |||
+ | The next step is to determine whether the noise waveform loops, since this knowledge is useful in respect to an algorithm based on manipulating an internal register. If the algorithm is based upon manipulation an internal register (by doing for instance shifting and exclusive-or), | ||
+ | |||
+ | The program " | ||
+ | < | ||
+ | void Loopchecker() { | ||
+ | start_noise-waveform(); | ||
+ | |||
+ | /* We want to sample *inside* the potential loop */ | ||
+ | wait_a_while(); | ||
+ | |||
+ | /* Sample 256 values */ | ||
+ | for (i=0; | ||
+ | data[i]= peek($d41b); | ||
+ | |||
+ | /* Sample until those 256 values arrive again */ | ||
+ | do { | ||
+ | i=0; | ||
+ | while (peek($d41b) = data[i]) do | ||
+ | i=i+1; | ||
+ | while (i256); | ||
+ | end; | ||
+ | </ | ||
+ | |||
+ | This program will terminate if a sequence of 256 recorded bytes will appear again later. If the 256 bytes reappear, we can be quite certain that the waveform loops, since the chance of this happening with a totally random source is infinitely small (well below 1e-500). Marko Makela and I (Asger Alstrup) hacked a loopchecker together over the IRC, and our results with a 16 cycle sampling-rate was that the computer terminated after approximately 2 minutes and 15 seconds. However, our results weren' | ||
+ | |||
+ | The " | ||
+ | < | ||
+ | (272 sec * 980000 cyc/sec)/32 cyc/bytes ~= 8.0 Megabytes. | ||
+ | </ | ||
+ | |||
+ | Thirdly, this implies an internal register length of log2(8.0 MB)= 23 bits. And finally, we must admit that it would be a difficult task to sample the entire 8 MB on a c64 with only 64KB memory. It is possible to sample the lot in chunks and saving it on multiple disks, but this is not a trivial task. Notice that the data probably can't be packed much since they are random. It should be mentioned that by changing the delayloop after the noisewaveform is selected, it can be demonstrated that the waveform indeed loops after 8 megabytes of data nomatter where in the waveform cycle you are. | ||
+ | |||
+ | With the implication of an internal register of 23 bits, the next step is to find a pattern in the data which might hint the algorithm used to produce the data. Since we know a shifting and eor scheme can be used, it might be useful to take a look at the data in binary: | ||
+ | < | ||
+ | 11111110 | ||
+ | 11111100 | ||
+ | 11111100 | ||
+ | 11111100 | ||
+ | 11111000 | ||
+ | 11111000 | ||
+ | 11111000 | ||
+ | 11111000 | ||
+ | 11110000 | ||
+ | 11110000 | ||
+ | 11100000 | ||
+ | 11100000 | ||
+ | 11100000 | ||
+ | 11000000 | ||
+ | 11000000 | ||
+ | 11000000 | ||
+ | 11000000 | ||
+ | 10000001 New value for bit 0! | ||
+ | 10000001 | ||
+ | 00000011 | ||
+ | 00000011 | ||
+ | 00000011 | ||
+ | 00000110 | ||
+ | 00000110 | ||
+ | 00000100 | ||
+ | 00000100 | ||
+ | 00001100 | ||
+ | 00001000 | ||
+ | 00011000 | ||
+ | 00011000 | ||
+ | 00011000 | ||
+ | 00110000 | ||
+ | 00110000 | ||
+ | ... | ||
+ | </ | ||
+ | It should be quite clear that some kind of shifting scheme is used. Further examination of the data suggests that the internal register indeed is on 23 bits, where the mapping between the 8 bits in the output and the internal 23 bit register is like this: | ||
+ | < | ||
+ | Internal register bit number | ||
+ | 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 | ||
+ | Output bit from register $1b | ||
+ | 7 | ||
+ | </ | ||
+ | The first data from the output can be reproduced with this layout if the internal register is leftshifted, | ||
+ | < | ||
+ | 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 | ||
+ | </ | ||
+ | Now we need to explain the new bits appearing in bit 0 of the data. Even further examination of the data implies that an eor-gate is used to feed bit 0 of the internal register. I have found that the bits 22 and 17 of the internal register provides the feed for bit 0 through an eor-gate, which gives us the entire mechanism: | ||
+ | < | ||
+ | 22-21-20-19-18-17-16-15-14-13-12-...-8-7-6-5-4-3-2-1-0 | ||
+ | | | ||
+ | +----> | ||
+ | </ | ||
+ | |||
+ | This can also be expressed as a C-program like this: | ||
+ | < | ||
+ | /* Test a bit. Returns 1 if bit is set. */ | ||
+ | long bit(long val, byte bitnr) { | ||
+ | return (val & (1<< | ||
+ | } | ||
+ | |||
+ | |||
+ | /* Generate output from noise-waveform */ | ||
+ | void Noisewaveform { | ||
+ | long bit22; /* Temp. to keep bit 22 */ | ||
+ | long bit17; /* Temp. to keep bit 17 */ | ||
+ | |||
+ | long reg= 0x7ffff8; /* Initial value of internal register*/ | ||
+ | |||
+ | /* Repeat forever */ | ||
+ | for (;;;) { | ||
+ | |||
+ | /* Pick out bits to make output value */ | ||
+ | output = (bit(reg, | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | /* Save bits used to feed bit 0 */ | ||
+ | bit22= bit(reg, | ||
+ | bit17= bit(reg, | ||
+ | |||
+ | /* Shift 1 bit left */ | ||
+ | reg= reg << 1; | ||
+ | |||
+ | /(* Feed bit 0 */ | ||
+ | reg= reg | (bit22 ^ bit17); | ||
+ | }; | ||
+ | }; | ||
+ | </ | ||
+ | |||
+ | Every loop in the above program provides a new value for the noise waveform in the variable " | ||
+ | |||
+ | When it comes to the quality of the pseudo-random generated numbers, one thing is clear: Since all bit-patterns of the 23 bits are generated, all values in the 8 bit output will appear equally many times over time. Furthermore, | ||
+ | |||
+ | ===== Programs in assembler ===== | ||
+ | < | ||
+ | ;" | ||
+ | ; | ||
+ | |||
+ | * = $1000 | ||
+ | |||
+ | freq = $8000 ; | ||
+ | |||
+ | cycle jsr init ;Init screen | ||
+ | |||
+ | lda #$08 ;Set testbit to reset the waveform. | ||
+ | sta $d412 | ||
+ | |||
+ | jsr pause ;Give it time to settle - nescessary on my machine. | ||
+ | |||
+ | lda #<freq ;Set frequency | ||
+ | ldx #>freq | ||
+ | sta $d40e | ||
+ | stx $d40f | ||
+ | |||
+ | ;Set up REU | ||
+ | |||
+ | ldx # | ||
+ | ldy #>$d41b | ||
+ | stx $df02 | ||
+ | sty $df03 | ||
+ | |||
+ | lda #$00 ;Record into $0000 | ||
+ | sta $df04 | ||
+ | sta $df05 | ||
+ | sta $df06 ;in bank 0. | ||
+ | |||
+ | sta $df07 ; | ||
+ | sta $df08 | ||
+ | |||
+ | lda #$00 ;No interrupts from the REU. | ||
+ | sta $df09 | ||
+ | |||
+ | lda # | ||
+ | sta $df0a | ||
+ | |||
+ | lda #$80 | ||
+ | ldx # | ||
+ | |||
+ | ;start waveform and do the sampling | ||
+ | |||
+ | sta $d412 ; | ||
+ | stx $df01 ;and start recording. | ||
+ | jmp done ;After sampling, wrap it up | ||
+ | |||
+ | |||
+ | ; | ||
+ | ; | ||
+ | |||
+ | * = $1100 | ||
+ | |||
+ | block = $2000 | ||
+ | |||
+ | loop jsr init ;Init screen | ||
+ | |||
+ | lda #$08 ;Set testbit to reset the waveform. | ||
+ | sta $d412 | ||
+ | |||
+ | jsr pause ;Give it time to settle - nescessary on my machine. | ||
+ | |||
+ | lda #$00 ;Set frequency to $8000 | ||
+ | sta $d40e | ||
+ | lda #$80 | ||
+ | sta $d40f | ||
+ | |||
+ | lda #$80 ;Start noise-waveform | ||
+ | sta $d412 | ||
+ | |||
+ | jsr pause ;Wait a while, so we get well into the waveform | ||
+ | |||
+ | inc $d020 ; | ||
+ | |||
+ | ldx #0 ;Sample 256 bytes at 32 cycles | ||
+ | l1 lda $d41b | ||
+ | sta block,x | ||
+ | bit $ffff | ||
+ | bit $ffff | ||
+ | bit $ffff | ||
+ | bit $ffff | ||
+ | nop | ||
+ | inx | ||
+ | bne l1 | ||
+ | |||
+ | nop ;Wait exactly 1 value | ||
+ | bit $ea | ||
+ | bit $ffff | ||
+ | bit $ffff | ||
+ | |||
+ | l2 ldy #$1d ;See if the 256 bytes repeat | ||
+ | ldx #$00 | ||
+ | l3 lda $d3ff, | ||
+ | bit $ffff | ||
+ | bit $ffff | ||
+ | bit $ffff | ||
+ | bit $ffff | ||
+ | cmp block,x | ||
+ | bne l2 | ||
+ | inx | ||
+ | bne l3 | ||
+ | |||
+ | jmp done ;And exit if they do | ||
+ | |||
+ | ;Misc subroutines | ||
+ | ; | ||
+ | |||
+ | * = $1800 | ||
+ | |||
+ | ;Init screen | ||
+ | |||
+ | init sei ; | ||
+ | lda # | ||
+ | sta $d015 | ||
+ | in1 lda $d011 ;and screen. | ||
+ | bpl in1 | ||
+ | and #$ef | ||
+ | sta $d011 | ||
+ | rts | ||
+ | |||
+ | ;Wait 50 frames so waveform can be reset | ||
+ | |||
+ | pause ldx #50 | ||
+ | p1 bit $d011 | ||
+ | bpl p1 | ||
+ | p2 bit $d011 | ||
+ | bmi p2 | ||
+ | dex | ||
+ | bne p1 | ||
+ | rts | ||
+ | |||
+ | ;Wraps it up | ||
+ | |||
+ | done lda $d011 ; | ||
+ | ora #$10 | ||
+ | sta $d011 | ||
+ | |||
+ | cli ;and exit. | ||
+ | rts | ||
+ | </ | ||
+ | Output from " | ||
+ | |||
+ | Value (number of times) $fe ($16), $fc ($30), $f8 ($40), $f0 ($20), $e0 ($30), $c0 ($40), $81 ($20), $03 ($30), $06 ($20), $04 ($20), $0c ($10), $08 ($10), $18 ($30), $30 ($20) ... | ||
+ | |||
base/noise_waveform.txt · Last modified: 2015-04-17 04:33 by 127.0.0.1