Created
March 9, 2020 03:06
-
-
Save cesarmiquel/ea30327f2249460bba384b90f6c5b685 to your computer and use it in GitHub Desktop.
Source code 303 demo by 4mat - http://4matprojects.blogspot.com/2020/03/c64-303.html
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; Source code to 303 dmeo by 4mat 2020 (http://4matprojects.blogspot.com/2020/03/c64-303.html) | |
; 303 style by 4mat 2020 | |
; Type sys 49152 to play. | |
; Written using Dasm assembler. Should be mostly compatible with other assemblers except: | |
; + The processor line is probably Dasm only unless your assembler handles multiple processors. | |
; + org might be replaced by * or something else. | |
; + Some assemblers use !byte or something else instead of .byte, see your docs. | |
; Memory map at bottom of source. | |
; Set start address to $c000. (49152) | |
org $c000 | |
processor 6502 | |
; Init Intro. | |
; Disable interrupts while we do our setup. | |
sei | |
; Copy song data to zero page. | |
; This saves a bit of memory as some assembler commands will only use 2 bytes instead | |
; of 3 if they were in 'normal' memory. | |
ldy #$00 | |
ldx #$30 | |
argha | |
tya | |
sta $60,x | |
lda datas-1,x | |
sta $0f,x | |
dex | |
bne argha | |
; Setup soundchip. | |
; By default I've set all channels to use triangle and sync/ring-mod for the first cycle. | |
setsound | |
lda #$17 | |
sta 54276,y | |
lda #$08 | |
sta 54275,y | |
lda #$00 | |
sta 54277,y | |
lda $1d,x | |
sta 54278,y | |
lda $10,x | |
sta $6b,x | |
lda $12,x | |
sta 54295,x | |
inx | |
clc | |
tya | |
adc #$07 | |
tay | |
cpy #$15 | |
bne setsound | |
; Point hardware interrupt at the music player section. | |
lda #<musicloop | |
sta $0314 | |
lda #>musicloop | |
sta $0315 | |
; Enable interrupts so we're good to start. | |
cli | |
; Main loop. | |
; Only the visuals are updated here. This part runs as fast as the spare CPU | |
; time will allow. | |
loop | |
; Take the current filter cut-off value and add 65 to it (so it's in the Petscii area | |
; of the character set) | |
lda $62 | |
adc #$41 | |
; Use the random number generator to read the oscillator value from channel 3 and use | |
; this as the X offset position. Because channel 3 is where the drums are, for snares | |
; and hihats this will be using the noise waveform. | |
ldx $d41b | |
; Store the petscii char into the screen area. This is repeated 4 times to fill the 4 | |
; pages of the screen. I used a slight offset from the usual $0400 start position to | |
; move the visuals around a bit. | |
sta $03e8,x | |
sta $04e8,x | |
sta $05e8,x | |
sta $06e8,x | |
jmp loop | |
; Music player. | |
musicloop | |
; Decrease tempo tick, if it goes minus (#$ff) we need a new note, otherwise we can jump | |
; to the instrument update part. | |
dec $60 | |
bmi musicloop2 | |
bpl updatedrums | |
; Get new note in the pattern. | |
musicloop2 | |
; Reset tempo tick to full. (in our case #$06 which is about 125 bpm) | |
lda #$06 | |
sta $60 | |
; Make new value to add to filter cut-off for this note. | |
; Using the current raster beam value ($d012) as an offset, read a value from the | |
; Kernal ROM and use only the lower 4-bit value. Then add the current number of | |
; loop cycles ($6b) to make each value slightly different. | |
ldy $d012 | |
lda $e144,y | |
and #$0f | |
adc $6b | |
sta filtsweep+$01 | |
; Get new note values, the current pattern position stored in $61. | |
ldy $61 | |
; First the main melody, using the lower 4-bits from some of the Kernal ROM to only | |
; use bass values between 0 (silence) and 15. The extra 'eor #$03' is more for | |
; personal taste with the different melodies. | |
chan1 | |
lda $f704,y | |
and #$0f | |
eor #$03 | |
sta 54273 | |
; Now the sync/ring-mod channel, using data from the BASIC ROM, but only taking | |
; the low 6-bits. | |
chan2 | |
lda $a340,y | |
and #$3f | |
sta 54273+7 | |
; Set new Drum | |
; This checks against 4 possible BIT values to see if a drum needs to be played. | |
; ($40 = Bass Drum, $08 = Snare , $02 = Hihat , $01 = Rest) | |
noresetsq | |
ldx #$00 | |
drumcheck | |
; Get next position value from drum rhythm table. | |
lda $30,y | |
; Check if value matches the current BIT value indexed. | |
and $20,x | |
beq nobit | |
; If it does this means we have a new drum to play. | |
; Firstly set the waveform from that table. Also store it at $66 so we can apply | |
; note off later. | |
lda $24,x | |
sta 54276+14 | |
sta $66 | |
; Set the drum pitch, this is placed directly in a variable as we do work on this | |
; value when adding the pitch sweep. | |
lda $27,x | |
sta $67 | |
; Set the drum pitch sweep. | |
lda $2a,x | |
sta $68 | |
; Finally set the timer value before applying the note-off on the drum. | |
lda $2d,x | |
sta $69 | |
nobit | |
dex | |
bpl drumcheck | |
; Increase the pattern position by 1 and AND the value by #$0f so it's always | |
; between 00-15. | |
iny | |
tya | |
and #$0f | |
sta $61 | |
; Check if the value is 00, if not we don't need to decrease the amount of pattern | |
; cycles yet. | |
bne noupdate | |
; Decrease the amount of pattern cycles and check if this is #$ff yet. If not we | |
; don't need to create a new pattern yet. | |
dec $6b | |
bpl noupdate | |
; When the pattern cycles are complete it's time to create a new pattern. | |
; Firstly we do some self-modifying code to the initial value of the drum check loop. | |
; This means we don't always get the same drum beat by dropping out checks for the | |
; snare and hi-hats. | |
dec noresetsq+$01 | |
lda noresetsq+$01 | |
and #$03 | |
sta noresetsq+$01 | |
; We also use this value to change the melody line's waveform, from the table at $15-$18. | |
tax | |
lda $15,x | |
sta 54276 | |
; We also change the amount of pattern cycles for the next pattern from the | |
; table at $19-$1c. | |
lda $19,x | |
sta $6b | |
; Now we do some more self-modifying code to change the memory position to read the | |
; note data from. This increase the high memory value by one for the main melody and | |
; the ring-tone channel. This does mean that eventually both values will reach past the | |
; end of memory and reset back to $0000. As mentioned in the docs as this was for a video | |
; I didn't add any checking for this occurance, however the older version of the player resets | |
; the machine to avoid this happening. | |
inc chan1+$02 | |
inc chan2+$02 | |
; This resets the starting value of the filter cut-off value. It works very similar to | |
; the filter sweep setup though we start with a 7-bit value, divide it by half and then add | |
; the current pattern position value to it. | |
noupdate | |
ldy $d012 | |
adc $e948,y | |
and #$7f | |
lsr | |
adc $61 | |
sta $62 | |
; This is where the player falls through to on every frame. This updates the filter and | |
; drums and then sends an acknowledgement to the timer system that this routine has finished. | |
; Check the tempo tick against the current drum's note-off value. If it's the same switch off | |
; the waveform's gate (bit 1) so the release part of the ADSR gets activated. | |
updatedrums | |
lda $60 | |
cmp $69 | |
bne nodrumgate | |
dec $66 | |
lda $66 | |
sta 54276+14 | |
nodrumgate | |
; Add the drum's pitch sweep value to the current pitch and store it in channel 3's high pitch | |
; register. Note that we don't do the math directly on the register because the SID is write | |
; only when enabled. | |
clc | |
lda $67 | |
adc $68 | |
sta $67 | |
sta 54273+14 | |
; Set current filter cut-off value to the cut-off register. | |
lda $62 | |
sta 54294 | |
; Decrease the cut-off value by the current filter sweep value. (note this was set in | |
; self-modifying code earlier) If the value is already below zero don't store it in the | |
; variable so it only stays at this value. | |
filtsweep | |
sbc #$00 | |
bmi filtnot | |
sta $62 | |
; End of music driver, call to IO system that we've ended our routine. | |
; As we have the full default system enabled we need to use $ea31 rather than the | |
; less cpu-heavy $ea81 | |
filtnot | |
jmp $ea31 | |
datas | |
.byte $05,$07 | |
siddata | |
.byte $f3,$1f,$00 | |
basswaves | |
.byte $11,$21,$41,$21 | |
length | |
.byte $01,$03,$03,$03 | |
vols | |
.byte $a9,$3c,$79 | |
btt | |
.byte $40,$02,$08,$00 | |
wav | |
.byte $41,$81,$81 | |
not | |
.byte $0a,$ff,$20 | |
plu | |
.byte $ff,$fe,$fc | |
del | |
.byte $03,$02,$01 | |
beat | |
.byte $40,$01,$02,$01,$08,$01,$02,$01,$40,$01,$02,$01,$08,$01,$02,$40 | |
; Memory map: | |
; $10 = Initial pattern cycles value for first pattern. (datas) | |
; $12 = SID Filter values for filter type/volume and resonance/channel allocation. (siddata) | |
; $15 = Melody line waveform table. (basswaves) | |
; $19 = Melody loop cycle length table. (length) | |
; $1d = SID Channel sustain and release values. (Attack/Decay are always zero) (vols) | |
; $20 = Drum BIT value check table. (btt) | |
; $24 = Drum Waveform table. (silence isn't stored in drum values) (wav) | |
; $27 = Drum starting Pitch value table. (not) | |
; $2a = Drum Pitch addition signed value table. (plu) | |
; $2d = Drum ticks before note off value table. (del) | |
; $30 = Drum beat pattern table. (beat) | |
; $60 = Current note timing tick. | |
; $61 = Current note position in pattern. | |
; $62 = Filter cut-off value. | |
; $66 = Drum Waveform setting for use with note-off. | |
; $67 = Current drum pitch. | |
; $68 = Pitch value to add to drum pitch every frame. (signed value) | |
; $69 = Drum note-off timer. | |
; $6b = number of cycles to loop the current pattern |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment