     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
