From 31cccce3e1697f025e5141cd687e168ace821879 Mon Sep 17 00:00:00 2001 From: g012 Date: Thu, 11 Apr 2024 14:37:13 +0200 Subject: [PATCH] [NES] Added UNROM-512 mapper and sample --- .gitignore | 2 +- README.md | 5 + asm.lua | 7 +- nes.l65 | 54 ++- samples/nes_chrram.l65 | 75 ++++ samples/vcs_flush.l65 | 226 +++++------ samples/vcs_music.l65 | 826 ++++++++++++++++++++--------------------- vim/syntax/l65.vim | 2 +- 8 files changed, 665 insertions(+), 532 deletions(-) create mode 100644 samples/nes_chrram.l65 diff --git a/.gitignore b/.gitignore index 0a850a3..b38e95f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,4 @@ *.nes.deb *.sym *.mlb -*.nl +*.nl \ No newline at end of file diff --git a/README.md b/README.md index a882d24..fe800c2 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ Table of Contents * [Windows](#windows) * [Linux](#linux) * [Vim files installation](#vim-files-installation) + * [Visual Studio Code extension](#vscode-extension) * [TODO List](#todo-list) * [Credits](#credits) * [License](#license) @@ -656,6 +657,10 @@ make Note that the syntax file includes some highlighting for features only activated via pragmas: `dna`, `xsr`, `rtx` and `far`. If you do not want to use these keywords, remove them from the syntax file. +## Visual Studio Code extension + +Install the "l65" extension from the marketplace or check [l65-vscode](https://github.com/g012/l65-vscode) repository. + ## TODO List * [k65](http://devkk.net/wiki/index.php?title=K65) style syntax diff --git a/asm.lua b/asm.lua index f1e7963..5ee5760 100644 --- a/asm.lua +++ b/asm.lua @@ -101,7 +101,8 @@ M.link = function() end for _,constraint in ipairs(section.constraints) do local cstart, cfinish = address+constraint.start, address+constraint.finish - if rorg(cstart) // 0x100 == rorg(cfinish) // 0x100 then + local s, f = rorg(cstart) // 0x100, rorg(cfinish) // 0x100 + if s==f or math.ceil(s)==math.floor(s) and s+1==f and cfinish-cstart==0x100 then if constraint.type == 'crosspage' then return end else if constraint.type == 'samepage' then return end @@ -259,7 +260,7 @@ M.link = function() return waste, cross, lsb end) if not position then - error("unable to find space for section " .. section.label) + error("unable to find space for related section '" .. section.label .. "' of size " .. section.size) end for section,offset in pairs(related) do section.org = position + (section.location.start - location_start) + offset @@ -273,7 +274,7 @@ M.link = function() table.sort(position_independent_sections, function(a,b) if a.size==b.size then return a.label>b.label end return a.size>b.size end) for _,section in ipairs(position_independent_sections) do if not position_section(section) then - error("unable to find space for section " .. section.label) + error("unable to find space for section '" .. section.label .. "' of size " .. section.size) end end diff --git a/nes.l65 b/nes.l65 index ca3bfc8..a1ef94f 100644 --- a/nes.l65 +++ b/nes.l65 @@ -405,7 +405,7 @@ mappers.UxROM = function(t) until off + sz >= t.chrsize chrrom = chrrom0 function switchprgrom(bankix) - if bankix then assert(bankix < bc-1) lda #bankix end + if bankix then assert(bankix < bc, "mappers.switchprgrom: bank out of range: " .. bankix .. ", expected 0-" .. (bc-1)) lda #bankix end -- lda to reverse map [n..0] to [0..n] tax lda bankbytes,x sta bankbytes,x end @@ -415,6 +415,58 @@ mappers.UxROM = function(t) end mappers[2] = mappers.UxROM +--[[ + https://www.nesdev.org/wiki/UNROM_512 +]] +mappers.UNROM512 = function(t) + if not t then t = {} end + t.mapperid = 30 + if not t.prgsize then t.prgsize = 32 * 16384 end + assert(t.prgsize >= 0x8000 and t.prgsize <= 0x80000, "prgsize must be at least 32kB and at most 512kB") + assert(n0ne(t.chrsize), "chrsize must be 0") + if n0ne(t.chrramsize) and n0ne(t.chrbramsize) then t.chrramsize = 4 * 8192 end + local csize = val0(t.chrramsize) + val0(t.chrbramsize) + assert(csize == 0x2000 or csize == 0x4000 or csize == 0x8000, "combined chrram size must be 8, 16 or 32kB") + hdrrom = location{0x7ff0, 0x7fff, name='header'} + header(t) + local bc = t.prgsize//0x4000 + for bi=0,bc-2 do + local o,ix = 0x8000 + bi*0x4000, bc-1-bi + _ENV['prgrom'..ix] = location{o, o+0x3fff, rorg=0x8000, name='prgrom'..ix} + end + local prglast = 0x8000 + (bc-1)*0x4000 + prgrom0 = location{prglast, prglast+0x3fff, rorg=0xc000, name='prgrom0'} + prgrom = prgrom0 + section{"vectors", org=prglast+0x3ffa} dc.w nmi, main, irq + section{ "bankbytes", align=256 } -- for handling bus conflicts + for m=0,1 do + for c=0,3 do + for p=0x1f,0,-1 do byte(m<<7 | c<<5 | p) end + end + end + function clearchrram(page_count, offset) + ppu_addr(CHAR0 + (offset or 0)) tay ldx#(page_count or 32) @_clear sta PPUDATA iny bne _clear dex bne _clear + end + function loadchrram(var, page_count, offset) + ppu_addr(CHAR0 + (offset or 0)) ldx#(page_count or 32) ldy#0 @_load lda (var),y sta PPUDATA iny bne _load inc var+1 dex bne _load + end + function switch(prgbankix, chrbankix, mirror) + if prgbankix then assert(prgbankix < bc, "mappers.switch: PRG bank out of range: " .. prgbankix .. ", expected 0-" .. (bc-1)) end + if chrbankix then assert(chrbankix < 4, "mappers.switch: CHR bank out of range: " .. chrbankix .. ", expected 0-3") end + if mirror then assert(mirror == 0 or mirror == 1, "mappers.switch: mirror out of range: " .. mirror .. ", expected 0-1") end + if prgbankix or chrbankix or mirror then + local r = (mirror or 0)<<7 | (chrbankix or 0)<<5 | (prgbankix or 1) + lda #r + end + -- lda to reverse prg map [n..0] to [0..n] + tax lda bankbytes,x sta bankbytes,x + end + mappers.init = function() + switch(1) + end +end +mappers[30] = mappers.UxROM + --[[ https://wiki.nesdev.com/w/index.php/CNROM Has bus conflicts. diff --git a/samples/nes_chrram.l65 b/samples/nes_chrram.l65 new file mode 100644 index 0000000..192b497 --- /dev/null +++ b/samples/nes_chrram.l65 @@ -0,0 +1,75 @@ +require'nes' +mappers.UNROM512() + +location(prgrom1) + +local font_size = 27 * 16 +@@font + dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -- + dc.b 0x38,0x38,0x2c,0x64,0x7e,0x46,0xce,0x00,0x38,0x38,0x2c,0x64,0x7e,0x46,0xce,0x00 -- A + dc.b 0xfc,0x62,0x66,0x7c,0x62,0x66,0xfc,0x00,0xfc,0x62,0x66,0x7c,0x62,0x66,0xfc,0x00 -- B + dc.b 0x7c,0xe6,0xc2,0xc0,0xc0,0xe6,0x7c,0x00,0x7c,0xe6,0xc2,0xc0,0xc0,0xe6,0x7c,0x00 -- C + dc.b 0xfc,0x4e,0x46,0x46,0x46,0xce,0xfc,0x00,0xfc,0x4e,0x46,0x46,0x46,0xce,0xfc,0x00 -- D + dc.b 0xfe,0x66,0x60,0x7c,0x60,0x66,0xfe,0x00,0xfe,0x66,0x60,0x7c,0x60,0x66,0xfe,0x00 -- E + dc.b 0xfe,0x66,0x60,0x7c,0x60,0x60,0xf0,0x00,0xfe,0x66,0x60,0x7c,0x60,0x60,0xf0,0x00 -- F + dc.b 0x7c,0xe6,0xc0,0xce,0xc6,0xe6,0x7c,0x00,0x7c,0xe6,0xc0,0xce,0xc6,0xe6,0x7c,0x00 -- G + dc.b 0xee,0x66,0x66,0x7e,0x66,0x66,0xee,0x00,0xee,0x66,0x66,0x7e,0x66,0x66,0xee,0x00 -- H + dc.b 0x3c,0x18,0x18,0x18,0x18,0x18,0x3c,0x00,0x3c,0x18,0x18,0x18,0x18,0x18,0x3c,0x00 -- I + dc.b 0x0e,0x06,0x06,0xc6,0xc6,0xce,0x7c,0x00,0x0e,0x06,0x06,0xc6,0xc6,0xce,0x7c,0x00 -- J + dc.b 0xce,0xdc,0xf8,0xf0,0xf8,0xdc,0xce,0x00,0xce,0xdc,0xf8,0xf0,0xf8,0xdc,0xce,0x00 -- K + dc.b 0xc0,0xc0,0xc0,0xc0,0xc6,0xc6,0xfe,0x00,0xc0,0xc0,0xc0,0xc0,0xc6,0xc6,0xfe,0x00 -- L + dc.b 0xc6,0xee,0xfe,0xd6,0xc6,0xc6,0xc6,0x00,0xc6,0xee,0xfe,0xd6,0xc6,0xc6,0xc6,0x00 -- M + dc.b 0xc6,0xe6,0xf6,0xde,0xce,0xc6,0xc6,0x00,0xc6,0xe6,0xf6,0xde,0xce,0xc6,0xc6,0x00 -- N + dc.b 0x7c,0xee,0xc6,0xc6,0xc6,0xee,0x7c,0x00,0x7c,0xee,0xc6,0xc6,0xc6,0xee,0x7c,0x00 -- O + dc.b 0xfc,0xc6,0xc6,0xc6,0xfc,0xc0,0xc0,0x00,0xfc,0xc6,0xc6,0xc6,0xfc,0xc0,0xc0,0x00 -- P + dc.b 0x7c,0xce,0xc6,0xc6,0xde,0xec,0x7e,0x00,0x7c,0xce,0xc6,0xc6,0xde,0xec,0x7e,0x00 -- Q + dc.b 0xfc,0x66,0x66,0x7c,0x58,0x6c,0xe6,0x00,0xfc,0x66,0x66,0x7c,0x58,0x6c,0xe6,0x00 -- R + dc.b 0x7c,0xc6,0xc0,0x7c,0x06,0xc6,0x7c,0x00,0x7c,0xc6,0xc0,0x7c,0x06,0xc6,0x7c,0x00 -- S + dc.b 0xfe,0x92,0x10,0x10,0x10,0x10,0x38,0x00,0xfe,0x92,0x10,0x10,0x10,0x10,0x38,0x00 -- T + dc.b 0xe6,0xe6,0xc2,0xc2,0xc2,0xe6,0x7c,0x00,0xe6,0xe6,0xc2,0xc2,0xc2,0xe6,0x7c,0x00 -- U + dc.b 0xc6,0xc6,0xc6,0x6c,0x6c,0x38,0x38,0x00,0xc6,0xc6,0xc6,0x6c,0x6c,0x38,0x38,0x00 -- V + dc.b 0xc6,0xc6,0xd6,0xfe,0xee,0xc6,0x82,0x00,0xc6,0xc6,0xd6,0xfe,0xee,0xc6,0x82,0x00 -- W + dc.b 0x86,0xcc,0x78,0x30,0x78,0xcc,0x86,0x00,0x86,0xcc,0x78,0x30,0x78,0xcc,0x86,0x00 -- X + dc.b 0xc6,0xc6,0x6c,0x38,0x18,0x18,0x38,0x00,0xc6,0xc6,0x6c,0x38,0x18,0x18,0x38,0x00 -- Y + dc.b 0x7e,0xce,0x98,0x30,0x62,0xe6,0xfc,0x00,0x7e,0xce,0x98,0x30,0x62,0xe6,0xfc,0x00 -- Z + +-- RAM +chr = 0 + +location(prgrom) +@@nmi rti +@@irq rti + +charset(" abcdefghijklmnopqrstuvwxyz") +local hello = "hello world" +@@text byte(hello) + +@@main + init() + clearchrram() + -- load font in CHR RAM + ppu_addr(CHAR0) + lda #font&0xff sta chr lda #font>>8 sta chr+1 + ldy #0 @_loadfnt1 lda (chr),y sta PPUDATA iny bne _loadfnt1 + inc chr+1 + @_loadfnt2 lda (chr),y sta PPUDATA iny cpy #font_size-256 bne _loadfnt2 + -- load BG palette in PPU RAM + ppu_addr(BGPAL) + for _,v in ipairs{ 0x1f, 0x00, 0x10, 0x20 } do lda #v sta PPUDATA end + -- load screen text in PPU RAM 0x21CA + ppu_addr(0x21ca) + ldy #0 @_loadtxt lda text,y sta PPUDATA iny cpy ##hello bne _loadtxt + -- reset scroll position + ppu_addr(0) sta BGSCROL sta BGSCROL + -- show BG + vblank_waitbegin() + lda #0x0a sta PPUMASK + -- idle + @_loop jmp _loop + +writebin(filename..'.nes') +writesym(filename..'.mlb', 'mesen') +writesym(filename..'.nes', 'fceux') +print(stats) + + diff --git a/samples/vcs_flush.l65 b/samples/vcs_flush.l65 index b32a459..16d05f0 100644 --- a/samples/vcs_flush.l65 +++ b/samples/vcs_flush.l65 @@ -1,113 +1,113 @@ -require'vcs' -mappers['4K']() - -local HEADER_LEN = 92 -local PICTURE_LEN = 64 -local STEP_COUNT = 32 - --- FLUSH text as playfield -local logo_img = assert(l65.image("flush.png")) -- analyse the image twice, so load it separately -@@logo_col samepage byte(0x00, 0xd4, 0x72, linecol(logo_img)) end -local pfs = playfield(logo_img) -for i=1,#pfs do - local pf = pfs[i] - section("logo_pf"..(i-1)) samepage byte(0, pf[1], pf[#pf], pf) end -end -local LOGO_HEIGHT = #pfs[1] - --- background -local bg = linecol("flushbg.png") -@@logo_bg_all -samepage - @logo_bg_pre for i=1,8 do dc.b bg[1] end - @logo_bg - for i=1,13 do dc.b bg[1] end - byte(bg) - for i=1,13 do dc.b bg[#bg] end - @logo_bg_post for i=1,8 do dc.b bg[#bg] end -end - --- generate offset tables into logo_pf* -do - local DISP_HEIGHT = PICTURE_LEN - local transfo,bgshift = {},{} - for x=0,STEP_COUNT-1 do - local theta = x/(STEP_COUNT-1) * math.pi/2 - math.pi/4 - local s,c = math.sin(theta),math.cos(theta) - local ra,rb,rc,rd = -c, s-c, s+c, c - for y=0,DISP_HEIGHT-1 do - local r = DISP_HEIGHT / LOGO_HEIGHT - local yn = r * (2 * y/(DISP_HEIGHT-1) - 1) - local function f() - local y = (s-yn) / c - return math.min(math.floor((y+1)/2*LOGO_HEIGHT), LOGO_HEIGHT-1) + 3 - end - local v - if rd < rc then v = yn>rc and 0 or yn>rb and f(yn) or yn>ra and 2 or 0 - else v = yn>rd and 0 or yn>rc and 1 or yn>rb and f(yn) or 0 end - transfo[x*DISP_HEIGHT+DISP_HEIGHT-y] = v - end - bgshift[#bgshift+1] = math.floor(math.sin(math.pi+theta)*LOGO_HEIGHT/4) - end - @@logo_transfo byte(transfo) - @@logo_bgshift byte(bgshift) -end - --- rotation anim -do - local PERIOD = 128 - local f = \x((math.sin(x*2*math.pi/PERIOD)+1)/2 * (STEP_COUNT-1)) - local rotation = {} - for x=1,PERIOD do - rotation[#rotation+1] = math.floor(math.min(f(x-1), (STEP_COUNT-1))) - end - @@logo_rotation byte(rotation) -end - -local bg_ptr = 0x80 -local trans_ptr = 0x82 -local time = 0x84 -local tmp = 0x85 - -local on_vblank = function() - inc time asl time lsr time - ldy time lda logo_rotation,y sta tmp - - ldy tmp lda logo_bgshift,y sta bg_ptr - lda#logo_bg&0xff clc adc bg_ptr sta bg_ptr lda#logo_bg>>8 sta bg_ptr+1 - - setptr(logo_transfo, trans_ptr) - lda tmp asl asl asl asl asl asl clc adc trans_ptr sta trans_ptr - lda trans_ptr+1 adc#0 sta trans_ptr+1 - lda tmp lsr lsr clc adc trans_ptr+1 sta trans_ptr+1 - - ldy#PICTURE_LEN-1 lda (bg_ptr),y sta COLUBK -end - -local kernel = function() - ldy#HEADER_LEN @_header sta WSYNC dey bne _header - samepage @_line - lda (trans_ptr),y tax - lda (bg_ptr),y - sta WSYNC sta COLUBK - lda logo_col,x sta COLUPF - lda logo_pf0,x sta PF0 - lda logo_pf1,x sta PF1 - lda logo_pf2,x sta PF2 - lda logo_pf3,x sta PF0 - lda logo_pf4,x sta PF1 - lda logo_pf5,x sta PF2 - iny cpy#PICTURE_LEN bne _line - end - lda#0 sta WSYNC sta PF0 sta PF1 sta PF2 -end - -@@main - init() -@_frame - overscan() vblank(on_vblank) screen(kernel) jmp _frame - -; -writebin(filename..'.bin') -writesym(filename..'.sym') -print(stats) +require'vcs' +mappers['4K']() + +local HEADER_LEN = 92 +local PICTURE_LEN = 64 +local STEP_COUNT = 32 + +-- FLUSH text as playfield +local logo_img = assert(l65.image("flush.png")) -- analyse the image twice, so load it separately +@@logo_col samepage byte(0x00, 0xd4, 0x72, linecol(logo_img)) end +local pfs = playfield(logo_img) +for i=1,#pfs do + local pf = pfs[i] + section("logo_pf"..(i-1)) samepage byte(0, pf[1], pf[#pf], pf) end +end +local LOGO_HEIGHT = #pfs[1] + +-- background +local bg = linecol("flushbg.png") +@@logo_bg_all +samepage + @logo_bg_pre for i=1,8 do dc.b bg[1] end + @logo_bg + for i=1,13 do dc.b bg[1] end + byte(bg) + for i=1,13 do dc.b bg[#bg] end + @logo_bg_post for i=1,8 do dc.b bg[#bg] end +end + +-- generate offset tables into logo_pf* +do + local DISP_HEIGHT = PICTURE_LEN + local transfo,bgshift = {},{} + for x=0,STEP_COUNT-1 do + local theta = x/(STEP_COUNT-1) * math.pi/2 - math.pi/4 + local s,c = math.sin(theta),math.cos(theta) + local ra,rb,rc,rd = -c, s-c, s+c, c + for y=0,DISP_HEIGHT-1 do + local r = DISP_HEIGHT / LOGO_HEIGHT + local yn = r * (2 * y/(DISP_HEIGHT-1) - 1) + local function f() + local y = (s-yn) / c + return math.min(math.floor((y+1)/2*LOGO_HEIGHT), LOGO_HEIGHT-1) + 3 + end + local v + if rd < rc then v = yn>rc and 0 or yn>rb and f(yn) or yn>ra and 2 or 0 + else v = yn>rd and 0 or yn>rc and 1 or yn>rb and f(yn) or 0 end + transfo[x*DISP_HEIGHT+DISP_HEIGHT-y] = v + end + bgshift[#bgshift+1] = math.floor(math.sin(math.pi+theta)*LOGO_HEIGHT/4) + end + @@logo_transfo byte(transfo) + @@logo_bgshift byte(bgshift) +end + +-- rotation anim +do + local PERIOD = 128 + local f = \x((math.sin(x*2*math.pi/PERIOD)+1)/2 * (STEP_COUNT-1)) + local rotation = {} + for x=1,PERIOD do + rotation[#rotation+1] = math.floor(math.min(f(x-1), (STEP_COUNT-1))) + end + @@logo_rotation byte(rotation) +end + +local bg_ptr = 0x80 +local trans_ptr = 0x82 +local time = 0x84 +local tmp = 0x85 + +local on_vblank = function() + inc time asl time lsr time + ldy time lda logo_rotation,y sta tmp + + ldy tmp lda logo_bgshift,y sta bg_ptr + lda#logo_bg&0xff clc adc bg_ptr sta bg_ptr lda#logo_bg>>8 sta bg_ptr+1 + + setptr(logo_transfo, trans_ptr) + lda tmp asl asl asl asl asl asl clc adc trans_ptr sta trans_ptr + lda trans_ptr+1 adc#0 sta trans_ptr+1 + lda tmp lsr lsr clc adc trans_ptr+1 sta trans_ptr+1 + + ldy#PICTURE_LEN-1 lda (bg_ptr),y sta COLUBK +end + +local kernel = function() + ldy#HEADER_LEN @_header sta WSYNC dey bne _header + samepage @_line + lda (trans_ptr),y tax + lda (bg_ptr),y + sta WSYNC sta COLUBK + lda logo_col,x sta COLUPF + lda logo_pf0,x sta PF0 + lda logo_pf1,x sta PF1 + lda logo_pf2,x sta PF2 + lda logo_pf3,x sta PF0 + lda logo_pf4,x sta PF1 + lda logo_pf5,x sta PF2 + iny cpy#PICTURE_LEN bne _line + end + lda#0 sta WSYNC sta PF0 sta PF1 sta PF2 +end + +@@main + init() +@_frame + overscan() vblank(on_vblank) screen(kernel) jmp _frame + +; +writebin(filename..'.bin') +writesym(filename..'.sym') +print(stats) diff --git a/samples/vcs_music.l65 b/samples/vcs_music.l65 index 1a311a7..39686dd 100644 --- a/samples/vcs_music.l65 +++ b/samples/vcs_music.l65 @@ -1,413 +1,413 @@ -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,-- - 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) +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,-- + 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) diff --git a/vim/syntax/l65.vim b/vim/syntax/l65.vim index 321c9ef..1ab5b66 100644 --- a/vim/syntax/l65.vim +++ b/vim/syntax/l65.vim @@ -577,7 +577,7 @@ syn match l65Opcode /\/ syn match l65Opcode /\/ syn match l65Opcode /\/ syn match l65Opcode /\/ -" Common alias, reomve if desired. +" Common alias, remove if desired. syn match l65Opcode /\/ syn match l65Keyword /\/ syn match l65Keyword /\/