mirror of
https://github.com/g012/l65.git
synced 2024-09-29 16:55:13 +00:00
414 lines
14 KiB
Plaintext
414 lines
14 KiB
Plaintext
require'vcs'
|
|
mappers['2K']()
|
|
|
|
local AUDC0s = 0x90
|
|
local AUDC1s, AUDF0s, AUDF1s, AUDV0s, AUDV1s = AUDC0s+1, AUDC0s+2, AUDC0s+3, AUDC0s+4, AUDC0s+5
|
|
local vubars = 0xA0
|
|
local tmp = 0xB0
|
|
|
|
local zic_filename = ... or 'Ishtar.ttt'
|
|
local zic = ttt(zic_filename)
|
|
print(string.format('Using file %s (ttt version %d)\n Name: %s\n Author: %s\n%s', zic_filename, zic.version, zic.name, zic.author, zic.comment))
|
|
|
|
-- debug printing of zic data
|
|
--[[
|
|
local printbytetable = function(v)
|
|
local s = ' '
|
|
for kk,vv in ipairs(v) do
|
|
s = s .. string.format('%02x, ', vv)
|
|
if #s >= 34 then print(s) s = ' ' end
|
|
end
|
|
if #s > 0 then print(s) end
|
|
end
|
|
for k,v in pairs(zic) do
|
|
if type(v) ~= 'table' then
|
|
print(k,v)
|
|
elseif type(v[1]) == 'number' then
|
|
print(k) printbytetable(v)
|
|
elseif type(v[1]) == 'table' then
|
|
for ek, e in ipairs(v) do
|
|
print(k, ek, e.name) printbytetable(e)
|
|
end
|
|
end
|
|
end
|
|
]]
|
|
|
|
@@tt_InsCtrlTable byte(zic.insctrltable)
|
|
@@tt_InsADIndexes byte(zic.insadindexes)
|
|
@@tt_InsSustainIndexes byte(zic.inssustainindexes)
|
|
@@tt_InsReleaseIndexes byte(zic.insreleaseindexes)
|
|
@@tt_InsFreqVolTable byte(zic.insfreqvoltable)
|
|
@@tt_PercIndexes byte(zic.percindexes)
|
|
@@tt_PercFreqTable byte(zic.percfreqtable)
|
|
@@tt_PercCtrlVolTable byte(zic.percctrlvoltable)
|
|
local patterns = {}
|
|
for i,pattern in ipairs(zic.patterns) do
|
|
table.insert(patterns, section("pattern"..i)) byte(pattern)
|
|
end
|
|
@@tt_PatternSpeeds byte(zic.patternspeeds)
|
|
@@tt_PatternPtrLo byte_lo(patterns)
|
|
@@tt_PatternPtrHi byte_hi(patterns)
|
|
@@tt_SequenceTable byte(zic.sequence[1]) byte(zic.sequence[2])
|
|
|
|
local tt = {
|
|
-- =====================================================================
|
|
-- Permanent variables. These are states needed by the player.
|
|
-- =====================================================================
|
|
'timer', -- current music timer value
|
|
'cur_pat_index_c0', -- current pattern index into tt_SequenceTable
|
|
'cur_pat_index_c1',
|
|
'cur_note_index_c0', -- note index into current pattern
|
|
'cur_note_index_c1',
|
|
'envelope_index_c0', -- index into ADSR envelope
|
|
'envelope_index_c1',
|
|
'cur_ins_c0', -- current instrument
|
|
'cur_ins_c1',
|
|
-- =====================================================================
|
|
-- Temporary variables. These will be overwritten during a call to the
|
|
-- player routine, but can be used between calls for other things.
|
|
-- =====================================================================
|
|
'ptr', -- 2 bytes
|
|
}
|
|
for k,v in ipairs(tt) do tt[v] = k + 0x7F end
|
|
|
|
tt.FREQ_MASK = 0b00011111
|
|
tt.INS_HOLD = 8
|
|
tt.INS_PAUSE = 16
|
|
tt.FIRST_PERC = 17
|
|
|
|
tt.fetchCurrentNoteImpl = function()
|
|
@_constructPatPtr
|
|
ldy tt.cur_pat_index_c0,x lda tt_SequenceTable,y
|
|
if zic.usegoto then
|
|
bpl _noPatternGoto
|
|
;and #0b01111111 sta tt.cur_pat_index_c0,x bpl _constructPatPtr
|
|
@_noPatternGoto
|
|
end
|
|
tay lda tt_PatternPtrLo,y sta tt.ptr lda tt_PatternPtrHi,y sta tt.ptr+1
|
|
if zic.overlay then
|
|
clv
|
|
lda tt.cur_note_index_c0,x bpl _notPrefetched
|
|
;and #0b01111111 sta tt.cur_note_index_c0,x
|
|
bit tt_Bit6Set
|
|
@_notPrefetched
|
|
tay
|
|
else
|
|
ldy tt.cur_note_index_c0,x
|
|
end
|
|
lda (tt.ptr),y bne _noEndOfPattern
|
|
sta tt.cur_note_index_c0,x
|
|
inc tt.cur_pat_index_c0,x
|
|
bne _constructPatPtr
|
|
@_noEndOfPattern
|
|
end
|
|
if zic.useoverlay then
|
|
@@tt_fetchCurrentNoteSection tt.fetchCurrentNoteImpl() rts
|
|
tt.fetchCurrentNote = function() jsr tt_fetchCurrentNoteSection end
|
|
else
|
|
tt.fetchCurrentNote = tt.fetchCurrentNoteImpl
|
|
end
|
|
|
|
@@tt_CalcInsIndex
|
|
lsr lsr lsr lsr lsr
|
|
tay
|
|
@tt_Bit6Set
|
|
rts
|
|
|
|
local function zic_tick()
|
|
dec tt.timer bpl _noNewNote
|
|
ldx #1
|
|
@_advanceLoop
|
|
tt.fetchCurrentNote()
|
|
cmp #tt.INS_PAUSE
|
|
if zic.useslide then
|
|
beq _pause
|
|
bcs _newNote
|
|
adc tt.cur_ins_c0,x sec sbc #8 sta tt.cur_ins_c0,x
|
|
bcs _finishedNewNote
|
|
else
|
|
bcc _finishedNewNote
|
|
bne _newNote
|
|
end
|
|
@_pause
|
|
lda tt.cur_ins_c0,x jsr tt_CalcInsIndex
|
|
lda tt_InsReleaseIndexes-1,y
|
|
clc adc #1 bcc _storeADIndex
|
|
@_newNote
|
|
sta tt.cur_ins_c0,x
|
|
cmp #tt.FREQ_MASK+1 bcs _startInstrument
|
|
tay
|
|
lda tt_PercIndexes-tt.FIRST_PERC,y
|
|
bne _storeADIndex
|
|
@_startInstrument
|
|
if zic.useoverlay then
|
|
bvs _finishedNewNote
|
|
end
|
|
jsr tt_CalcInsIndex
|
|
lda tt_InsADIndexes-1,y
|
|
@_storeADIndex
|
|
sta tt.envelope_index_c0,x
|
|
@_finishedNewNote
|
|
inc tt.cur_note_index_c0,x
|
|
@_sequencerNextChannel
|
|
dex
|
|
bpl _advanceLoop
|
|
if zic.globalspeed then
|
|
ldx #zic.speed-1
|
|
if zic.usefunktempo then
|
|
lda tt.cur_note_index_c0 lsr bcc _noOddFrame ldx #zic.oddspeed-1 @_noOddFrame
|
|
end
|
|
stx tt.timer
|
|
else
|
|
ldx tt.cur_pat_index_c0 ldy tt_SequenceTable,x
|
|
if zic.usefunktempo then
|
|
lda tt.cur_note_index_c0 lsr
|
|
lda tt_PatternSpeeds,y
|
|
bcc _evenFrame
|
|
;and #0x0f
|
|
bcs _storeFunkTempo
|
|
@_evenFrame
|
|
lsr lsr lsr lsr
|
|
@_storeFunkTempo
|
|
sta tt.timer
|
|
else
|
|
lda tt_PatternSpeeds,y sta tt.timer
|
|
end
|
|
end
|
|
@_noNewNote
|
|
ldx #1
|
|
@_updateLoop
|
|
lda tt.cur_ins_c0,x
|
|
if not zic.startswithnotes then
|
|
beq _afterAudioUpdate
|
|
end
|
|
cmp #tt.FREQ_MASK+1 bcs _instrument
|
|
ldy tt.envelope_index_c0,x
|
|
lda tt_PercCtrlVolTable-1,y beq _endOfPercussion inc tt.envelope_index_c0,x @_endOfPercussion
|
|
sta AUDV0,x sta AUDV0s,x lsr lsr lsr lsr sta AUDC0,x sta AUDC0s,x
|
|
lda tt_PercFreqTable-1,y
|
|
sta AUDF0,x
|
|
sta AUDF0s,x -- EXTRA for vumeter rendering
|
|
if zic.useoverlay then
|
|
bpl _afterAudioUpdate
|
|
tt.fetchCurrentNote()
|
|
cmp #tt.FREQ_MASK+1
|
|
bcc _afterAudioUpdate
|
|
sta tt.cur_ins_c0,x
|
|
jsr tt_CalcInsIndex
|
|
lda tt_InsSustainIndexes-1,y sta tt.envelope_index_c0,x
|
|
asl tt.cur_note_index_c0,x sec ror tt.cur_note_index_c0,x
|
|
bmi _afterAudioUpdate
|
|
else
|
|
jmp _afterAudioUpdate
|
|
end
|
|
@_instrument
|
|
jsr tt_CalcInsIndex
|
|
lda tt_InsCtrlTable-1,y sta AUDC0,x
|
|
sta AUDC0s,x -- EXTRA for vumeter rendering
|
|
lda tt.envelope_index_c0,x cmp tt_InsReleaseIndexes-1,y bne _noEndOfSustain lda tt_InsSustainIndexes-1,y @_noEndOfSustain
|
|
tay
|
|
lda tt_InsFreqVolTable,y beq _endOfEnvelope iny @_endOfEnvelope
|
|
sty tt.envelope_index_c0,x
|
|
sta AUDV0,x
|
|
sta AUDV0s,x -- EXTRA for vumeter rendering
|
|
lsr lsr lsr lsr clc adc tt.cur_ins_c0,x sec sbc #8
|
|
sta AUDF0,x
|
|
sta AUDF0s,x -- EXTRA for vumeter rendering
|
|
@_afterAudioUpdate
|
|
dex
|
|
bpl _updateLoop
|
|
end
|
|
|
|
local function zic_init()
|
|
if zic.c0init ~= 0 then lda#zic.c0init sta tt.cur_pat_index_c0 end
|
|
if zic.c1init ~= 0 then lda#zic.c1init sta tt.cur_pat_index_c1 end
|
|
end
|
|
|
|
----------------------------------------------------------------------
|
|
-- display song name and author
|
|
----------------------------------------------------------------------
|
|
|
|
section{ "font", align=256 } dc.b
|
|
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,-- <SPC>
|
|
0x18,0x3c,0x66,0x7e,0x66,0x66,0x66,0x00,-- A
|
|
0x7c,0x66,0x66,0x7c,0x66,0x66,0x7c,0x00,-- B
|
|
0x3c,0x66,0x60,0x60,0x60,0x66,0x3c,0x00,-- C
|
|
0x78,0x6c,0x66,0x66,0x66,0x6c,0x78,0x00,-- D
|
|
0x7e,0x60,0x60,0x78,0x60,0x60,0x7e,0x00,-- E
|
|
0x7e,0x60,0x60,0x78,0x60,0x60,0x60,0x00,-- F
|
|
0x3c,0x66,0x60,0x6e,0x66,0x66,0x3c,0x00,-- G
|
|
0x66,0x66,0x66,0x7e,0x66,0x66,0x66,0x00,-- H
|
|
0x3c,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,-- I
|
|
0x1e,0x0c,0x0c,0x0c,0x0c,0x6c,0x38,0x00,-- J
|
|
0x66,0x6c,0x78,0x70,0x78,0x6c,0x66,0x00,-- K
|
|
0x60,0x60,0x60,0x60,0x60,0x60,0x7e,0x00,-- L
|
|
0x63,0x77,0x7f,0x6b,0x63,0x63,0x63,0x00,-- M
|
|
0x66,0x76,0x7e,0x7e,0x6e,0x66,0x66,0x00,-- N
|
|
0x3c,0x66,0x66,0x66,0x66,0x66,0x3c,0x00,-- O
|
|
0x7c,0x66,0x66,0x7c,0x60,0x60,0x60,0x00,-- P
|
|
0x3c,0x66,0x66,0x66,0x66,0x3c,0x0e,0x00,-- Q
|
|
0x7c,0x66,0x66,0x7c,0x78,0x6c,0x66,0x00,-- R
|
|
0x3c,0x66,0x60,0x3c,0x06,0x66,0x3c,0x00,-- S
|
|
0x7e,0x18,0x18,0x18,0x18,0x18,0x18,0x00,-- T
|
|
0x66,0x66,0x66,0x66,0x66,0x66,0x3c,0x00,-- U
|
|
0x66,0x66,0x66,0x66,0x66,0x3c,0x18,0x00,-- V
|
|
0x63,0x63,0x63,0x6b,0x7f,0x77,0x63,0x00,-- W
|
|
0x66,0x66,0x3c,0x18,0x3c,0x66,0x66,0x00,-- X
|
|
0x66,0x66,0x66,0x3c,0x18,0x18,0x18,0x00,-- Y
|
|
0x7e,0x06,0x0c,0x18,0x30,0x60,0x7e,0x00 -- Z
|
|
|
|
charset(" abcdefghijklmnopqrstuvwxyz", \x(x*8))
|
|
local normtxt = function(txt)
|
|
txt = txt:lower()
|
|
local out = {}
|
|
for i=1,#txt do
|
|
local c = string.byte(txt:sub(i,i))
|
|
if c < string.byte('a') or c > string.byte('z') then c = string.byte(' ') end
|
|
table.insert(out, string.char(c))
|
|
end
|
|
local ex = 12 - #out
|
|
for i=1,ex do
|
|
if i&1 ~= 0 then table.insert(out, 1, ' ')
|
|
else table.insert(out, ' ')
|
|
end
|
|
end
|
|
return table.concat(out)
|
|
end
|
|
@@song_name byte(normtxt(zic.name))
|
|
@@song_author byte(normtxt(zic.author))
|
|
|
|
local print_txt = tmp+1 -- text pointer, can cross
|
|
local print_line_count=tmp -- number of lines to print
|
|
local print_ptr = tmp+3 -- array of pointers to the characters
|
|
-- a = line count
|
|
@@print12
|
|
sta print_line_count
|
|
lda#6 sta NUSIZ0 sta NUSIZ1
|
|
-- set MSB of font character addresses
|
|
lda#font>>8 ldx#23 @_loadfont sta print_ptr,x dex dex bpl _loadfont
|
|
-- position sprites
|
|
samepage
|
|
sta WSYNC
|
|
ldx#6 @_delay dex bne _delay
|
|
sta RESP0 nop sta RESP1 lda#0x70 sta HMP0 lda#0x60 sta HMP1 sta WSYNC sta HMOVE
|
|
end
|
|
@_loop
|
|
-- load text line
|
|
ldx#0 ldy#0 @_loadline lda (print_txt),y sta print_ptr,x inx inx iny cpy#12 bne _loadline
|
|
lda#0x80 sta HMP0 sta HMP1
|
|
ldy#0 samepage @_printline
|
|
sta WSYNC sta HMOVE
|
|
-- first scanline
|
|
lda (print_ptr+2),y sta GRP0 lda (print_ptr+6),y sta GRP1
|
|
lda (print_ptr+22),y tax sleep(6)
|
|
lda (print_ptr+10),y sta GRP0 lda (print_ptr+14),y sta GRP1 lda (print_ptr+18),y sta GRP0 stx GRP1
|
|
sta HMCLR sleep(8) sta HMOVE
|
|
-- second scanline
|
|
lda (print_ptr),y sta GRP0 lda (print_ptr+4),y sta GRP1
|
|
lda (print_ptr+20),y tax lda#0x80 sta HMP0 sta HMP1 nop
|
|
lda (print_ptr+8),y sta GRP0 lda (print_ptr+12),y sta GRP1 lda (print_ptr+16),y sta GRP0 stx GRP1
|
|
iny cpy#8 bne _printline
|
|
end
|
|
dec print_line_count beq _end
|
|
lda print_txt clc adc#12 sta print_txt lda print_txt+1 adc#0 sta print_txt+1
|
|
jmp _loop
|
|
@_end
|
|
lda#0 sta GRP0 sta GRP1
|
|
rts
|
|
|
|
----------------------------------------------------------------------
|
|
-- equalizer anim
|
|
----------------------------------------------------------------------
|
|
|
|
-- convert to freq: http://atariage.com/forums/topic/257526-musicsound-programming-question-atari-2600/
|
|
-- maps AUDCi to 0-7 3 bits (<<5) value for wave length of: 1, 2, 6, 15, 31, 93, 465, 511
|
|
@@wavlen samepage
|
|
dc.b 0x00,0x60,0xC0,0xC0,0x20,0x20,0x80,0x80,
|
|
0xF0,0x80,0x80,0x00,0x40,0x40,0xA0,0xA0
|
|
end
|
|
local wavelen_map = { 1, 2, 6, 15, 31, 93, 465, 511 }
|
|
local freq_ranges = { 30, 60, 120, 240, 480, 960, 1920, 9999999 }
|
|
local t = {}
|
|
for i=0,255 do
|
|
local wavelen = wavelen_map[(i>>5)+1]
|
|
local note = (i&31)+1
|
|
local freq = 3546894 / 114 / wavelen / note
|
|
for x=1,9 do
|
|
if freq <= freq_ranges[x] then
|
|
t[#t+1] = x-1
|
|
break
|
|
end
|
|
end
|
|
end
|
|
section{'regfreq', align=256} byte(t)
|
|
@@vm_pf2 samepage
|
|
dc.b 0x00,0x80,0xC0,0xE0,0xF0,0xF8,0xFC,0xFE,
|
|
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
|
|
end
|
|
@@vm_pf1 samepage
|
|
dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
|
0x00,0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F
|
|
end
|
|
@@vm_col samepage
|
|
dc.b 0x3C,0x7C,0x9C,0xDC,0x2C,0x4C,0x6C,0xAC
|
|
end
|
|
local vumeter_tick = function()
|
|
lda #0 ldy #15 @_vudec ldx vubars,y dex bmi _vudecskip stx vubars,y @_vudecskip dey bpl _vudec
|
|
lda AUDF0s ldy AUDC0s ora wavlen,y tax ldy regfreq,x
|
|
lda AUDV0s cmp vubars,y bcc _vuless0 sta vubars,y @_vuless0
|
|
lda AUDF1s ldy AUDC1s ora wavlen,y tax ldy regfreq,x
|
|
lda AUDV1s cmp vubars+8,y bcc _vuless1 sta vubars+8,y @_vuless1
|
|
end
|
|
local vumeter_draw = function()
|
|
lda #ctrlpf.PF_MIRRORED sta CTRLPF
|
|
ldy #7
|
|
@_vudraw
|
|
samepage
|
|
lda #16 sta tmp
|
|
@_vudraw1
|
|
sta WSYNC
|
|
lda vm_col,y sta COLUPF sleep(4)
|
|
ldx vubars,y
|
|
lda vm_pf1,x sta PF1
|
|
lda vm_pf2,x sta PF2
|
|
sleep(8)
|
|
ldx vubars+8,y
|
|
lda vm_pf2,x sta PF2
|
|
lda vm_pf1,x sta PF1
|
|
dec tmp bpl _vudraw1
|
|
sta WSYNC lda #0 sta PF2 sta PF1
|
|
sta WSYNC
|
|
dey bpl _vudraw
|
|
end
|
|
lda #0 sta CTRLPF
|
|
sta WSYNC sta WSYNC
|
|
end
|
|
|
|
local tick = function()
|
|
zic_tick()
|
|
-- clamp to valid TIA range
|
|
lda AUDC0s ;and #0x0f sta AUDC0s lda AUDC1s ;and #0x0f sta AUDC1s
|
|
lda AUDF0s ;and #0x1f sta AUDF0s lda AUDF1s ;and #0x1f sta AUDF1s
|
|
lda AUDV0s ;and #0x0f sta AUDV0s lda AUDV1s ;and #0x0f sta AUDV1s
|
|
vumeter_tick()
|
|
end
|
|
|
|
local kernel = function()
|
|
lda#0x8a sta COLUP0 sta COLUP1
|
|
setptr(song_author,print_txt) lda#1 jsr print12
|
|
vumeter_draw()
|
|
lda#0xaa sta COLUP0 sta COLUP1
|
|
setptr(song_name,print_txt) lda#1 jsr print12
|
|
end
|
|
|
|
@@main
|
|
init()
|
|
zic_init()
|
|
@_frame
|
|
overscan() vblank(tick) screen(kernel) jmp _frame
|
|
|
|
; -- needed if last instruction is implied
|
|
writebin(filename..'.bin')
|
|
writesym(filename..'.sym')
|
|
print(stats)
|