Viznut's waves?

Basic and Machine Language

Moderator: Moderators

User avatar
R'zo
Vic 20 Nerd
Posts: 514
Joined: Fri Jan 16, 2015 11:48 pm

Viznut's waves?

Post by R'zo »

I've been working on routines for producing Viznuts pwp waveforms. I can't seem to make a complete sense out of his code but i've been building my own code based of off his theory. I have gotten test demos to work ( so far I've only tested $900c) but I have a few questions.

Here's the original code and theory...

Code: Select all

15 NEW WAVEFORMS FOR THE VIC-20

Written by viznut/pwp in the early June 2003

Most VIC-20 people assume that the waveform of the VIC-I pulse channels
($900a..$900c) is generated by a simple flip-flop. This does not seem to be
the case.

By changing an oscillator's "on/off" bit very rapidly it is possible to get
it in states that give out waveforms different to the normal 1:1 pulse
waveform you have learned to expect from the VIC-20.

An example:

	; assume that the content of $900c has been $7e for a while
	; (at least a couple of hundred cycles)

	ldx #$7e   ; oscillator off, maximum shift rate (4 clocks)
	ldy #$fe   ; oscillator on, maximum shift rate (4 clocks)
	lda #152   ; the initial frequency of the sound (can be any)

	sei        ; the following stuff needs exact timing
	sty $900c  ; push 1 to oscillator
	sty $900c  ; push 1 to oscillator
	stx $900c  ; push 0 to oscillator
	sta $900c  ; and let it rotate on its own
	cli

This gives out the waveform later referred to as the "110" wave.

The following is my current theory about the VIC-I sound:

  Each VIC-I voice has:
  - an 8-bit shift register
  - a 7-bit counter
  - a 7-bit frequency value
  - on/off bit

  The clock rates for each voice:
  - $900c: cpuclock/4 
  - $900b: cpuclock/8
  - $900a: cpuclock/16
  - $900d: cpuclock/32 (?)

  When a voice gets a clock cycle:
  - The counter value is incremented by 1.
  - If the incremented value is all ones ($7F), the counter is reset
    to the frequency value. Also, the channel's shift register is shifted.

  The shift function for pulsewave channels:
  - Shift all bits left by one position.
  - If the voice is ON: the lowest bit becomes the complement of the
    previous top bit.
  - If the voice is OFF: the lowest bit becomes zero.
  - The lowest bit of the shift register is the output
  - In formal C-like code:
    reg = (reg<<1) | ( ((reg>>7)^1) & voiceon);

  The shift function for noise is probably something akin to that of the TED
  chip used in the C-16 and the Plus-4.

Waveforms:

  These are the 16 possible waveforms according to the theory:

  NAME     SHAPE

  default  0000000011111111
  "10"     0000001011111101
  "100"    0000010011111011
  "110"    0000011011111001
  "1000"   0000100011110111
  "1010"   0000101011110101
  "1011"   0000110011110011
  "1110"   0000111011110001
  "10010"  0001001011101101
  "10100"  0001010011101011
  "10110"  0001011011101001
  "11000"  0001100011100111
  "11010"  0001101011100101
  "100100" 0010010011011011
  "101010" 0010101011010101
  "101100" 0010110011010011

A short generic routine for setting any shift register value for any pulse
channel in about 150 cpu clocks. Use it freely.

setwave:
	; USAGE: y = channel ($0a..$0c)
	;        x = initial frequency
	;        a = shift register contents
	;
	; WARNING for purists: self-modifying code, illegal opcodes.
	;
	; code align assertion: make sure that the loop is within a page.
	; oscillator assertion: make sure that the channel has been at $7e
	; for some time before calling this function.
	; put TMP and TMP2 in the zero page.

	stx .initfreq	; 4

	sty .ch0	; 4
	sty .ch1	; 4
	ldx .ldfqmasks-$a,y ; 4
	sta TMP		; 3

	ora #$7f	; 2

	axs $900c	; 4  [$900c] = a AND x
	.ch0=*-2
	sty TMP2	; 3
	ldy #7		; 2

.l0:	lda #$7f	; 2
	aso TMP		; 5  asl tmp; a = [tmp] OR $7f
	axs $900c	; 4  [$900c] = a AND x
	.ch1=*-2
	dey		; 2
	bne .l0		; 3

	lda #128	; 2
	.initfreq=*-1
	nop		; 2
	ldy TMP2	; 3
.noset:	sta $9000,y	; 5

	rts		; 6	total clocks 11+4+3+2+16*7+16+6 eq 154

.ldfqmasks:
     !byte $fe	; $900a - 1 x 16 clocks/bit
     !byte $fd  ; $900b - 2 x  8 clocks/bit
     !byte $fb  ; $900c - 4 x  4 clocks/bit
My first question concerns the wave forms.
All of his documentation state that the sound channels use an 8-but shift register. The waveforms that he provides are all 16 bits long.

Are all 16 bits necessary to pound the 8-bit shift registers?
It looks like the second byte of each form is just the.
inversed form of the first.
Using all 16 has an effect as well as does using each half.
alone. I just wanted to make sure that I am achieving the.
desired effect.

Why did Viznut never implement the noise channel in any of his code?
Could he not get it to work?
Did it not produce his desired results?
Did it just not fit into his code format?

This question is on the Vic chip itself.
Why use 8- bit shift registers if your not going to make.
them controllers. Why not use a simple on/off switch?
Is it possible that control registers could have been.
intended but never implemented? Or implemented and.
just never documented?
documented?

Edit: this document had a little more detail. I found these documents while digging around in the midicart documentation

Code: Select all

> Users want to be able to use your waveforms over MIDI, so I'm
> implementing them as a Program Change.  So here are my questions about
> your setwave code...
>
> 1) What exactly needs to be passed in A?  The comments just say "a =
> shift register contents".  Should A be the value represented by the
> binary value of the "waveform name"?  i.e. if I want the "11010"
> waveform, do I pass in $1A?
>
> Is it valid to pass other values, or should I force it so that only
> those waveforms listed in your document are allowed?

You can pass any values. There are 16 waveforms, and each waveform cycles
thru 16 different shift register states, so each of the 16x16=256 possible
shift register contents is a valid state in one of the possible waveforms.
The "waveform names" I've given are just the shortest possible binary
sequences that set up each waveform.

And yes, you can put these binary numbers directly into A when using the
routine.

> 2) You have a comment "make sure that the channel has been at $7e for
> some time before calling this function.".  Does that imply some
> silence before changing waveforms?  Does this have to be repeated for
> every new note?

It implies that the shift register must be cleared before setting a new
waveform. The easiest way to clear the shift register is switching the
channel off for a short while. It is also important that the shift rate is
fast enough ($7E is the fastest setting).

Of course, it IS possible to change the waveform without fulfilling these
requirements, but in this case the exact change is not predictable. Still,
there is some VIC-20 music (e.g. Orb Megademo, Summer Lameness) that do this
kind of "random unpredictable tweaks" constantly in order to create a unique
kind of sound, so you might want to give the users this possibility as well.

When changing the note: if you just change the frequency (without turning
the channel off), then it is not necessary to set the waveform again.
However, if you have any silence before the new note, then it is necessary
to call the waveform routine again.

viznut

--------------------------------------------------------

> How long should I wait for?  "A short while" isn't very precise ;-)
> (How many NOPs would I need?)

It depends on the channel and the pitch it's playing before setting it to
$7E. I once calculated the worst case as being 2160 clock cycles (for the
slowest channel ($900a) on the lowest pitch), but I haven't verified it in
practice as my players never need to change the waveform very often.

Anyway, if you want to be sure you don't wait any longer than you need to,
here's the formula I used:

  offtime in clock cycles = clocks_per_shift * (wavelength + 7)

where clocks_per_shift is 16, 8 or 4 depending on the channel (alto, tenor,
soprano) and wavelength is ((126-PEEK(3687X))AND127)+1.

viznut
Last edited by Mike on Fri Jul 16, 2021 1:14 am, edited 2 times in total.
Reason: spelling of "visnuts" in topic title corrected
R'zo
I do not believe in obsolete...
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Viznut's waves?

Post by chysn »

I'm more familiar with Viznut's bytebeat work than VIC-20 stuff, and it's high time I changed that.

I think I know the answers to a couple of your questions, though.

Why not use the noise register? The noise register won't be as predictable as the square wave registers are, and it seems like cycle-exact changes of the pulse is at the heart of these techniques.

Why not use a simple on/off switch? That looks exactly like what the shift register is, a sequenced on/off switch. Different patterns yield different results.

But I'm going to play around with this for a bit, and come back and talk more about it later.
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
R'zo
Vic 20 Nerd
Posts: 514
Joined: Fri Jan 16, 2015 11:48 pm

Re: Viznut's waves?

Post by R'zo »

chysn wrote: Thu Jul 15, 2021 3:03 pm
Why not use a simple on/off switch? That looks exactly like what the shift register is, a sequenced on/off switch. Different patterns yield different results.
Yes, but if it is not intended in the design of the chip to be able to alter the shift register and the pattern is simple 1010etc. pattern then why use a shift register. It would seem as if the existence of the shift registers would imply that they were intended to be altered.
R'zo
I do not believe in obsolete...
User avatar
R'zo
Vic 20 Nerd
Posts: 514
Joined: Fri Jan 16, 2015 11:48 pm

Re: Viznut's waves?

Post by R'zo »

I've been studying his actual code right now and I'm staring to understand it. This leads me to a few more questions.

If the 900c requires a drop every 4 cycles and 900b 8 then how does his loop with 16 cycles achieving this achieve this?

l0: lda #$7f ; 2
aso TMP ; 5 asl tmp; a = [tmp] OR $7f
axs $900c ; 4 [$900c] = a AND x
.ch1=*-2
dey ; 2
bne .l0 ; 3

Am I misunderstanding the timing?

In this line...
ldx .ldfqmasks-$a,y ; 4
I can't seem to understand why hes subtracting 10.

What is going on here? I am unfamiliar with !byte.
R'zo
I do not believe in obsolete...
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Viznut's waves?

Post by chysn »

R'zo wrote: Thu Jul 15, 2021 3:41 pm In this line...
ldx .ldfqmasks-$a,y ; 4
I can't seem to understand why hes subtracting 10.

What is going on here? I am unfamiliar with !byte.
!byte is literally a byte value, usually used for tables.

Subtraction is an offset here because the value of Y (channel) is between $a and $c, and we don’t want to waste bytes on values that won’t be used.

I’m workshopping the code right now, too, but pausing to make dinner. BRB!
VIC-20 Projects: wAx Assembler, TRBo: Turtle RescueBot, Helix Colony, Sub Med, Trolley Problem, Dungeon of Dance, ZEPTOPOLIS, MIDI KERNAL, The Archivist, Ed for Prophet-5

WIP: MIDIcast BASIC extension

he/him/his
User avatar
R'zo
Vic 20 Nerd
Posts: 514
Joined: Fri Jan 16, 2015 11:48 pm

Re: Viznut's waves?

Post by R'zo »

So !byte $xx would be the same as byte $xx in cbm org studio?
R'zo
I do not believe in obsolete...
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Viznut's waves?

Post by chysn »

R'zo wrote: Thu Jul 15, 2021 4:33 pm So !byte $xx would be the same as byte $xx in cbm org studio?
Yeah, seems likely. Or .byte $xx in XA or :xx in wAx :)
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Viznut's waves?

Post by chysn »

So... what's it supposed to do? When I run the subroutine, it makes a tone, but it sounds exactly like a normal VIC-20 tone, regardless of the shift register value...?

I'm doing all this on real hardware.
User avatar
R'zo
Vic 20 Nerd
Posts: 514
Joined: Fri Jan 16, 2015 11:48 pm

Re: Viznut's waves?

Post by R'zo »

chysn wrote: Thu Jul 15, 2021 5:35 pm So... what's it supposed to do? When I run the subroutine, it makes a tone, but it sounds exactly like a normal VIC-20 tone, regardless of the shift register value...?

I'm doing all this on real hardware.
I have not yet tested his code since I can't assemble it. Now that I understand all of the elements I can reproduce it in cbm. I'll work on that later tonight. I wonder if it is unfinished. It seems to me that the loop is to long for everything but the low voice.

I have some simple test routines that i've been playing around with that I'll post later when I'm home.
R'zo
I do not believe in obsolete...
User avatar
chysn
Vic 20 Scientist
Posts: 1205
Joined: Tue Oct 22, 2019 12:36 pm
Website: http://www.beigemaze.com
Location: Michigan, USA
Occupation: Software Dev Manager

Re: Viznut's waves?

Post by chysn »

Okay! It says something that, after like 40 years, the VIC-20 can still blow my mind. Holy $#!T!

I skipped the cross-assembler on this project, and worked directly with the VIC-20, because I wasn't sure I could trust VICE. But I'll check that out tomorrow. Meanwhile, here's a BASIC-with-wAx program that demonstrates setting waveforms (also attached as a PRG file, which is only superficially different):

Code: Select all

100 *- ;CLEAR SYMBOLS
110 @033C STX -F
120 @*    LDX #$FD
125 @*    STA $07
130 @*    ORA #$7F
135 @*    SAX $900B
140 @*    LDY #7
145 @* -@ LDA #$7F
150 @*    SLO $07
155 @*    SAX $900B
160 @*    DEY
165 @*    BNE -@
170 @*    :A9 ;LDA#
175 @* -F :FF ;FREQ
180 @*    STA $900B
190 @*    RTS
200 *  ;SHOW SYMBOLS
210 @900B :7E 00 00 0A
230 ;00 B0
240 SYS 828
250 GET A$:IFA$="" THEN 250
255 @900B :7E
260 ;24 B0
270 SYS 828
Since this was a proof-of-concept, I'm just using Voice 2 in this example, so the code and data associated with channels has been omitted. The subroutine at 828 ($33c) just requires the shift register to be set in A and the frequency in X.
  • Line 100 clears the symbol table, to free up labels.
  • Lines 110-190 assemble the code at $033c
  • For illegal opcodes, note that wAx knows AXS as SAX, which is just fun to say aloud, while ASO is SLO.
  • Line 200 shows the symbol table, a diagnostic measure
  • Line 210 sets Voice 2 to $7e, and sets the Volume to 10/15
  • Line 230 sets shift register (A) to $00 and frequency (X) to $b0
  • Line 240 runs the routine with those parameters. The shift register of $00 corresponds to the VIC-20's default waveform, so it'll just sound like a VIC-20 at this point
  • Line 250 waits for you to listen to the sound, then press a key
  • After you press a key, Voice 2 is reset back to $7e (per the "oscillator assertion" comment)
  • Line 260 sets a different shift register value (corresponding to the first eight bits of the "100100" waveform in the table), at the same frequency
  • Line 270 plays the new tone. It'll be distinctly different now, but at the same pitch. This is the whole point of the thing!
You can now continue to update $900b with different frequencies for as long as you want, and it'll continue using the same waveform. But if you update $900b with a frequency of $7e, it'll shut off the register and reset the waveform back to "default."

So, this is pretty amazing. I'm grateful that you introduced me to this, Ryan, and it's what I'll be doing this weekend.
Attachments
viz-v2.prg.zip
(802 Bytes) Downloaded 68 times
Last edited by chysn on Sat Jul 17, 2021 7:30 pm, edited 1 time in total.
User avatar
mathom
Vic 20 Dabbler
Posts: 80
Joined: Wed Aug 07, 2019 11:37 am
Location: Centennial, Colorado
Occupation: Software Engineer

Re: Viznut's waves?

Post by mathom »

Wow! It sounds very cool. The tone is reminiscent of FM synthesis which I suppose stands to reason? Seems to work on both real hardware and vice.
...mathom...
User avatar
Mike
Herr VC
Posts: 4841
Joined: Wed Dec 01, 2004 1:57 pm
Location: Munich, Germany
Occupation: electrical engineer

Re: Viznut's waves?

Post by Mike »

R'zo wrote:If the 900c requires a drop every 4 cycles and 900b 8 then how does his loop with 16 cycles achieving this achieve this?
The effective length of the shift register is 16 (i.e. the normal square waveform repeats every 16 ticks).

With $7E in $900C, the tick length is 4 CPU cycles, and an unrolled loop with STx instructions is the fastest way to 'drop' a selection of the bits in the 'high half' of the square wave. One could also use a timing that does the drops every 16xN+1 ticks, i.e. let one or more periods, plus 1 tick, run by to do the next drop. Yet another way is using a period for the drops that has no common divisor with 16. In that case, the 'drop definition' is scrambled, but it is still possible to select the drops freely.

BTW, there exist only 12 distinct wave forms of that kind (including the square wave). Two of the 14 originally proposed wave forms are just mirror images of others that are already present in the set of the 12 other wave forms, and they sound exactly the same.
User avatar
R'zo
Vic 20 Nerd
Posts: 514
Joined: Fri Jan 16, 2015 11:48 pm

Re: Viznut's waves?

Post by R'zo »

here are the test routines that i have been playing around with. Both only use 1 wave form on the high voice. I have only tested them in vice so far.

This one all 16 bits of the wave form. It is somewhat consistent but not 100%

Code: Select all

*=$a000

lo        = $900a
mid       = lo+1
hi        = lo+2
noise     = lo+3
vol       = lo+4
off       = $7e
on        = $fe
init      = $c3
ofx       = $8e
onx       = $8c


c_sound   lda   #7
          sta   vol
          ldy   255
          nop
          nop
          nop
          nop
loop      dey
          beq   loop
          ldy   #on        
          ldx   #off      
          lda   #init
c_sound_0 stx   hi      ;4
c_sound_1 stx   hi      ;4      00100100 11011011
c_sound_2 sty   hi      ;4
c_sound_3 stx   hi      ;4
c_sound_4 stx   hi      ;4
c_sound_5 sty   hi      ;4
c_sound_6 stx   hi      ;4
c_sound_7 stx   hi      ;4
c_sound_8 sty   hi      ;4
c_sound_9 sty   hi      ;4
c_sound_a stx   hi      ;4
c_sound_b sty   hi      ;4
c_sound_c sty   hi      ;4
c_sound_d stx   hi      ;4
c_sound_e sty   hi      ;4
c_sound_f sty   hi      ;4
          sta   hi      ;4        
          rts
t16.7z
(173 Bytes) Downloaded 46 times
This one only uses the first 8 bits and is inconsistent.

Code: Select all

*=$a000

lo        = $900a
mid       = lo+1
hi        = lo+2
noise     = lo+3
vol       = lo+4
off       = $7e
on        = $fe
init      = $c3
onx       = $8e
ofx       = $8c


c_sound   lda   #7
          sta   vol
          ldy   255
          nop
          nop
          nop
          nop
loop      dey
          beq   loop
          ldy   #on        
          ldx   #off      
          lda   #init
c_sound_0 stx   hi      ;4
c_sound_1 stx   hi      ;4      00100100 11011011
c_sound_2 sty   hi      ;4
c_sound_3 stx   hi      ;4
c_sound_4 stx   hi      ;4
c_sound_5 sty   hi      ;4
c_sound_6 stx   hi      ;4
c_sound_7 stx   hi      ;4
          sta   hi      ;4        
          rts
t1.7z
(170 Bytes) Downloaded 43 times
R'zo
I do not believe in obsolete...
User avatar
Noizer
Vic 20 Devotee
Posts: 297
Joined: Tue May 15, 2018 12:00 pm
Location: Europa

Re: Viznut's waves?

Post by Noizer »

I gave a hint some time ago, if you really want to find out then use $900D instead.
BR
Valid rule today as earlier: 1 Byte = 8 Bits
-._/classes instead of masses\_.-
User avatar
R'zo
Vic 20 Nerd
Posts: 514
Joined: Fri Jan 16, 2015 11:48 pm

Re: Viznut's waves?

Post by R'zo »

Noizer wrote: Fri Jul 16, 2021 10:10 am I gave a hint some time ago, if you really want to find out then use $900D instead.
BR
What part are you hinting to?
R'zo
I do not believe in obsolete...
Post Reply