mirror of https://github.com/g012/l65.git
Compare commits
68 Commits
Author | SHA1 | Date |
---|---|---|
g012 | 31cccce3e1 | |
g012 | 5f71b92bcd | |
g012 | 29f5666b20 | |
g012 | 0a65af8621 | |
g012 | 990b30f410 | |
maraflush | e57aee36bd | |
MooZ | 8ade3f3fd4 | |
g012 | 5105f8bd8d | |
mooz | 52a5777949 | |
mooz | f2ab52c0ae | |
mooz | fa59803eeb | |
mooz | 8f5e37dd82 | |
mooz | 18faa0ba34 | |
mooz | d6254b2ca3 | |
mooz | 7bfb04cf86 | |
mooz | ae99514183 | |
mooz | 345f9d522d | |
mooz | c42546e483 | |
mooz | ec56b7b504 | |
mooz | b47f3ebb99 | |
mooz | bac144896a | |
mooz | b9fc343548 | |
mooz | 08086d222e | |
mooz | a2e44ae24a | |
mooz | db038e29e5 | |
mooz | 0d66e70a69 | |
mooz | c09a04964a | |
mooz | 283c4256f5 | |
mooz | 711fe67574 | |
mooz | 636b5297c1 | |
mooz | 77b69902c2 | |
mooz | fe3cdabf4f | |
mooz | b9a9b9027d | |
mooz | 09a45eaffb | |
mooz | 93ffdf28dd | |
mooz | b04ff787d4 | |
mooz | 4ef0a69e8e | |
mooz | f917afff6a | |
mooz | 2d96e4e1d2 | |
mooz | 6708ddaecc | |
mooz | 6fc00c2b63 | |
mooz | 6efbd07034 | |
mooz | 564746133a | |
mooz | 6fafc034a0 | |
mooz | e56a44afa9 | |
mooz | f8ad543033 | |
mooz | d5c53b2f01 | |
mooz | e88b89cd01 | |
mooz | 662ef74b53 | |
g012 | 9c576ef774 | |
g012 | c888a63ee3 | |
g012 | 0eefee0282 | |
g012 | 9bf0461f41 | |
g012 | d539f9c194 | |
g012 | 808bf10b26 | |
g012 | f3bd018090 | |
g012 | 75db428750 | |
g012 | 22a89970f8 | |
g012 | 08d1dd99ad | |
g012 | 63fcde04ee | |
g012 | a60cf98c57 | |
g012 | d740492e8f | |
g012 | 99deb2b875 | |
g012 | 1245156c19 | |
g012 | cbe967a074 | |
g012 | d27e4ce8d1 | |
g012 | c95b8f8f24 | |
g012 | f589f55961 |
|
@ -1,4 +1,10 @@
|
|||
/build
|
||||
/tmp
|
||||
*.bin
|
||||
*.sym
|
||||
/build
|
||||
/tmp
|
||||
/zig-*
|
||||
*.bin
|
||||
*.cdl
|
||||
*.nes
|
||||
*.nes.deb
|
||||
*.sym
|
||||
*.mlb
|
||||
*.nl
|
11
.travis.yml
11
.travis.yml
|
@ -8,15 +8,8 @@ compiler:
|
|||
script:
|
||||
- cmake . -DCMAKE_BUILD_TYPE=Release
|
||||
- make
|
||||
- cd samples
|
||||
- ../l65 vcs_banks.l65
|
||||
- ../l65 vcs_basic.l65
|
||||
- ../l65 vcs_flush.l65
|
||||
- ../l65 vcs_hello.l65
|
||||
- ../l65 vcs_hooks.l65
|
||||
- ../l65 vcs_music.l65
|
||||
- ../l65 vcs_spr48.l65
|
||||
- cd ..
|
||||
- cd samples; for f in *.l65; do echo $f; ../l65 $f || break; done; cd ..
|
||||
- cd samples; for f in *.l7801; do echo $f; ../l7801 $f || break; done; cd ..
|
||||
- cp l65 l65-$TRAVIS_TAG-$TRAVIS_OS_NAME
|
||||
deploy:
|
||||
provider: releases
|
||||
|
|
|
@ -8,7 +8,7 @@ set(L65_SOURCE_DIR ${PROJECT_SOURCE_DIR})
|
|||
set(L65_BINARY_DIR ${PROJECT_BINARY_DIR})
|
||||
|
||||
set(L65_VERSION_MAJOR "1")
|
||||
set(L65_VERSION_MINOR "2")
|
||||
set(L65_VERSION_MINOR "3")
|
||||
set(L65_VERSION_REVISION "0")
|
||||
set(L65_VERSION "${L65_VERSION_MAJOR}.${L65_VERSION_MINOR}.${L65_VERSION_REVISION}")
|
||||
configure_file("${L65_SOURCE_DIR}/l65cfg.lua.in" "${L65_BINARY_DIR}/l65cfg.lua")
|
||||
|
@ -63,7 +63,7 @@ else ()
|
|||
add_definitions(-D_FILE_OFFSET_BITS=64)
|
||||
|
||||
add_compile_options(-fno-strict-aliasing -fomit-frame-pointer -ffast-math -fvisibility=hidden)
|
||||
add_compile_options(-Wsign-compare -Wno-missing-braces -Wno-unused-result -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-parentheses)
|
||||
add_compile_options(-Wsign-compare -Wno-missing-braces -Wno-unused-result -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-parentheses -Wno-string-plus-int)
|
||||
|
||||
endif()
|
||||
|
||||
|
@ -80,6 +80,7 @@ set(L65_HEADERS
|
|||
|
||||
file(GLOB L65_FILES ${L65_SOURCE_DIR}/*.l65)
|
||||
set(L65_SCRIPTS
|
||||
${L65_SOURCE_DIR}/asm.lua
|
||||
${L65_SOURCE_DIR}/6502.lua
|
||||
${L65_SOURCE_DIR}/dkjson.lua
|
||||
${L65_SOURCE_DIR}/l65.lua
|
||||
|
@ -116,3 +117,42 @@ endif()
|
|||
|
||||
target_link_libraries(embed ${LINKLIBS})
|
||||
target_link_libraries(${PROJECT_NAME} ${LINKLIBS})
|
||||
|
||||
set(L7801_SOURCES
|
||||
${L65_SOURCE_DIR}/lfs.c
|
||||
${L65_SOURCE_DIR}/lpeg.c
|
||||
${L65_SOURCE_DIR}/l7801.c
|
||||
)
|
||||
set(L7801_HEADERS
|
||||
${L65_HEADERS}
|
||||
)
|
||||
|
||||
set(L7801_FILES ${L65_SOURCE_DIR}/scv.l7801)
|
||||
|
||||
set(L7801_SCRIPTS
|
||||
${L65_SOURCE_DIR}/asm.lua
|
||||
${L65_SOURCE_DIR}/uPD7801.lua
|
||||
${L65_SOURCE_DIR}/dkjson.lua
|
||||
${L65_SOURCE_DIR}/l7801.lua
|
||||
${L65_BINARY_DIR}/l65cfg.lua
|
||||
${L65_SOURCE_DIR}/re.lua
|
||||
${L7801_FILES}
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${L65_BINARY_DIR}/scripts_7801.h
|
||||
COMMAND embed -o ${L65_BINARY_DIR}/scripts_7801.h ${L7801_SCRIPTS}
|
||||
DEPENDS embed ${L7801_SCRIPTS}
|
||||
)
|
||||
add_custom_target(prereq_7801 DEPENDS ${L65_BINARY_DIR}/scripts_7801.h)
|
||||
add_executable(l7801 ${L7801_SOURCES} ${L7801_HEADERS} ${L7801_FILES})
|
||||
add_dependencies(l7801 prereq_7801)
|
||||
set_property(TARGET l7801 PROPERTY C_STANDARD 99)
|
||||
target_include_directories(l7801 PRIVATE "${L7801_SOURCE_DIR}" "${L65_BINARY_DIR}")
|
||||
target_link_libraries(l7801 ${LINKLIBS})
|
||||
|
||||
|
||||
install(TARGETS l65 l7801
|
||||
RUNTIME DESTINATION bin)
|
||||
install(DIRECTORY samples DESTINATION doc)
|
||||
install(FILES README.md DESTINATION doc)
|
||||
|
|
30
README.md
30
README.md
|
@ -1,7 +1,7 @@
|
|||
# l65
|
||||
[![Build Status](https://travis-ci.org/g012/l65.svg?branch=master)](https://travis-ci.org/g012/l65)
|
||||
|
||||
l65 is a 6502 assembler, operating from within Lua and written in Lua. This means assembler mnemonics become regular Lua statements anywhere in the middle of Lua code.
|
||||
l7801 is a µPD7801 assembler, using l65 framework, created and maintained by [@MooZ] (https://github.com/BlockoS).
|
||||
|
||||
Table of Contents
|
||||
=================
|
||||
|
@ -47,7 +47,7 @@ Table of Contents
|
|||
* [resolve()](#resolve)
|
||||
* [genbin([filler])](#genbinfiller)
|
||||
* [writebin(filename)](#writebinfilename)
|
||||
* [writesym(filename)](#writesymfilename)
|
||||
* [writesym(filename [, format])](#writesymfilename--format)
|
||||
* [Parser Functions](#parser-functions)
|
||||
* [Pragmas](#pragmas)
|
||||
* [syntax6502 on|off](#syntax6502-onoff)
|
||||
|
@ -66,11 +66,12 @@ Table of Contents
|
|||
* [searcher(name)](#searchername)
|
||||
* [load, loadfile, dofile ; installhooks(), uninstallhooks()](#load-loadfile-dofile--installhooks-uninstallhooks)
|
||||
* [image(filename)](#imagefilename)
|
||||
* [VCS Module](#vcs-module)
|
||||
* [Platform Modules](#platform-modules)
|
||||
* [Building](#building)
|
||||
* [Windows](#windows)
|
||||
* [Linux](#linux)
|
||||
* [Vim files installation](#vim-files-installation)
|
||||
* [Visual Studio Code extension](#vscode-extension)
|
||||
* [TODO List](#todo-list)
|
||||
* [Credits](#credits)
|
||||
* [License](#license)
|
||||
|
@ -105,6 +106,9 @@ Have a look at these files in the `samples` folder to get started with l65:
|
|||
* `vcs_flush.l65`: a port of flewww's famous flush logo from the demo [.bin](http://www.pouet.net/prod.php?which=69666)
|
||||
* `vcs_spr48.l65`: a port of [.bin](http://www.pouet.net/prod.php?which=69666) title 48 pixels sprite animation.
|
||||
* `vcs_music.l65`: imports and plays back a TIATracker .ttt file directly.
|
||||
* `vcs_mcart.l65`: imports all TIATracker .ttt files in the folder supplied as arg, or current directory, and builds a music cart.
|
||||
|
||||
* `nes_hello.l65`: a NES 'Hello World' using BG tiles, and automatic positioned font within a 4kB CHR rom page.
|
||||
|
||||
There's also `vcspal.act`, a palette file for authoring software for VCS. Use this palette or a similar one to create 8b PNG for `l65.image` and helper loaders depending on it. You can generate such a palette, or a GPL one for GIMP using [vcsconv](https://github.com/g012/vcsconv) `authpalette` command.
|
||||
|
||||
|
@ -314,6 +318,8 @@ Check if `v` is within long range, call `error` if it is not, or convert negativ
|
|||
|
||||
`pcall_za`: ...unless this field is set to system's `pcall`. Defaults to module's `pcall`. This field is used only by the `za*` (`zab`, `zax`, `zay`) virtual addressing modes, to discriminate between zeropage and absolute addressing.
|
||||
|
||||
`zeropage`: a function with one number parameter, returning its zeropage byte value if it is within zero/direct page addressing range, nothing otherwise. Set by default to page [0x0000, 0x00ff].
|
||||
|
||||
`symbols`: list of symbols, resolved or not. Values can be anything before the resolve phase, but must be numbers after (except for the metatable fields). Set as the metatable of the 6502 module, which itself should be set as the metatable of the current `_ENV` environment.
|
||||
|
||||
`locations`: list of all the registered locations.
|
||||
|
@ -333,7 +339,7 @@ Check if `v` is within long range, call `error` if it is not, or convert negativ
|
|||
`id()`: return a new unique numeric identifier.
|
||||
|
||||
`stats`: a table of statistics regarding the build:
|
||||
* `cycles`: the total 6502 cycle count of the program, considering no branch is taken and no page is crossed.
|
||||
* `cycles`: the total 6502 cycle count of the program, assuming no branch is taken and no page is crossed.
|
||||
* `used`: the total ROM bytes actually used by the program.
|
||||
* `unused`: total empty ROM space.
|
||||
* `resolved_count`: number of symbols resolved during resolve phase.
|
||||
|
@ -465,9 +471,13 @@ Return a table, where each entry is a byte.
|
|||
|
||||
Write the final binary into `filename`.
|
||||
|
||||
#### writesym(filename)
|
||||
#### writesym(filename [, format])
|
||||
|
||||
Write a DASM symbol file into `filename` for debuggers. The last `_` in a label is turned into a `.`, to get stripping of the prefixed global label working in Stella. As such, it's best to avoid using `_` in local label names, after the initial one.
|
||||
Write one or more symbol files into `filename` (prefix in case of multiple outputs) for debuggers. The last `_` in a label is turned into a `.`, to get stripping of the prefixed global label working in Stella. As such, it's best to avoid using `_` in local label names, after the initial one.
|
||||
|
||||
`format` defaults to 'dasm'.
|
||||
All platforms: 'dasm', 'lua'.
|
||||
NES: 'mesen', 'fceux'.
|
||||
|
||||
### Parser Functions
|
||||
|
||||
|
@ -593,10 +603,12 @@ Return `nil` and an error message on failure, or a table with the following fiel
|
|||
* `height`: image height, in pixels
|
||||
* [1..width\*height]: the pixels, as palette indices (all bytes)
|
||||
|
||||
### VCS Module
|
||||
### Platform Modules
|
||||
|
||||
`vcs.l65` is a helper file for developing on Atari 2600 VCS. It's embedded into the l65 executable. It sets the 6502.lua module as metatable of the current `_ENV`, defines all TIA and PIA symbols, some helper constants and functions, as well as mapper helpers and automatic cross bank call functions. See the samples directory for usage examples, and browse vcs.l65 directly for the list of self-explanatory helpers.
|
||||
|
||||
`nes.l65` is for NES/Famicom. It provides templates for some mappers, including 0-NROM, 1-MMC1, 2-UxROM, 3-CNROM, 4-MMC3, 5-MMC5, and 66-GxROM.
|
||||
|
||||
## Building
|
||||
|
||||
Use CMake to build a standalone executable. Following are basic instructions if you've never used CMake.
|
||||
|
@ -645,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
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
version: '0.1.{build}'
|
||||
|
||||
os: Visual Studio 2013
|
||||
platform: x64
|
||||
configuration: Release
|
||||
|
||||
before_build:
|
||||
- mkdir install
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake -G "Visual Studio 12 2013 Win64" -DCMAKE_INSTALL_PREFIX=../install ..
|
||||
|
||||
build_script:
|
||||
- FOR /F "tokens=*" %%i in ('git describe') do SET COMMITNOW=%%i
|
||||
- if defined APPVEYOR_REPO_TAG_NAME (set L65_RELEASE=true) else (set L65_SNAPSHOT=true)
|
||||
- if defined L65_RELEASE set L65_VERSION=%APPVEYOR_REPO_TAG_NAME:~1%
|
||||
- if defined L65_RELEASE echo Building l65 %L65_VERSION%... (from %COMMITNOW%)
|
||||
- if defined L65_SNAPSHOT set L65_VERSION=%APPVEYOR_BUILD_VERSION%
|
||||
- if defined L65_SNAPSHOT echo Building l65 snapshot %L65_VERSION%... (from %COMMITNOW%)
|
||||
- cmake --build . --config Release
|
||||
- cmake --build . --config Release --target install
|
||||
|
||||
after_build:
|
||||
- cd ../install
|
||||
- 7z a ../l65.zip * -tzip
|
||||
|
||||
artifacts:
|
||||
- path: l65.zip
|
||||
name: l65-${L65_VERSION}.zip
|
||||
|
||||
deploy:
|
||||
- provider: GitHub
|
||||
release: l65-${L65_VERSION}
|
||||
description: 'l65 msvc12 win64 build'
|
||||
auth_token:
|
||||
secure: xRIravp3mvMiAgogn6KGuK1yrolmSJUsum/wPXwu82bh97O7YkuQ3B178ac+WHml
|
||||
artifact: /l65.*\.zip/
|
||||
draft: true
|
||||
on:
|
||||
appveyor_repo_tag: true
|
||||
push_release: true
|
|
@ -0,0 +1,827 @@
|
|||
local M = {}
|
||||
|
||||
local symbols,symbolsorg={},{} M.symbols,M.symbolsorg=symbols,symbolsorg
|
||||
local locations={} M.locations=locations
|
||||
local sections={} M.sections=sections
|
||||
local relations={} M.relations=relations
|
||||
local stats={} M.stats=stats setmetatable(stats, stats)
|
||||
|
||||
local before_link={} M.before_link=before_link
|
||||
|
||||
M.strip = true -- set to false to disable dead stripping of relocatable sections
|
||||
M.strip_empty = false -- set to true to strip empty sections: their label will then not resolve
|
||||
M.pcall = pcall -- set to empty function returning false to disable eval during compute_size()
|
||||
-- set to pcall directly if you want to keep ldazab/x/y eval during compute_size() even if
|
||||
-- disabled for other parts (required to distinguish automatically between zp/abs addressing)
|
||||
M.pcall_za = function(...) return M.pcall(...) end
|
||||
|
||||
M.__index = M
|
||||
M.__newindex = function(t,k,v)
|
||||
local kk = k
|
||||
if type(k) == 'string' and k:sub(1,1) == '_' and M.label_current then
|
||||
kk = M.label_current .. k
|
||||
end
|
||||
if symbols[kk] then error("attempt to modify symbol " .. k) end
|
||||
rawset(t,k,v)
|
||||
end
|
||||
symbols.__index = symbols
|
||||
setmetatable(M, symbols)
|
||||
|
||||
local id_ = 0
|
||||
local id = function() id_=id_+1 return id_ end M.id=id
|
||||
|
||||
M.link = function()
|
||||
if stats.unused then return end
|
||||
|
||||
for _,v in ipairs(before_link) do v() end
|
||||
|
||||
if M.strip then
|
||||
symbols.__index = function(tab,key)
|
||||
local val = rawget(symbols, key)
|
||||
if type(val) == 'table' and val.type == 'label' then
|
||||
val.section.refcount = val.section.refcount + 1
|
||||
end
|
||||
return val
|
||||
end
|
||||
end
|
||||
for _,section in ipairs(sections) do
|
||||
section:compute_size()
|
||||
end
|
||||
symbols.__index = symbols
|
||||
|
||||
local chunk_reserve = function(section, chunk_ix)
|
||||
local chunks = section.location.chunks
|
||||
local chunk = chunks[chunk_ix]
|
||||
local holes = section.holes
|
||||
local new_chunks,ins = {},table.insert
|
||||
|
||||
local chunk1 = { id=id(), start=chunk.start, size=section.org-chunk.start }
|
||||
local hole_ix = 1
|
||||
local hole1 = holes[1]
|
||||
if hole1 and hole1.start==0 then
|
||||
chunk1.size = chunk1.size + hole1.size
|
||||
hole_ix = 2
|
||||
end
|
||||
if chunk1.size > 0 then ins(new_chunks, chunk1) end
|
||||
while hole_ix <= #holes do
|
||||
local hole = holes[hole_ix]
|
||||
local chunki = { id=id(), start=section.org+hole.start, size=hole.size }
|
||||
ins(new_chunks, chunki)
|
||||
hole_ix = hole_ix + 1
|
||||
end
|
||||
local chunkl = { id=id(), start=section.org+section.size, size=chunk.start+chunk.size-(section.org+section.size) }
|
||||
local chunkn = new_chunks[#new_chunks]
|
||||
if chunkn and chunkn.start+chunkn.size==chunkl.start then
|
||||
chunkn.size = chunkn.size + chunkl.size
|
||||
elseif chunkl.size > 0 then
|
||||
ins(new_chunks, chunkl)
|
||||
end
|
||||
|
||||
table.remove(chunks, chunk_ix)
|
||||
for i=chunk_ix,chunk_ix+#new_chunks-1 do ins(chunks, i, new_chunks[i-chunk_ix+1]) end
|
||||
end
|
||||
|
||||
local chunk_from_address = function(section, address)
|
||||
local chunks,rorg = section.location.chunks,section.location.rorg
|
||||
for i,chunk in ipairs(chunks) do
|
||||
if address >= chunk.start and address+section.size <= chunk.start+chunk.size then
|
||||
return chunk,i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local check_section_position = function(section, address, chunk)
|
||||
local chunk = chunk_from_address(section, address)
|
||||
if not chunk then return end
|
||||
local rorg = section.location.rorg
|
||||
if section.align then
|
||||
local raddress = rorg(address)
|
||||
if section.offset then raddress = raddress - section.offset end
|
||||
if raddress % section.align ~= 0 then return end
|
||||
end
|
||||
for _,constraint in ipairs(section.constraints) do
|
||||
local cstart, cfinish = address+constraint.start, address+constraint.finish
|
||||
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
|
||||
end
|
||||
end
|
||||
local address_end = address+section.size
|
||||
local waste = math.min(address - chunk.start, chunk.size - (address_end - chunk.start))
|
||||
local raddress,raddress_end = rorg(address),rorg(address_end)
|
||||
local align,cross=0x100,0
|
||||
repeat
|
||||
local cross_count = (raddress_end+align-1)//align - (raddress+align-1)//align
|
||||
if raddress&(align-1) == 0 then cross_count=cross_count+1 end
|
||||
cross = cross + align * align * cross_count
|
||||
align = align>>1
|
||||
until align==1
|
||||
local lsb=0
|
||||
for i=0,15 do if raddress&(1<<i) == 0 then lsb=lsb+1 else break end end
|
||||
return waste, cross, lsb
|
||||
end
|
||||
|
||||
local position_section = function(section, constrain)
|
||||
local location = section.location
|
||||
local chunks,rorg = location.chunks,location.rorg
|
||||
table.sort(chunks, function(a,b) if a.size==b.size then return a.id<b.id end return a.size<b.size end)
|
||||
for chunk_ix,chunk in ipairs(chunks) do if chunk.size >= section.size then
|
||||
local waste,cross,lsb,position = math.maxinteger,math.maxinteger,math.maxinteger
|
||||
local usage_lowest = function(start, finish)
|
||||
local inc=1
|
||||
if section.align then
|
||||
local rstart = rorg(start)
|
||||
local arstart = (rstart + section.align - 1) // section.align * section.align
|
||||
if section.offset then arstart = arstart + section.offset end
|
||||
start = start + arstart-rstart
|
||||
inc = section.align
|
||||
end
|
||||
for address=start,finish,inc do
|
||||
local nwaste, ncross, nlsb = check_section_position(section, address, chunk)
|
||||
if nwaste then
|
||||
if constrain then
|
||||
nwaste, ncross, nlsb = constrain(address, nwaste, ncross, nlsb)
|
||||
if not nwaste then goto skip end
|
||||
end
|
||||
if nwaste > waste then goto skip end
|
||||
if nwaste == waste then
|
||||
-- if waste is the same, keep the one that uses the least amount of aligned addresses
|
||||
if ncross > cross then goto skip end
|
||||
if ncross == cross then
|
||||
-- if cross count is same, take the one with the most set LSB count (eg. select 11 over 10)
|
||||
if nlsb > lsb then goto skip end
|
||||
end
|
||||
end
|
||||
position,waste,cross,lsb = address,nwaste,ncross,nlsb
|
||||
::skip::
|
||||
end
|
||||
end
|
||||
end
|
||||
local finish = math.min(chunk.start + 0xff, chunk.start + chunk.size - section.size)
|
||||
usage_lowest(chunk.start, finish)
|
||||
if chunk.size ~= math.huge then
|
||||
local start = math.max(chunk.start + chunk.size - section.size - 0xff, chunk.start)
|
||||
usage_lowest(start, chunk.start + chunk.size - section.size)
|
||||
end
|
||||
if position then
|
||||
section.org = position
|
||||
chunk_reserve(section, chunk_ix)
|
||||
--print(section.label, string.format("%04X\t%d", position, section.size))
|
||||
--for k,v in ipairs(location.chunks) do print(string.format(" %04X %04X %d", v.start, v.size+v.start-1, v.size)) end
|
||||
return position
|
||||
end
|
||||
end end
|
||||
end
|
||||
|
||||
stats.used = 0
|
||||
stats.unused = 0
|
||||
stats.cycles = 0
|
||||
local related_sections = {}
|
||||
for _,location in ipairs(locations) do
|
||||
local sections,rorg = location.sections,location.rorg
|
||||
|
||||
-- filter sections list
|
||||
local position_independent_sections = {}
|
||||
local symbols_to_remove = {}
|
||||
local section_count = #sections
|
||||
location.cycles=0 location.used=0
|
||||
for ix,section in ipairs(sections) do
|
||||
location.cycles = location.cycles + section.cycles
|
||||
location.used = location.used + section.size
|
||||
if section.size == 0 then
|
||||
if M.strip_empty or section.weak then
|
||||
sections[ix]=nil
|
||||
if not section.org then table.insert(symbols_to_remove, section.label) end
|
||||
else
|
||||
section.org = location.start
|
||||
end
|
||||
elseif not section.org then
|
||||
if M.strip and not section.refcount and not section.strong then
|
||||
sections[ix]=nil
|
||||
table.insert(symbols_to_remove, section.label)
|
||||
elseif section.related then
|
||||
table.insert(related_sections, section)
|
||||
else
|
||||
table.insert(position_independent_sections, section)
|
||||
end
|
||||
end
|
||||
end
|
||||
do local j=0 for i=1,section_count do
|
||||
if sections[i] ~= nil then j=j+1 sections[j],sections[i] = sections[i],sections[j] end
|
||||
end end
|
||||
for _,v in ipairs(symbols_to_remove) do symbols[v] = nil end
|
||||
location.position_independent_sections = position_independent_sections
|
||||
stats.cycles = stats.cycles + location.cycles
|
||||
stats.used = stats.used + location.used
|
||||
|
||||
-- fixed position sections
|
||||
for section_ix,section in ipairs(sections) do if section.org then
|
||||
if section.org < location.start or section.org > (location.finish or math.huge) then
|
||||
error("ORG section " .. section.label .. " starts outside container location")
|
||||
end
|
||||
for chunk_ix,chunk in ipairs(location.chunks) do
|
||||
if chunk.start <= section.org and chunk.size - (section.org - chunk.start) >= section.size then
|
||||
chunk_reserve(section, chunk_ix)
|
||||
goto chunk_located
|
||||
end
|
||||
end
|
||||
error("ORG section " .. section.label .. " overflows its location")
|
||||
::chunk_located::
|
||||
end end
|
||||
end
|
||||
|
||||
table.sort(related_sections, function(a,b) if a.size==b.size then return a.id<b.id end return a.size>b.size end)
|
||||
for _,section in ipairs(related_sections) do if not section.org then
|
||||
local related,ins = {},table.insert
|
||||
local function collect(section_parent, offset)
|
||||
local relatives = relations[section_parent]
|
||||
if relatives then
|
||||
for relative,relative_offset in pairs(relatives) do
|
||||
if not related[relative] and relative ~= section then
|
||||
relative_offset = relative_offset + offset
|
||||
related[relative] = relative_offset
|
||||
collect(relative, relative_offset)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
collect(section, 0)
|
||||
local location_start = section.location.start
|
||||
local position = position_section(section, function(address, waste, cross, lsb)
|
||||
local waste, cross, lsb = 0, 0, 0
|
||||
for section,offset in pairs(related) do
|
||||
local section_address = address + (section.location.start - location_start) + offset
|
||||
local nwaste, ncross, nlsb = check_section_position(section, section_address)
|
||||
if not nwaste then return end
|
||||
waste, cross, lsb = waste+nwaste, cross+ncross, lsb+nlsb
|
||||
end
|
||||
return waste, cross, lsb
|
||||
end)
|
||||
if not position then
|
||||
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
|
||||
local chunk,chunk_ix = chunk_from_address(section, section.org)
|
||||
chunk_reserve(section, chunk_ix)
|
||||
end
|
||||
end end
|
||||
|
||||
for _,location in ipairs(locations) do
|
||||
local position_independent_sections = location.position_independent_sections
|
||||
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 .. "' of size " .. section.size)
|
||||
end
|
||||
end
|
||||
|
||||
-- unused space stats
|
||||
local unused = 0
|
||||
for _,chunk in ipairs(location.chunks) do
|
||||
if chunk.size ~= math.huge then
|
||||
unused = unused + chunk.size
|
||||
else
|
||||
location.stops_at = chunk.start-1
|
||||
end
|
||||
end
|
||||
location.unused = unused
|
||||
stats.unused = stats.unused + unused
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
M.resolve = function()
|
||||
if stats.resolved_count then return end
|
||||
M.link()
|
||||
|
||||
stats.resolved_count = 0
|
||||
repeat local count = 0
|
||||
for k,v in pairs(symbols) do if k ~= '__index' then
|
||||
local t = type(v)
|
||||
if t == 'function' then v=v() t=type(v) symbols[k]=v count=count+1 end
|
||||
if t == 'table' and type(v.resolve) == 'function' then symbols[k],symbolsorg[k]=v.resolve() count=count+1 end
|
||||
if t == 'string' and symbols[v] then symbols[k]=symbols[v] count=count+1 end
|
||||
stats.resolved_count = stats.resolved_count + count
|
||||
end end until count == 0
|
||||
|
||||
-- set local label references resolver
|
||||
local llresolver = { __index = function(tab,key)
|
||||
if type(key) ~= 'string' or key:sub(1,1) ~= '_' or not M.label_current then return nil end
|
||||
return symbols[M.label_current .. key]
|
||||
end }
|
||||
setmetatable(symbols, llresolver)
|
||||
end
|
||||
|
||||
M.genbin = function(filler)
|
||||
if #locations == 0 then return end
|
||||
if not filler then filler = 0 end -- brk opcode
|
||||
M.resolve()
|
||||
local bin = {}
|
||||
local ins,mov = table.insert,table.move
|
||||
table.sort(locations, function(a,b) return a.start < b.start end)
|
||||
local of0 = locations[1].start
|
||||
local fill
|
||||
for _,location in ipairs(locations) do
|
||||
if location.start < #bin then
|
||||
error(string.format("location [%04x,%04x] overlaps another", location.start, location.finish or location.stops_at))
|
||||
end
|
||||
if fill then for i=#bin+of0,location.start-1 do ins(bin, filler) end end
|
||||
M.size=0 M.cycles=0
|
||||
local sections = location.sections
|
||||
table.sort(sections, function(a,b) return a.org < b.org end)
|
||||
for _,section in ipairs(sections) do
|
||||
for i=#bin+of0,section.org-1 do ins(bin, filler) end
|
||||
local bin_offset = math.min(#bin, section.org-of0)+1
|
||||
for _,instruction in ipairs(section.instructions) do
|
||||
local b,o = instruction.bin
|
||||
if type(b) == 'function' then b,o = b(filler) end
|
||||
if type(b) == 'table' then mov(b,1,#b,bin_offset,bin) bin_offset=bin_offset+#b
|
||||
elseif b then bin[bin_offset]=b bin_offset=bin_offset+1 end
|
||||
if o then
|
||||
bin_offset=bin_offset+o
|
||||
for i=#bin,bin_offset-1 do ins(bin, filler) end
|
||||
end
|
||||
M.size=#bin M.cycles=M.cycles+(instruction.cycles or 0)
|
||||
end
|
||||
end
|
||||
fill = not location.nofill
|
||||
if location.finish and fill then
|
||||
for i=#bin+of0,location.finish do ins(bin, filler) end
|
||||
end
|
||||
end
|
||||
stats.bin_size = #bin
|
||||
return bin
|
||||
end
|
||||
|
||||
M.writebin = function(filename, bin)
|
||||
if not filename then filename = 'main.bin' end
|
||||
if not bin then bin = M.genbin() end
|
||||
local f = assert(io.open(filename, "wb"), "failed to open " .. filename .. " for writing")
|
||||
f:write(string.char(table.unpack(bin)))
|
||||
f:close()
|
||||
end
|
||||
|
||||
-- return a table of entry(address, label)
|
||||
M.getsym = function(entry)
|
||||
local ins = table.insert
|
||||
local s,sym_rev = {},{}
|
||||
for k,v in pairs(symbols) do if type(v) == 'number' then ins(sym_rev,k) end end
|
||||
table.sort(sym_rev, function(a,b) local x,y=symbols[a],symbols[b] if x==y then return a<b end return x<y end)
|
||||
for _,v in ipairs(sym_rev) do
|
||||
local k,vorg=symbols[v],v
|
||||
local u=v:match'.*()_' if u then -- change _ to . in local labels
|
||||
local parent=v:sub(1,u-1) if symbols[parent] then v = parent..'.'..v:sub(u+1) end
|
||||
end
|
||||
local e = entry(k,v,vorg) if e then
|
||||
if type(e) == 'table' then for _,ev in ipairs(e) do ins(s, ev) end
|
||||
else ins(s, e) end
|
||||
end
|
||||
end
|
||||
return s
|
||||
end
|
||||
M.getsym_as = {
|
||||
lua = function() -- .lua
|
||||
local fmt,rep = string.format,string.rep
|
||||
local s = M.getsym(function(a,l) return fmt("%s = 0x%04x", l, a) end)
|
||||
return table.concat(s, '\n')
|
||||
end,
|
||||
dasm = function() -- .sym
|
||||
local fmt,rep = string.format,string.rep
|
||||
local s = M.getsym(function(a,l) return fmt("%s%s %04x", l, rep(' ',24-#l), a) end)
|
||||
table.insert(s, 1, '--- Symbol List')
|
||||
s[#s+1] = '--- End of Symbol List.'
|
||||
return table.concat(s, '\n')
|
||||
end,
|
||||
}
|
||||
-- write a symbol file for debuggers, using specified format (defaults to DASM)
|
||||
M.writesym = function(filename, format)
|
||||
assert(filename)
|
||||
local s = M.getsym_as[format or 'dasm'](filename)
|
||||
if s then
|
||||
local f = assert(io.open(filename, "wb"), "failed to open " .. filename .. " for writing")
|
||||
f:write(s) f:close()
|
||||
end
|
||||
end
|
||||
|
||||
stats.__tostring = function()
|
||||
local s,ins={},table.insert
|
||||
ins(s, " Free Used Size Area")
|
||||
for _,location in ipairs(locations) do
|
||||
local name = (location.name or ''):sub(1,14)
|
||||
name = string.rep(' ', 14-#name) .. name
|
||||
local fmt = "%s %5d %5d %5d [%04X-%04X]"
|
||||
if location.finish then
|
||||
local size = location.finish-location.start+1
|
||||
ins(s, string.format(fmt, name,
|
||||
location.unused, size-location.unused, size, location.start, location.finish))
|
||||
else
|
||||
ins(s, string.format(fmt, name,
|
||||
location.unused, location.used, location.stops_at-location.start+1, location.start, location.stops_at))
|
||||
end
|
||||
end
|
||||
if #locations > 1 then
|
||||
ins(s, string.format(" --- Total --- %5d %5d %5d", stats.unused, stats.used, stats.bin_size))
|
||||
end
|
||||
return table.concat(s, '\n')
|
||||
end
|
||||
|
||||
M.location = function(start, finish)
|
||||
local location
|
||||
if type(start) ~= 'table' then
|
||||
location = { start=start, finish=finish }
|
||||
else
|
||||
if start.type == 'location' then
|
||||
for _,v in ipairs(locations) do if v == start then
|
||||
M.location_current = start
|
||||
return start
|
||||
end end
|
||||
error("unable to find reference to location [" .. (start.start or '?') .. ", " .. (start.finish or '?') .. "]")
|
||||
end
|
||||
location = start
|
||||
location.start = start[1]
|
||||
location.finish = start[2]
|
||||
if type(location.rorg) == 'number' then
|
||||
local offset = location.rorg - location.start
|
||||
location.rorg = function(x) return x+offset end
|
||||
end
|
||||
end
|
||||
location.type = 'location'
|
||||
location.sections = {}
|
||||
if not location.rorg then location.rorg = function(x) return x end end
|
||||
local size = (location.finish or math.huge) - location.start + 1
|
||||
location.chunks={ { id=id(), start=location.start, size=size } }
|
||||
locations[#locations+1] = location
|
||||
M.location_current = location
|
||||
return location
|
||||
end
|
||||
|
||||
M.section = function(t)
|
||||
local section = {}
|
||||
local name = t or 'S'..id()
|
||||
if type(name) ~= 'string' then
|
||||
assert(type(t) == 'table', "invalid arguments for section")
|
||||
if t.type == 'section' then
|
||||
for _,v in ipairs(sections) do if v == t then
|
||||
M.location_current = t.location
|
||||
M.section_current = t
|
||||
return t
|
||||
end end
|
||||
error("unable to find reference to section " .. (t.label or '?'))
|
||||
end
|
||||
section=t name=t[1] or 'S'..id() section[1]=nil
|
||||
if section.offset and not section.align then error("section " .. name .. " has offset, but no align") end
|
||||
end
|
||||
table.insert(M.location_current.sections, section)
|
||||
table.insert(M.sections, section)
|
||||
section.location = M.location_current
|
||||
M.section_current = section
|
||||
section.type = 'section'
|
||||
section.id = id()
|
||||
section.constraints = {}
|
||||
section.instructions = {}
|
||||
assert(name:sub(1,1) ~= '_', "sections can't be named with a local label")
|
||||
section.label = M.label(name)
|
||||
section.holes = {}
|
||||
section.refcount = 0
|
||||
function section:compute_size()
|
||||
local instructions = self.instructions
|
||||
self.size=0 self.cycles=0
|
||||
for _,instruction in ipairs(instructions) do
|
||||
instruction.offset = self.size
|
||||
local ins_sz = instruction.size or 0
|
||||
if type(ins_sz) == 'function' then
|
||||
-- evaluation is needed to get the size (distinguish zpg/abs)
|
||||
-- labels and sections are not resolved at this point, so
|
||||
-- evaluation will fail if the size is not explicitly stated (.b/.w);
|
||||
-- in that case, assume max size
|
||||
ins_sz = ins_sz()
|
||||
end
|
||||
self.size = self.size + ins_sz
|
||||
self.cycles = self.cycles + (instruction.cycles or 0)
|
||||
end
|
||||
for _,constraint in ipairs(self.constraints) do
|
||||
constraint.start = instructions[constraint.from].offset
|
||||
constraint.finish = constraint.to==#instructions and self.size or instructions[constraint.to+1].offset
|
||||
end
|
||||
end
|
||||
return section
|
||||
end
|
||||
|
||||
-- relate(section1, section2 [, [offset1,] offset2])
|
||||
-- Add a position relationship between 'section1' and 'section2', with 'offset1'
|
||||
-- bytes from selected position for 'section2', and 'offset2' bytes from selec-
|
||||
-- -ted positon for 'section1'.
|
||||
-- If offset1 is omitted, -offset2 is used.
|
||||
M.relate = function(section1, section2, offset, offset2)
|
||||
assert(section1.type == 'section', "section1 is not a section")
|
||||
assert(section2.type == 'section', "section2 is not a section")
|
||||
local rel1 = relations[section1] or {}
|
||||
rel1[section2] = (offset2 or offset) or 0
|
||||
relations[section1] = rel1
|
||||
local rel2 = relations[section2] or {}
|
||||
rel2[section1] = (offset2 and offset) or -rel1[section2]
|
||||
relations[section2] = rel2
|
||||
section1.related = true
|
||||
section2.related = true
|
||||
end
|
||||
|
||||
M.label = function(name)
|
||||
local label,offset
|
||||
local section,rorg = M.section_current,M.location_current.rorg
|
||||
label = { type='label', section=section }
|
||||
if not name then name='_L'..id() end
|
||||
if name:sub(1,1) == '_' then -- local label
|
||||
name = M.label_current .. name
|
||||
else
|
||||
M.label_current = name
|
||||
label.bin = function() M.label_current = name end
|
||||
end
|
||||
if symbols[name] then error("duplicate symbol: " .. name) end
|
||||
symbols[name] = label
|
||||
label.label = name
|
||||
label.size = function()
|
||||
offset = section.size
|
||||
label.size = 0
|
||||
return 0
|
||||
end
|
||||
label.resolve = function()
|
||||
local o = section.org + offset
|
||||
return rorg(o),o
|
||||
end
|
||||
table.insert(section.instructions, label)
|
||||
return name,label
|
||||
end
|
||||
|
||||
M.samepage = function()
|
||||
local section = M.section_current
|
||||
table.insert(section.constraints, { type='samepage', from=#section.instructions+1 })
|
||||
end
|
||||
M.crosspage = function()
|
||||
local section = M.section_current
|
||||
table.insert(section.constraints, { type='crosspage', from=#section.instructions+1 })
|
||||
end
|
||||
M.endpage = function()
|
||||
local section = M.section_current
|
||||
local constraint = section.constraints[#section.constraints]
|
||||
assert(constraint and not constraint.to, "closing constraint, but no constraint is open")
|
||||
constraint.to = #section.instructions
|
||||
end
|
||||
|
||||
-- skip(bytes)
|
||||
-- Insert a hole in the section of 'bytes' bytes, which can be used by other
|
||||
-- relocatable sections.
|
||||
M.skip = function(bytes)
|
||||
local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local ins,section = {},M.section_current
|
||||
ins.size = function()
|
||||
table.insert(section.holes, { start=ins.offset, size=bytes })
|
||||
return bytes
|
||||
end
|
||||
ins.bin = function(filler) return nil,bytes end
|
||||
table.insert(section.instructions, ins)
|
||||
end
|
||||
|
||||
-- sleep(cycles [, noillegal])
|
||||
-- Waste 'cycles' cycles. If 'noillegal' is true, trashes NZ flags.
|
||||
M.sleep = function(cycles, noillegal)
|
||||
assert(cycles > 1, "can't sleep for less than 2 cycles")
|
||||
if cycles & 1 ~= 0 then
|
||||
if noillegal then bitzpg(0) else nopzpg(0) end
|
||||
cycles = cycles - 3
|
||||
end
|
||||
for i=1,cycles/2 do nopimp() end
|
||||
end
|
||||
|
||||
local op_resolve = function(v)
|
||||
if type(v) == 'function' then v=v() end
|
||||
if type(v) == 'table' and v.label then v = symbols[v.label] end
|
||||
if type(v) == 'string' then v = symbols[v] end
|
||||
if type(v) ~= 'number' then error("unresolved symbol: " .. tostring(v)) end
|
||||
return v
|
||||
end M.op_resolve = op_resolve
|
||||
|
||||
local size_ref = function(v)
|
||||
if type(v) == 'string' then v=symbols[v] end
|
||||
if type(v) == 'table' and v.type == 'label' then v.section.refcount = 1 + (v.section.refcount or 0) end
|
||||
end
|
||||
M.size_ref = size_ref
|
||||
|
||||
local size_dc = function(v)
|
||||
if type(v) == 'function' then
|
||||
local r,x = M.pcall(v)
|
||||
if not r or not x then return v end
|
||||
end
|
||||
size_ref(v)
|
||||
return v
|
||||
end
|
||||
M.size_dc = size_dc
|
||||
|
||||
local size_op = function(late, early)
|
||||
if type(late) == 'function' then
|
||||
local r,x = M.pcall(late, early or 0, op_resolve)
|
||||
if not r or not x then return late,early end
|
||||
late=x early=nil
|
||||
end
|
||||
size_ref(late) size_ref(early)
|
||||
return late,early
|
||||
end
|
||||
M.size_op = size_op
|
||||
|
||||
local byte_normalize = function(v)
|
||||
if v < -0x80 or v > 0xFF then error("value out of byte range: " .. v) end
|
||||
if v < 0 then v = v + 0x100 end
|
||||
return v & 0xff
|
||||
end
|
||||
M.byte_normalize = byte_normalize
|
||||
|
||||
local word_normalize = function(v)
|
||||
if v < -0x8000 or v > 0xFFFF then error("value out of word range: " .. v) end
|
||||
if v < 0 then v = v + 0x10000 end
|
||||
return v & 0xffff
|
||||
end
|
||||
M.word_normalize = word_normalize
|
||||
|
||||
local long_normalize = function(v)
|
||||
if v < -0x80000000 or v > 0xFFFFFFFF then error("value out of word range: " .. v) end
|
||||
if v < 0 then v = v + 0x100000000 end
|
||||
return v & 0xffffffff
|
||||
end
|
||||
M.long_normalize = long_normalize
|
||||
|
||||
-- charset([s] [, f])
|
||||
-- Set a new charset to be used for next string data in byte().
|
||||
-- Without argument, revert to Lua charset.
|
||||
-- s: string of all letters of charset
|
||||
-- f: letter index offset or function to transform the letter index
|
||||
M.charset = function(s, f)
|
||||
local st = type(s)
|
||||
if st == 'nil' then M.cs = nil return s end
|
||||
if st == 'table' then M.cs = s return s end
|
||||
if not f then f = function(v) return v end
|
||||
elseif type(f) == 'number' then f = function(v) return v + f end end
|
||||
local t,i={},0
|
||||
for c in s:gmatch'.' do local v=i t[c]=function() return f(v) end i=i+1 end
|
||||
M.cs=t
|
||||
return t
|
||||
end
|
||||
|
||||
M.byte_impl = function(args, nrm)
|
||||
local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local data,cs = {},M.cs
|
||||
for k,v in ipairs(args) do
|
||||
local t = type(v)
|
||||
if t == 'number' or t == 'function' then data[#data+1] = v
|
||||
elseif t == 'table' then table.move(v,1,#v,#data+1,data)
|
||||
elseif t == 'string' then
|
||||
if cs then
|
||||
for c in v:gmatch'.' do
|
||||
local i=cs[c]
|
||||
if not i then error("character " .. c .. " is not part of current charset") end
|
||||
data[#data+1]=i
|
||||
end
|
||||
else
|
||||
local s = {v:byte(1,#v)}
|
||||
table.move(s, 1, #s, #data+1, data)
|
||||
end
|
||||
else error("unsupported type for byte() argument: " .. t .. ", value: " .. v)
|
||||
end
|
||||
end
|
||||
local size = function()
|
||||
for i,v in ipairs(data) do data[i] = size_dc(v) end
|
||||
return #data
|
||||
end
|
||||
local bin = function() local l65dbg=l65dbg
|
||||
local b={}
|
||||
for k,v in ipairs(data) do
|
||||
if type(v) == 'function' then v = v() end
|
||||
local vt = type(v)
|
||||
if vt == 'table' and v.label then v = symbols[v.label]
|
||||
elseif vt == 'string' then v = symbols[v] end
|
||||
if type(v) ~= 'number' then error("unresolved symbol for dc.b, index " .. k) end
|
||||
b[#b+1] = nrm(v)
|
||||
end
|
||||
return b
|
||||
end
|
||||
table.insert(M.section_current.instructions, { data=data, size=size, bin=bin })
|
||||
end
|
||||
-- byte(...)
|
||||
-- Declare bytes to go into the binary stream.
|
||||
-- Each argument can be either:
|
||||
-- * a number resolving to a valid range byte
|
||||
-- * a string, converted to bytes using the charset previously defined,
|
||||
-- or Lua's charset if none was defined
|
||||
-- * a table, with each entry resolving to a valid range byte
|
||||
-- * a function, resolving to exactly one valid range byte, evaluated
|
||||
-- after symbols have been resolved
|
||||
M.byte = function(...)
|
||||
return M.byte_impl({...}, byte_normalize)
|
||||
end
|
||||
local byte_encapsulate = function(args)
|
||||
for k,v in ipairs(args) do
|
||||
local vt = type(v)
|
||||
if vt == 'string' or vt == 'table' and (v.type == 'section' or v.type == 'label') then
|
||||
args[k] = function() return v end
|
||||
end
|
||||
end
|
||||
return args
|
||||
end
|
||||
M.byte_hi = function(...)
|
||||
return M.byte_impl(byte_encapsulate{...}, function(v) return (v>>8)&0xff end)
|
||||
end
|
||||
M.byte_lo = function(...)
|
||||
return M.byte_impl(byte_encapsulate{...}, function(v) return v&0xff end)
|
||||
end
|
||||
|
||||
-- word(...)
|
||||
-- Declare words to go into the binary stream.
|
||||
-- Each argument can be either:
|
||||
-- * a section or a label
|
||||
-- * a number resolving to a valid range word
|
||||
-- * a table, with each entry resolving to a valid range word
|
||||
-- * a function, resolving to exactly one valid range word, evaluated
|
||||
-- after symbols have been resolved
|
||||
M.word = function(...)
|
||||
local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local args = {...}
|
||||
local data = {}
|
||||
for k,v in ipairs(args) do
|
||||
local t = type(v)
|
||||
if t == 'number' or t == 'function' or t == 'string' then data[#data+1] = v
|
||||
elseif t == 'table' then
|
||||
if v.type == 'section' or v.type == 'label' then data[#data+1] = function() return v end
|
||||
else table.move(v,1,#v,#data+1,data) end
|
||||
else error("unsupported type for word() argument: " .. t .. ", value: " .. v)
|
||||
end
|
||||
end
|
||||
local size = function()
|
||||
for i,v in ipairs(data) do data[i] = size_dc(v) end
|
||||
return #data*2
|
||||
end
|
||||
local bin = function() local l65dbg=l65dbg
|
||||
local b={}
|
||||
for k,v in ipairs(data) do
|
||||
if type(v) == 'function' then v = v() end
|
||||
local vt = type(v)
|
||||
if vt == 'table' and v.label then v = symbols[v.label]
|
||||
elseif vt == 'string' then v = symbols[v] end
|
||||
if type(v) ~= 'number' then error("unresolved symbol for dc.w, index " .. k) end
|
||||
v = word_normalize(v)
|
||||
b[#b+1] = v&0xff
|
||||
b[#b+1] = v>>8
|
||||
end
|
||||
return b
|
||||
end
|
||||
table.insert(M.section_current.instructions, { data=data, size=size, bin=bin })
|
||||
end
|
||||
|
||||
M.long = function(...)
|
||||
local l65dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local args = {...}
|
||||
local data = {}
|
||||
for k,v in ipairs(args) do
|
||||
local t = type(v)
|
||||
if t == 'number' or t == 'function' or t == 'string' then data[#data+1] = v
|
||||
elseif t == 'table' then
|
||||
if v.type == 'section' or v.type == 'label' then data[#data+1] = function() return v end
|
||||
else table.move(v,1,#v,#data+1,data) end
|
||||
else error("unsupported type for long() argument: " .. t .. ", value: " .. v)
|
||||
end
|
||||
end
|
||||
local size = function()
|
||||
for i,v in ipairs(data) do data[i] = size_dc(v) end
|
||||
return #data*4
|
||||
end
|
||||
local bin = function() local l65dbg=l65dbg
|
||||
local b={}
|
||||
for k,v in ipairs(data) do
|
||||
if type(v) == 'function' then v = v() end
|
||||
local vt = type(v)
|
||||
if vt == 'table' and v.label then v = symbols[v.label]
|
||||
elseif vt == 'string' then v = symbols[v] end
|
||||
if type(v) ~= 'number' then error("unresolved symbol for dc.l, index " .. k) end
|
||||
v = long_normalize(v)
|
||||
b[#b+1] = v&0xff
|
||||
b[#b+1] = (v>>8)&0xff
|
||||
b[#b+1] = (v>>16)&0xff
|
||||
b[#b+1] = v>>24
|
||||
end
|
||||
return b
|
||||
end
|
||||
table.insert(M.section_current.instructions, { data=data, size=size, bin=bin })
|
||||
end
|
||||
|
||||
local op = function(code, cycles, extra_on_crosspage)
|
||||
return { opc=code, cycles=cycles or cycles_def, xcross=extra_on_crosspage or xcross_def }
|
||||
end
|
||||
M.op = op
|
||||
|
||||
local op_eval = function(late, early)
|
||||
local x = early or 0
|
||||
return type(late) == 'function' and late(x,op_resolve) or x+op_resolve(late)
|
||||
end
|
||||
M.op_eval = op_eval
|
||||
|
||||
return M
|
|
@ -0,0 +1,120 @@
|
|||
const std = @import("std");
|
||||
|
||||
const major: u32 = 1;
|
||||
const minor: u32 = 3;
|
||||
const revision: u32 = 0;
|
||||
|
||||
const targets: []const std.Target.Query = &.{
|
||||
.{ .os_tag = .windows, .cpu_arch = .x86_64, .cpu_model = .baseline },
|
||||
.{ .os_tag = .windows, .cpu_arch = .aarch64, .cpu_model = .baseline },
|
||||
.{ .os_tag = .macos, .cpu_arch = .x86_64, .cpu_model = .baseline, .os_version_min = std.zig.CrossTarget.OsVersion{ .semver = .{ .major = 10, .minor = 7, .patch = 0 } } },
|
||||
.{ .os_tag = .macos, .cpu_arch = .aarch64, .cpu_model = .baseline, .os_version_min = std.zig.CrossTarget.OsVersion{ .semver = .{ .major = 11, .minor = 0, .patch = 0 } } },
|
||||
.{ .os_tag = .linux, .cpu_arch = .x86_64, .cpu_model = .baseline, .abi = .musl },
|
||||
.{ .os_tag = .linux, .cpu_arch = .aarch64, .cpu_model = .baseline },
|
||||
};
|
||||
|
||||
fn setupExe(exe: *std.Build.Step.Compile, embed_output: *const std.Build.LazyPath) !void {
|
||||
exe.addCSourceFiles(.{
|
||||
.files = &.{ "lfs.c", "lpeg.c", "main.c" },
|
||||
.flags = &.{},
|
||||
});
|
||||
|
||||
exe.addIncludePath(embed_output.dirname());
|
||||
|
||||
exe.linkLibC();
|
||||
}
|
||||
|
||||
pub fn build(b: *std.Build) !void {
|
||||
const target = b.standardTargetOptions(.{});
|
||||
const optimize = b.standardOptimizeOption(.{});
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Embed tool
|
||||
|
||||
const version_storage = struct { var str: [32]u8 = undefined; };
|
||||
const version = try std.fmt.bufPrint(version_storage.str[0..], "{d}.{d}.{d}", .{ major, minor, revision });
|
||||
const l65cfg_lua_in = b.addConfigHeader(
|
||||
.{
|
||||
.style = .{ .cmake = .{ .path = "l65cfg.lua.in" } },
|
||||
.include_path = "l65cfg.lua",
|
||||
},
|
||||
.{
|
||||
.L65_VERSION_MAJOR = major,
|
||||
.L65_VERSION_MINOR = minor,
|
||||
.L65_VERSION_REVISION = revision,
|
||||
.L65_VERSION = version,
|
||||
},
|
||||
);
|
||||
// FIXME current version (0.12-dev) of ConfigHeader adds a C comment on top of file
|
||||
// - comment "try output.appendSlice(c_generated_line);" in ConfigHeader.zig to avoid it
|
||||
|
||||
|
||||
const embed_exe = b.addExecutable(.{
|
||||
.name = "embed",
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
embed_exe.addCSourceFile(.{ .file = .{ .path = "embed.c" }, .flags = &.{ "-std=c99" } });
|
||||
embed_exe.linkLibC();
|
||||
|
||||
const embed = b.addRunArtifact(embed_exe);
|
||||
embed.step.dependOn(&l65cfg_lua_in.step);
|
||||
embed.addArg("-o");
|
||||
const embed_output = embed.addOutputFileArg("scripts.h");
|
||||
embed.addFileArg(l65cfg_lua_in.getOutput());
|
||||
embed.addFileArg(.{ .path = "asm.lua" });
|
||||
embed.addFileArg(.{ .path = "6502.lua" });
|
||||
embed.addFileArg(.{ .path = "dkjson.lua" });
|
||||
embed.addFileArg(.{ .path = "l65.lua" });
|
||||
embed.addFileArg(.{ .path = "re.lua" });
|
||||
embed.addFileArg(.{ .path = "nes.l65" });
|
||||
embed.addFileArg(.{ .path = "pce.l65" });
|
||||
embed.addFileArg(.{ .path = "vcs.l65" });
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Build for current machine
|
||||
|
||||
var exe = b.addExecutable(.{
|
||||
.name = "l65",
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
|
||||
exe.step.dependOn(&embed.step);
|
||||
try setupExe(exe, &embed_output);
|
||||
b.installArtifact(exe);
|
||||
|
||||
const run_cmd = b.addRunArtifact(exe);
|
||||
run_cmd.step.dependOn(b.getInstallStep());
|
||||
if (b.args) |args| {
|
||||
run_cmd.addArgs(args);
|
||||
}
|
||||
const run_step = b.step("run", "Run l65");
|
||||
run_step.dependOn(&run_cmd.step);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Release binaries
|
||||
|
||||
const release_step = b.step("release", "Generate a release");
|
||||
for (targets) |t| {
|
||||
const rel_exe = b.addExecutable(.{
|
||||
.name = "l65",
|
||||
.target = b.resolveTargetQuery(t),
|
||||
.optimize = .ReleaseSmall,
|
||||
});
|
||||
const target_output = b.addInstallArtifact(rel_exe, .{
|
||||
.dest_dir = .{
|
||||
.override = .{
|
||||
.custom = try t.zigTriple(b.allocator),
|
||||
},
|
||||
},
|
||||
});
|
||||
rel_exe.step.dependOn(&embed.step);
|
||||
try setupExe(rel_exe, &embed_output);
|
||||
release_step.dependOn(&target_output.step);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Build samples as test
|
||||
// TODO
|
||||
}
|
68
embed.c
68
embed.c
|
@ -82,40 +82,40 @@ static int writer(lua_State* L, const void* p, size_t size, void* f)
|
|||
|
||||
// use custom version so that filename is not used for debug info
|
||||
LUALIB_API int luaL_loadfilex2 (lua_State *L, const char *filename,
|
||||
const char *mode, const char *chunkname) {
|
||||
luai_LoadF lf;
|
||||
int status, readstatus;
|
||||
int c;
|
||||
int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
|
||||
if (filename == NULL) {
|
||||
lua_pushliteral(L, "=stdin");
|
||||
lf.f = stdin;
|
||||
}
|
||||
else {
|
||||
lua_pushfstring(L, "@%s", filename);
|
||||
lf.f = fopen(filename, "r");
|
||||
if (lf.f == NULL) return luai_errfile(L, "open", fnameindex);
|
||||
}
|
||||
if (luai_skipcomment(&lf, &c)) /* read initial portion */
|
||||
lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */
|
||||
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
|
||||
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
|
||||
if (lf.f == NULL) return luai_errfile(L, "reopen", fnameindex);
|
||||
luai_skipcomment(&lf, &c); /* re-read initial portion */
|
||||
}
|
||||
if (c != EOF)
|
||||
lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */
|
||||
lua_pop(L, 1);
|
||||
lua_pushstring(L, chunkname);
|
||||
status = lua_load(L, luai_getF, &lf, lua_tostring(L, -1), mode);
|
||||
readstatus = ferror(lf.f);
|
||||
if (filename) fclose(lf.f); /* close file (even in case of errors) */
|
||||
if (readstatus) {
|
||||
lua_settop(L, fnameindex); /* ignore results from 'lua_load' */
|
||||
return luai_errfile(L, "read", fnameindex);
|
||||
}
|
||||
lua_remove(L, fnameindex);
|
||||
return status;
|
||||
const char *mode, const char *chunkname) {
|
||||
luai_LoadF lf;
|
||||
int status, readstatus;
|
||||
int c;
|
||||
int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
|
||||
if (filename == NULL) {
|
||||
lua_pushliteral(L, "=stdin");
|
||||
lf.f = stdin;
|
||||
}
|
||||
else {
|
||||
lua_pushfstring(L, "@%s", filename);
|
||||
lf.f = fopen(filename, "r");
|
||||
if (lf.f == NULL) return luai_errfile(L, "open", fnameindex);
|
||||
}
|
||||
if (luai_skipcomment(&lf, &c)) /* read initial portion */
|
||||
lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */
|
||||
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
|
||||
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
|
||||
if (lf.f == NULL) return luai_errfile(L, "reopen", fnameindex);
|
||||
luai_skipcomment(&lf, &c); /* re-read initial portion */
|
||||
}
|
||||
if (c != EOF)
|
||||
lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */
|
||||
lua_pop(L, 1);
|
||||
lua_pushstring(L, chunkname);
|
||||
status = lua_load(L, luai_getF, &lf, lua_tostring(L, -1), mode);
|
||||
readstatus = ferror(lf.f);
|
||||
if (filename) fclose(lf.f); /* close file (even in case of errors) */
|
||||
if (readstatus) {
|
||||
lua_settop(L, fnameindex); /* ignore results from 'lua_load' */
|
||||
return luai_errfile(L, "read", fnameindex);
|
||||
}
|
||||
lua_remove(L, fnameindex);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int pmain(lua_State* L)
|
||||
|
|
3
l65.lua
3
l65.lua
|
@ -32,7 +32,7 @@ local Keywords = lookupify{
|
|||
'return', 'then', 'true', 'until', 'while',
|
||||
};
|
||||
|
||||
-- 6502
|
||||
-------------------------------------------------------- 6502::begin
|
||||
local Keywords_control = {
|
||||
-- control keywords
|
||||
'samepage', 'crosspage',
|
||||
|
@ -168,6 +168,7 @@ local addressing_map = {
|
|||
iny = opcode_indirect_y,
|
||||
rel = opcode_relative,
|
||||
}
|
||||
-------------------------------------------------------- 6502::end
|
||||
|
||||
local Scope = {
|
||||
new = function(self, parent)
|
||||
|
|
16
l65cfg.lua
16
l65cfg.lua
|
@ -1,8 +1,8 @@
|
|||
local M = {}
|
||||
|
||||
M.major = 0
|
||||
M.minor = 0
|
||||
M.revision = 0
|
||||
M.version = "0.0.0"
|
||||
|
||||
return M
|
||||
local M = {}
|
||||
|
||||
M.major = 0
|
||||
M.minor = 0
|
||||
M.revision = 0
|
||||
M.version = "0.0.0"
|
||||
|
||||
return M
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
local M = {}
|
||||
|
||||
M.major = ${L65_VERSION_MAJOR}
|
||||
M.minor = ${L65_VERSION_MINOR}
|
||||
M.revision = ${L65_VERSION_REVISION}
|
||||
M.version = "${L65_VERSION}"
|
||||
|
||||
return M
|
||||
local M = {}
|
||||
|
||||
M.major = ${L65_VERSION_MAJOR}
|
||||
M.minor = ${L65_VERSION_MINOR}
|
||||
M.revision = ${L65_VERSION_REVISION}
|
||||
M.version = "${L65_VERSION}"
|
||||
|
||||
return M
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_ONLY_PNG
|
||||
#define STBI_NO_FAILURE_STRINGS
|
||||
#include "stb_image.h"
|
||||
|
||||
#define LUA_IMPLEMENTATION
|
||||
#include "lua.h"
|
||||
#include "scripts_7801.h"
|
||||
|
||||
extern int luaopen_lpeg(lua_State *L);
|
||||
extern int luaopen_lfs(lua_State *L);
|
||||
|
||||
// l7801 lib
|
||||
static int r_s32be(uint8_t **b) { uint8_t *p = *b; int v = ((int)(p[0]))<<24 | ((int)(p[1]))<<16 | ((int)p[2])<<8 | p[3]; *b += 4; return v; }
|
||||
typedef struct { int len, nam; } chunk_s;
|
||||
static chunk_s r_chunk(uint8_t **b) { int len = r_s32be(b), nam = r_s32be(b); chunk_s c = { len, nam }; return c; }
|
||||
static int open_image(lua_State *L)
|
||||
{
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
FILE *file = fopen(filename, "rb");
|
||||
if (!file)
|
||||
{
|
||||
lua_pushnil(L);
|
||||
lua_pushfstring(L, "failed to open file %s", filename);
|
||||
return 2;
|
||||
}
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t sz = ftell(file);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
uint8_t *png = malloc(sz);
|
||||
fread(png, sz, 1, file);
|
||||
fclose(file);
|
||||
static uint8_t png_sig[8] = { 137,80,78,71,13,10,26,10 };
|
||||
if (memcmp(png, png_sig, 8) != 0)
|
||||
{
|
||||
free(png);
|
||||
lua_pushnil(L);
|
||||
lua_pushfstring(L, "file %s is not a PNG", filename);
|
||||
return 2;
|
||||
}
|
||||
uint8_t *b = png + 8;
|
||||
int w, h;
|
||||
uint8_t *d = 0; long d_sz = 0;
|
||||
#define CHUNK_NAM(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
|
||||
for (;;)
|
||||
{
|
||||
chunk_s chunk = r_chunk(&b);
|
||||
switch (chunk.nam)
|
||||
{
|
||||
case CHUNK_NAM('I','H','D','R'): {
|
||||
w = r_s32be(&b); h = r_s32be(&b);
|
||||
if (b[0] != 8 || b[1] != 3)
|
||||
{
|
||||
free(png);
|
||||
lua_pushnil(L);
|
||||
lua_pushfstring(L, "PNG file %s must be 8b indexed", filename);
|
||||
return 2;
|
||||
}
|
||||
b += 9;
|
||||
} break;
|
||||
case CHUNK_NAM('I','D','A','T'): {
|
||||
d = realloc(d, d_sz + chunk.len);
|
||||
memcpy(d + d_sz, b, chunk.len);
|
||||
d_sz += chunk.len;
|
||||
b += chunk.len+4;
|
||||
} break;
|
||||
case CHUNK_NAM('I','E','N','D'): {
|
||||
free(png);
|
||||
if (!d)
|
||||
{
|
||||
lua_pushnil(L);
|
||||
lua_pushfstring(L, "invalid PNG file %s", filename);
|
||||
return 2;
|
||||
}
|
||||
int px_sz;
|
||||
uint8_t *px_raw = (uint8_t*)stbi_zlib_decode_malloc_guesssize_headerflag((void*)d, d_sz, (w+1) * h, &px_sz, 1);
|
||||
free(d);
|
||||
uint8_t *px = calloc(w,h);
|
||||
uint8_t *px0 = px, *px_raw0 = px_raw;
|
||||
for (int y = 0; y < h; ++y)
|
||||
{
|
||||
int filter = *px_raw++;
|
||||
#define prev (x==0 ? 0 : px[x-1])
|
||||
#define up (px[x-w])
|
||||
#define prevup (x==0 ? 0 : px[x-w-1])
|
||||
switch (filter)
|
||||
{
|
||||
case 0: memcpy(px, px_raw, w); break;
|
||||
case 1: for (int x = 0; x < w; ++x) { px[x] = px_raw[x] + prev; } break;
|
||||
case 2: for (int x = 0; x < w; ++x) { px[x] = px_raw[x] + up; } break;
|
||||
case 3: for (int x = 0; x < w; ++x) { px[x] = px_raw[x] + ((prev+up)>>1); } break;
|
||||
case 4: for (int x = 0; x < w; ++x) { px[x] = px_raw[x] + stbi__paeth(prev,up,prevup); } break;
|
||||
}
|
||||
#undef prev
|
||||
#undef up
|
||||
#undef prevup
|
||||
px += w;
|
||||
px_raw += w;
|
||||
}
|
||||
STBI_FREE(px_raw0);
|
||||
|
||||
lua_createtable(L, w*h, 3);
|
||||
lua_pushstring(L, filename);
|
||||
lua_setfield(L, -2, "filename");
|
||||
lua_pushinteger(L, w);
|
||||
lua_setfield(L, -2, "width");
|
||||
lua_pushinteger(L, h);
|
||||
lua_setfield(L, -2, "height");
|
||||
for (int i = 0; i < w*h; ++i)
|
||||
{
|
||||
lua_pushinteger(L, px0[i]);
|
||||
lua_rawseti(L, -2, i+1);
|
||||
}
|
||||
free(px0);
|
||||
return 1;
|
||||
}
|
||||
default:
|
||||
b += chunk.len+4;
|
||||
}
|
||||
}
|
||||
#undef CHUNK_NAM
|
||||
if (d) free(d);
|
||||
free(png);
|
||||
lua_pushnil(L);
|
||||
lua_pushfstring(L, "invalid PNG file %s", filename);
|
||||
return 2;
|
||||
}
|
||||
static const struct luaL_Reg l7801lib[] = {
|
||||
{"image", open_image},
|
||||
{NULL, NULL},
|
||||
};
|
||||
static int luaopen_l7801(lua_State *L)
|
||||
{
|
||||
luaL_newlib(L, l7801lib);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define SRC_LUA(name) { #name, 0, script_ ## name ## _lua, sizeof(script_ ## name ## _lua) }
|
||||
#define SRC_L7801(name) { #name, 1, script_ ## name ## _l7801, sizeof(script_ ## name ## _l7801) }
|
||||
static struct script { const char *name; int t; const char *data; size_t sz; } embedded[] = {
|
||||
SRC_LUA(dkjson),
|
||||
SRC_LUA(l65cfg),
|
||||
SRC_LUA(re),
|
||||
SRC_L7801(scv),
|
||||
};
|
||||
#undef SRC_LUA
|
||||
#undef SRC_L7801
|
||||
|
||||
static int getembedded(lua_State *L)
|
||||
{
|
||||
const char *name = lua_tostring(L, 1);
|
||||
for (struct script *s = embedded, *e = s + sizeof(embedded) / sizeof(embedded[0]); s != e; ++s)
|
||||
{
|
||||
if (!strcmp(s->name, name))
|
||||
{
|
||||
lua_pushlstring(L, s->data, s->sz);
|
||||
lua_pushboolean(L, s->t);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msghandler(lua_State *L)
|
||||
{
|
||||
const char *msg = lua_tostring(L, 1);
|
||||
if (msg == NULL)
|
||||
{
|
||||
if (luaL_callmeta(L, 1, "__tostring") && lua_type(L, -1) == LUA_TSTRING)
|
||||
return 1;
|
||||
msg = lua_pushfstring(L, "(error object is a %s value)", luaL_typename(L, 1));
|
||||
}
|
||||
luaL_traceback(L, L, msg, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
lua_State *L = luaL_newstate();
|
||||
luaL_openlibs(L);
|
||||
luaL_requiref(L, "lpeg", luaopen_lpeg, 1); lua_pop(L, 1);
|
||||
luaL_requiref(L, "lfs", luaopen_lfs, 1); lua_pop(L, 1);
|
||||
luaL_requiref(L, "l7801", luaopen_l7801, 1); lua_pop(L, 1);
|
||||
|
||||
// preload embedded lua scripts
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
|
||||
luaL_loadbufferx(L, script_asm_lua, sizeof(script_asm_lua), "asm.lua", "b");
|
||||
lua_setfield(L, -2, "asm");
|
||||
luaL_loadbufferx(L, script_uPD7801_lua, sizeof(script_uPD7801_lua), "uPD7801.lua", "b");
|
||||
lua_setfield(L, -2, "uPD7801");
|
||||
lua_pop(L, 1);
|
||||
|
||||
// error handler
|
||||
lua_pushcfunction(L, msghandler);
|
||||
// l65.lua script
|
||||
luaL_loadbufferx(L, script_l7801_lua, sizeof(script_l7801_lua), "l7801.lua", "b");
|
||||
// arg[] table
|
||||
lua_createtable(L, argc-1, 2);
|
||||
lua_pushcfunction(L, getembedded); // pass embedded script lookup function as arg[-1]
|
||||
lua_rawseti(L, -2, -1);
|
||||
for (int i = 0; i < argc; i++) lua_pushstring(L, argv[i]), lua_rawseti(L, -2, i);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setglobal(L, "arg");
|
||||
// ... arguments
|
||||
{ int i; for (i = 1; i < argc; ++i) lua_rawgeti(L, -i, i); lua_remove(L, -i); }
|
||||
// call l7801
|
||||
int status = lua_pcall(L, argc-1, 0, -argc-1);
|
||||
if (status != LUA_OK)
|
||||
{
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1); // remove msghandler
|
||||
lua_close(L);
|
||||
return status;
|
||||
}
|
||||
|
2
lua.h
2
lua.h
|
@ -20280,7 +20280,7 @@ static void luai_fchecksize (luai_LoadState *S, size_t size, const char *tname)
|
|||
#define luai_checksize(S,t) luai_fchecksize(S,sizeof(t),#t)
|
||||
|
||||
static void luai_checkHeader (luai_LoadState *S) {
|
||||
luai_checkliteral(S, LUA_SIGNATURE + 1, "not a"); /* 1st char already checked */
|
||||
luai_checkliteral(S, (const char*)LUA_SIGNATURE + 1, "not a"); /* 1st char already checked */
|
||||
if (luai_LoadByte(S) != LUAC_VERSION)
|
||||
luai_error(S, "version mismatch in");
|
||||
if (luai_LoadByte(S) != LUAC_FORMAT)
|
||||
|
|
5
main.c
5
main.c
|
@ -145,6 +145,7 @@ static struct script { const char *name; int t; const char *data; size_t sz; }
|
|||
SRC_LUA(dkjson),
|
||||
SRC_LUA(l65cfg),
|
||||
SRC_LUA(re),
|
||||
SRC_L65(nes),
|
||||
SRC_L65(vcs),
|
||||
};
|
||||
#undef SRC_LUA
|
||||
|
@ -163,7 +164,7 @@ static int getembedded(lua_State *L)
|
|||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int msghandler(lua_State *L)
|
||||
{
|
||||
|
@ -188,6 +189,8 @@ int main(int argc, char *argv[])
|
|||
|
||||
// preload embedded lua scripts
|
||||
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
|
||||
luaL_loadbufferx(L, script_asm_lua, sizeof(script_asm_lua), "asm.lua", "b");
|
||||
lua_setfield(L, -2, "asm");
|
||||
luaL_loadbufferx(L, script_6502_lua, sizeof(script_6502_lua), "6502.lua", "b");
|
||||
lua_setfield(L, -2, "6502");
|
||||
lua_pop(L, 1);
|
||||
|
|
|
@ -0,0 +1,902 @@
|
|||
-- set cpu to 6502
|
||||
cpu = require "6502"
|
||||
setmetatable(_ENV, cpu)
|
||||
|
||||
nes = {
|
||||
OAM = 0x200, -- 0x100 bytes
|
||||
RAM = 0x300, -- 0x500 bytes + ZP 0x100 bytes + Stack 0x100 bytes + OAM 0x100 bytes = 0x800 bytes
|
||||
|
||||
-- 2C02 / 2C07 PPU
|
||||
PPUCTRL = 0x2000,
|
||||
PPUMASK = 0x2001,
|
||||
PPUSTAT = 0x2002,
|
||||
OAMADDR = 0x2003,
|
||||
OAMDATA = 0x2004,
|
||||
BGSCROL = 0x2005,
|
||||
PPUADDR = 0x2006,
|
||||
PPUDATA = 0x2007,
|
||||
|
||||
-- 2A03 / 2A07 CPU+APU
|
||||
SQ1VOL = 0x4000,
|
||||
SQ1SWEEP = 0x4001,
|
||||
SQ1LO = 0x4002,
|
||||
SQ1HI = 0x4003,
|
||||
SQ2VOL = 0x4004,
|
||||
SQ2SWEEP = 0x4005,
|
||||
SQ2LO = 0x4006,
|
||||
SQ2HI = 0x4007,
|
||||
TRILINEAR = 0x4008,
|
||||
TRILO = 0x400A,
|
||||
TRIHI = 0x400B,
|
||||
NOISEVOL = 0x400C,
|
||||
NOISELO = 0x400E,
|
||||
NOISEHI = 0x400F,
|
||||
DMCFREQ = 0x4010,
|
||||
DMCRAW = 0x4011,
|
||||
DMCSTART = 0x4012,
|
||||
DMCLEN = 0x4013,
|
||||
OAMDMA = 0x4014,
|
||||
SNDCNT = 0x4015,
|
||||
SPECIO1 = 0x4016,
|
||||
SPECIO2 = 0x4017,
|
||||
|
||||
SRAM = 0x6000, -- 0x2000 bytes
|
||||
ROM = 0x8000, -- 0x8000 bytes
|
||||
|
||||
-- PPU Memory declarations
|
||||
CHAR0 = 0x0000, -- 0x1000 bytes
|
||||
CHAR1 = 0x1000, -- 0x1000 bytes
|
||||
SCREEN0 = 0x2000, -- 0x400 bytes
|
||||
SCREEN1 = 0x2400, -- 0x400 bytes
|
||||
SCREEN2 = 0x2800, -- 0x400 bytes
|
||||
SCREEN3 = 0x2C00, -- 0x400 bytes
|
||||
BGPAL = 0x3F00, -- 0x10 bytes
|
||||
OBJPAL = 0x3F10, -- 0x10 bytes
|
||||
}
|
||||
do
|
||||
local symbols = cpu.symbols
|
||||
for k,v in pairs(nes) do symbols[k] = v end
|
||||
end
|
||||
|
||||
NTSC = {
|
||||
CLOCK = 1789773
|
||||
}
|
||||
PAL = {
|
||||
CLOCK = 1662607
|
||||
}
|
||||
|
||||
-- add some symbol file formats for NES debuggers
|
||||
cpu.getsym_as.mesen = function() -- .mlb
|
||||
local ins,fmt = table.insert,string.format
|
||||
local s = getsym(function(a,l,lorg)
|
||||
if a >= 0x10000 then return end
|
||||
local prefix = {}
|
||||
if a < 0x2000 then prefix[1]='R'
|
||||
elseif a >= 0x6000 and a < 0x8000 then prefix[1]='S' prefix[2]='W' a=a-0x6000
|
||||
elseif a >= 0x8000 then prefix[1]='P' a=(symbolsorg[lorg] or a)-0x8000
|
||||
else prefix[1]='G' end
|
||||
local s = {}
|
||||
for _,p in ipairs(prefix) do ins(s, fmt("%s:%04x:%s", p, a, l)) end
|
||||
return s
|
||||
end)
|
||||
return table.concat(s, '\n')
|
||||
end
|
||||
cpu.getsym_as.fceux = function(filename) -- .nl, multiple files
|
||||
local ins,fmt = table.insert,string.format
|
||||
local ram,rom = {},{}
|
||||
local s = getsym(function(a,l,lorg)
|
||||
local s = fmt("$%04x#%s#", a, l)
|
||||
if a < 0x8000 then ins(ram, s)
|
||||
elseif a < 0x10000 then
|
||||
local a_org = symbolsorg[lorg] or a
|
||||
local romstart = locations[2].start -- header location should always be defined first, skip it
|
||||
if a_org >= romstart then
|
||||
local bank = math.floor((a_org - romstart) / 0x4000)
|
||||
if not rom[bank] then rom[bank] = {} end
|
||||
ins(rom[bank], s)
|
||||
end
|
||||
end
|
||||
end)
|
||||
local fn = filename
|
||||
if not fn:find('%.') then fn = fn .. '.nes' end
|
||||
local fni = fn .. '.ram.nl'
|
||||
local f = assert(io.open(fni, "wb"), "failed to open " .. fni .. " for writing")
|
||||
f:write(table.concat(ram, '\n')) f:close()
|
||||
for k,v in pairs(rom) do
|
||||
fni = fn .. '.' .. k .. '.nl'
|
||||
f = assert(io.open(fni, "wb"), "failed to open " .. fni .. " for writing")
|
||||
f:write(table.concat(v, '\n')) f:close()
|
||||
end
|
||||
end
|
||||
|
||||
mappers = {}
|
||||
|
||||
vblank_waitbegin = function()
|
||||
local l=label() bit PPUSTAT bpl l
|
||||
end
|
||||
vblank_waitend = function()
|
||||
local l=label() bit PPUSTAT bmi l
|
||||
end
|
||||
|
||||
ppu_addr = function(addr)
|
||||
lda #addr>>8 sta PPUADDR
|
||||
if addr&0xff ~= addr>>8 then lda #addr&0xff end
|
||||
sta PPUADDR
|
||||
end
|
||||
|
||||
oam_bytes = function(t)
|
||||
return {
|
||||
t.y - 1,
|
||||
t.tile,
|
||||
(t.palette or 0)&3 | ((t.behind or t.priority==1) and 0x20 or 0) | (t.flipx and 0x40 or 0) | (t.flipy and 0x80 or 0),
|
||||
t.x
|
||||
}
|
||||
end
|
||||
oam_set = function(t)
|
||||
local b = oam_bytes(t)
|
||||
lda #t[1]*4 sta OAMADDR
|
||||
for _,v in ipairs(b) do lda #v sta OAMDATA end
|
||||
end
|
||||
|
||||
oamcache = OAM -- change it to set other location
|
||||
oamcache_clear = function()
|
||||
local oam = oamcache
|
||||
ldx #0 lda #0xff
|
||||
local l=label() sta oam,x inx inx inx inx bne l
|
||||
end
|
||||
oamcache_flush = function()
|
||||
local oam = oamcache
|
||||
lda #0 sta OAMADDR lda #oam>>8 sta OAMDMA
|
||||
end
|
||||
oamcache_set = function(t)
|
||||
local oam = oamcache
|
||||
local b = oam_bytes(t)
|
||||
ldx #t[1]*4 lda #b[1] sta oam,x
|
||||
inx lda #b[2] sta oam,x
|
||||
inx lda #b[3] sta oam,x
|
||||
inx lda #b[4] sta oam,x
|
||||
end
|
||||
|
||||
--[[ button state:
|
||||
bit: 7 6 5 4 3 2 1 0
|
||||
button: A B Select Start Up Down Left Right
|
||||
https://wiki.nesdev.com/w/index.php/Controller_Reading
|
||||
]]
|
||||
-- fast reading of just the A button
|
||||
-- return C if A is pressed, c otherwise
|
||||
read_joy_a = function(joy_index)
|
||||
local joy = joy_index == 2 and SPECIO2 or SPECIO1
|
||||
lda #1 sta joy lsr sta joy lda joy lsr
|
||||
end
|
||||
-- read one joypad state into dst
|
||||
read_joy = function(dst, joy_index)
|
||||
local joy = joy_index == 2 and SPECIO2 or SPECIO1
|
||||
lda #1 sta joy sta dst lsr sta joy
|
||||
@_readbutton lda joy lsr rol dst bcc _readbutton
|
||||
end
|
||||
-- read both joypad states and Famicom's DA15 expansion port
|
||||
read_joys = function(dst1, dst2)
|
||||
lda #1 sta SPECIO1 sta dst2 lsr sta SPECIO1
|
||||
@_readbuttons
|
||||
lda SPECIO1 and #3 cmp #1 rol dst1
|
||||
lda SPECIO2 and #3 cmp #1 rol dst2
|
||||
bcc _readbuttons
|
||||
end
|
||||
-- read both joypad states on even cycles only, to safely work with DPCM
|
||||
-- must be called right after oamcache_flush or any other sta OAMDMA
|
||||
read_joys_even = function(dst1, dst2)
|
||||
ldx #1 stx dst1 stx SPECIO1 dex stx SPECIO1
|
||||
@_readbuttons
|
||||
lda SPECIO2 and #3 cmp #1 rol dst2,x
|
||||
lda SPECIO1 and #3 cmp #1 rol dst1
|
||||
bcc _readbuttons
|
||||
end
|
||||
|
||||
init = function()
|
||||
sei -- cld not needed, no BCD support
|
||||
ldx #0x40 stx SPECIO2 -- disable APU frame IRQ
|
||||
ldx #0xff txs inx stx PPUCTRL stx PPUMASK stx DMCFREQ -- disable NMI, rendering, DMC IRQs
|
||||
bit PPUSTAT -- clear remnant VBlank PPU status flag on reset
|
||||
vblank_waitbegin()
|
||||
lda #0 sta SNDCNT -- stop APU channels
|
||||
-- clear CPU RAM
|
||||
@_zeroram
|
||||
sta 0x0000,x sta 0x0100,x sta 0x0200,x sta 0x0300,x
|
||||
sta 0x0400,x sta 0x0500,x sta 0x0600,x sta 0x0700,x
|
||||
inx bne _zeroram
|
||||
vblank_waitbegin()
|
||||
-- clear OAM
|
||||
oamcache_clear() oamcache_flush()
|
||||
-- clear PPU RAM
|
||||
bit PPUSTAT ppu_addr(0x2000) tax ldy #0x10
|
||||
@_zeroppu
|
||||
sta PPUDATA dex bne _zeroppu dey bne _zeroppu
|
||||
bit PPUSTAT -- reset latch
|
||||
if mappers.init then mappers.init() end
|
||||
end
|
||||
|
||||
-- NES 2.0 (backward compatible with iNES)
|
||||
-- https://wiki.nesdev.com/w/index.php/NES_2.0
|
||||
header = function(t)
|
||||
if not t then t = {} end
|
||||
local logsz = function(sz)
|
||||
assert(sz >= 0 and sz <= 1048576, "invalid size: " .. sz .. ", expected [0, 1048576]")
|
||||
if sz < 1 then return 0 end
|
||||
if sz <= 128 then return 1 end
|
||||
return math.ceil(math.log(sz/64, 2))
|
||||
end
|
||||
-- mapper
|
||||
local mi1 = t.mapperid or 0
|
||||
assert(mi1 >= 0 and mi1 < 4096, "invalid mapper id: " .. mi1 .. ", expected [0, 4095]")
|
||||
local ms1 = t.submapperid or 0
|
||||
assert(ms1 >= 0 and ms1 < 16, "invalid submapper id: " .. ms1 .. ", expected [0, 15]")
|
||||
local mapper6 = (mi1 & 0xf) << 4
|
||||
local mapper7 = mi1 & 0xf0
|
||||
local mapper8 = (mi1 >> 8) | (ms1 << 4)
|
||||
-- prgsize
|
||||
local prgsize = math.tointeger((t.prgsize or 16384) / 16384)
|
||||
assert(prgsize, "prgsize must be a multiple of 16384")
|
||||
-- chrsize
|
||||
local chrsize = math.tointeger((t.chrsize or 0) / 8192)
|
||||
assert(chrsize, "chrsize must be a multiple of 8192")
|
||||
-- wramsize (not battery-backed)
|
||||
local wramsize = logsz(t.wramsize or 0)
|
||||
-- bramsize (battery-backed)
|
||||
local bramsize = logsz(t.bramsize or 0)
|
||||
-- chrbramsize (battery-backed)
|
||||
local chrbramsize = logsz(t.chrbramsize or 0)
|
||||
-- chrramsize (not battery-backed)
|
||||
local chrramsize = logsz(t.chrramsize or (chrbramsize==0 and chrsize==0 and 8192 or 0))
|
||||
local battery_bit = bramsize == 0 and chrbramsize == 0 and 0 or 2
|
||||
-- mirror: 'H' for horizontal mirroring, 'V' for vertical mirroring
|
||||
-- '4' for four-screen VRAM, 218 for four-screen and vertical
|
||||
local mirror = (t.mirror or 'h'):lower()
|
||||
mirror = ({ h=0, v=1, ['4']=8, [218]=9 })[mirror]
|
||||
assert(mirror, "invalid mirror mode: " .. mirror .. ", expected 'H', 'V', '4', or 218")
|
||||
-- tv: 'N' for NTSC, 'P' for PAL, 'NP' for both preferring NTSC, 'PN' for both preferring PAL
|
||||
local tv, tvm = 0, (t.tv or 'n'):lower()
|
||||
assert(tvm=='n' or tvm=='p' or tvm=='np' or tvm=='pn', "invalid tv mode: " .. tostring(t.tv) .. ", expected 'N', 'P', 'NP' or 'PN'")
|
||||
if tvm:sub(1,1) == 'p' then tv = 1 end
|
||||
if #tvm > 1 then tv = tv + 2 end
|
||||
|
||||
@@header -- size: 16 bytes
|
||||
dc.b 0x4e, 0x45, 0x53 -- 'NES'
|
||||
dc.b 0x1a
|
||||
dc.b prgsize, chrsize
|
||||
dc.b mapper6 | mirror | battery_bit
|
||||
dc.b mapper7 | 8
|
||||
dc.b mapper8
|
||||
dc.b ((chrsize >> 4) & 0xF0) | ((prgsize >> 8) & 0x0F)
|
||||
dc.b (bramsize << 4) | wramsize
|
||||
dc.b (chrbramsize << 4) | chrramsize
|
||||
dc.b tv, 0, 0, 0
|
||||
|
||||
-- update table with defaulted values
|
||||
t.prgsize = prgsize * 16384
|
||||
t.chrsize = chrsize * 8192
|
||||
t.wramsize = math.tointeger(2^wramsize*64)
|
||||
t.bramsize = math.tointeger(2^bramsize*64)
|
||||
t.chrbramsize = math.tointeger(2^chrbramsize*64)
|
||||
t.chrramsize = math.tointeger(2^chrramsize*64)
|
||||
mappers.header=t
|
||||
end
|
||||
|
||||
local n0ne = function(x) return not x or x == 0 end
|
||||
local val0 = function(x) return x and x or 0 end
|
||||
|
||||
-- 2a03 APU emulator
|
||||
apuemu = {
|
||||
--a=0,x=0,y=0,s=0,p=0,pc=0,xjam=false,
|
||||
--reset = function(self)
|
||||
-- self.a,self.x,self.y,self.s,self.p,self.pc,self.xjam = 0,0,0,0xff,
|
||||
--end
|
||||
}
|
||||
|
||||
-- plays a NSF tune and record its register writes for replay
|
||||
-- stops at C00
|
||||
-- FamiTracker must be in the path
|
||||
-- Ported from Shiru's nsf2vgm
|
||||
nsf = function(filename, song)
|
||||
local f, nsf = assert(io.open(filename,'rb')) nsf=f:read('*all') f:close()
|
||||
assert(nsf[0x7a] == 0, 'expansion chips are not supported')
|
||||
nsf.songs = nsf[7]
|
||||
nsf.loadadr=nsf[9]+(nsf[10]<<8) nsf.initadr=nsf[11]+(nsf[12]<<8) nsf.playadr=nsf[13]+(nsf[14]<<8)
|
||||
local bs = false for i=1,8 do local p=nsf[0x70+i] nsf.page[i]=p if p~=0 then bs=true end end
|
||||
if not bs then for i=1,8 do nsf.page[i]=i-1 end end
|
||||
local ram,rom,page = {},{},{}
|
||||
for i=1,#nsf-0x80 do rom[nsf.loadadr-0x7FFF] = nsf[0x81] end
|
||||
local reg = { 0x13f, 0x108, 0x100, 0x100, 0x13f, 0x108, 0x100, 0x100, 0x180, 0x100, 0x100, 0x100, 0x13f, 0x100, 0x100, 0x100,
|
||||
0x100, 0x100, 0x100, 0x100, 0x100, 0x11f, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100 }
|
||||
|
||||
end
|
||||
|
||||
--[[ For all mappers, where relevant:
|
||||
|
||||
prg rom banks:
|
||||
prgroms are numbered from last (0) to first (#-1), so that adding more does not change
|
||||
prgrom0, which must contain the reset vector (main). The switchprgrom* functions expect
|
||||
a bank index according to this reversed numbering (0 is last physical bank in ROM).
|
||||
|
||||
t.prgmap is an optional function taking a prgrom bank index and returning its rorg value.
|
||||
Default is to map banks in sequence according to their size, eg.:
|
||||
* for sizes 32kB: always 0x8000.
|
||||
* for sizes 16kB+16kB: repeat sequence 0x8000, 0xa000.
|
||||
* for sizes 16kB+8kB+8kB: repeat sequence 0x8000, 0xc000, 0xe000.
|
||||
* for sizes 8kB+8kB+8kB+8kB: repeat sequence 0x8000, 0xa000, 0xc000, 0xe000.
|
||||
If last bank is fixed, it is not part of the sequence and its mapping value is skipped:
|
||||
* for sizes 8kB+8kB+ last fixed 16kB: repeat sequence 0x8000, 0xa000, set last bank to 0xc000.
|
||||
|
||||
chr rom banks:
|
||||
chrroms are numbered in physical ROM address order, unlike prgroms.
|
||||
|
||||
Banks are created with the smallest switchable size, maximum being 4kB by default.
|
||||
t.chrmap is an optional function taking a chrrom bank index and returning its offset in
|
||||
ROM from the beginning of the first chrrom, its size (defaults to everything remaining),
|
||||
and its relocation origin (defaults to 0).
|
||||
So if a mapper allows switching chrroms in any of 1-1-1-1-1-1-1-1kB / 2-2-2-2kB, etc.,
|
||||
the default is to create only 1kB chrroms. Conversely, with a mapper switching only in 8kB
|
||||
slices, 4kB chrrom are created, not 8kB (but you can specify any size using t.chrmap).
|
||||
]]
|
||||
|
||||
--[[
|
||||
https://wiki.nesdev.com/w/index.php/NROM
|
||||
]]
|
||||
mappers.NROM = function(t)
|
||||
if not t then t = {} end
|
||||
if not t.prgsize then t.prgsize = 16384 end
|
||||
assert(t.prgsize == 16384 or t.prgsize == 32768, "prgsize must be 16 or 32kB")
|
||||
if n0ne(t.chrsize) and n0ne(t.chrramsize) and n0ne(t.chrbramsize) then t.chrsize = 8192 end
|
||||
assert(val0(t.chrsize) + val0(t.chrramsize) + val0(t.chrbramsize) == 8192, "combined chrrom size must be 8kB")
|
||||
assert(not t.mirror or ({ h=1, v=1 })[t.mirror:lower()], "only H and V mirroring are supported")
|
||||
local prgstart = 0x10000 - t.prgsize
|
||||
hdrrom = location{prgstart - 16, prgstart - 1, name='header'}
|
||||
header(t)
|
||||
prgrom0 = location{prgstart, 0xffff, name='prgrom'}
|
||||
prgrom = prgrom0
|
||||
section{"vectors", org=0xfffa} dc.w nmi, main, irq
|
||||
if (t.chrsize > 0) then
|
||||
local ci = 0
|
||||
local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end
|
||||
repeat
|
||||
local off, sz, rorg = chrmap(ci)
|
||||
sz = sz or t.chrsize - off
|
||||
local o = off + 0x10000
|
||||
_ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci}
|
||||
ci = ci+1
|
||||
until off + sz >= t.chrsize
|
||||
chrrom = chrrom0
|
||||
end
|
||||
end
|
||||
mappers[0] = mappers.NROM
|
||||
|
||||
--[[
|
||||
https://wiki.nesdev.com/w/index.php/UxROM
|
||||
Has bus conflicts.
|
||||
]]
|
||||
mappers.UxROM = function(t)
|
||||
if not t then t = {} end
|
||||
t.mapperid = 2
|
||||
if not t.prgsize then t.prgsize = 32768 end
|
||||
assert(t.prgsize >= 0x8000 and t.prgsize <= 0x40000, "prgsize must be at least 32kB and at most 256kB")
|
||||
if not t.chrsize then t.chrsize = 8192 end
|
||||
assert(t.chrsize == 0x2000, "chrsize must be 8kB")
|
||||
assert(not t.mirror or ({ h=1, v=1 })[t.mirror:lower()], "only H and V mirroring are supported")
|
||||
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
|
||||
@@bankbytes -- for handling bus conflicts
|
||||
samepage for i=bc-1,0,-1 do byte(i) end end
|
||||
local ci, chrstart = 0, 0x8000 + bc*0x4000
|
||||
local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end
|
||||
repeat
|
||||
local off, sz, rorg = chrmap(ci)
|
||||
sz = sz or t.chrsize - off
|
||||
local o = off + chrstart
|
||||
_ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci}
|
||||
ci = ci+1
|
||||
until off + sz >= t.chrsize
|
||||
chrrom = chrrom0
|
||||
function switchprgrom(bankix)
|
||||
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
|
||||
mappers.init = function()
|
||||
switchprgrom(1)
|
||||
end
|
||||
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.
|
||||
]]
|
||||
mappers.CNROM = function(t)
|
||||
if not t then t = {} end
|
||||
t.mapperid = 3
|
||||
if not t.prgsize then t.prgsize = 16384 end
|
||||
assert(t.prgsize == 16384 or t.prgsize == 32768, "prgsize must be 16 or 32kB")
|
||||
if not t.chrsize then t.chrsize = 8192 end
|
||||
assert(t.chrsize >= 0x2000 and t.chrsize <= 0x8000, "chrsize must be at least 8kB and at most 32kB")
|
||||
assert(not t.mirror or ({ h=1, v=1 })[t.mirror:lower()], "only H and V mirroring are supported")
|
||||
local prgstart = 0x10000 - t.prgsize
|
||||
hdrrom = location{prgstart - 16, prgstart - 1, name='header'}
|
||||
header(t)
|
||||
prgrom = location{prgstart, 0xffff, name='prgrom'}
|
||||
section{"vectors", org=0xfffa} dc.w nmi, main, irq
|
||||
@@bankbytes samepage -- for handling bus conflicts
|
||||
dc.b 0x00, 0x01, 0x02, 0x03
|
||||
dc.b 0x10, 0x11, 0x12, 0x13
|
||||
dc.b 0x20, 0x21, 0x22, 0x23
|
||||
dc.b 0x30, 0x31, 0x32, 0x33
|
||||
end
|
||||
local ci, cc = 0, t.chrsize//0x2000
|
||||
local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end
|
||||
repeat
|
||||
local off, sz, rorg = chrmap(ci)
|
||||
sz = sz or t.chrsize - off
|
||||
local o = off + 0x10000
|
||||
_ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci}
|
||||
ci = ci+1
|
||||
until off + sz >= t.chrsize
|
||||
chrrom = chrrom0
|
||||
securitydiodes = 0 -- set to actual value, eg 0x20
|
||||
function switchchrrom(bankix)
|
||||
assert(securitydiodes < 0x40)
|
||||
if bankix then
|
||||
assert(bankix < cc)
|
||||
ldx #bankix|securitydiodes>>2 lda #bankix|securitydiodes
|
||||
else
|
||||
ora #securitydiodes>>2 tax and #3 ora #securitydiodes
|
||||
end
|
||||
sta bankbytes,x
|
||||
end
|
||||
mappers.init = function()
|
||||
switchchrrom(0)
|
||||
end
|
||||
end
|
||||
mappers[3] = mappers.CNROM
|
||||
|
||||
--[[
|
||||
https://wiki.nesdev.com/w/index.php/GxROM
|
||||
Has bus conflicts.
|
||||
]]
|
||||
mappers.GxROM = function(t)
|
||||
if not t then t = {} end
|
||||
t.mapperid = 66
|
||||
if not t.prgsize then t.prgsize = 32768 end
|
||||
assert(t.prgsize >= 0x8000 and t.prgsize <= 0x20000, "prgsize must be at least 32kB and at most 128kB")
|
||||
if not t.chrsize then t.chrsize = 8192 end
|
||||
assert(t.chrsize >= 0x2000 and t.chrsize <= 0x8000, "chrsize must be at least 8kB and at most 32kB")
|
||||
assert(not t.mirror or ({ h=1, v=1 })[t.mirror:lower()], "only H and V mirroring are supported")
|
||||
hdrrom = location{0x7ff0, 0x7fff, name='header'}
|
||||
header(t)
|
||||
local bc = t.prgsize//0x8000
|
||||
local cc = t.chrsize//0x2000
|
||||
local ci, chrstart = 0, 0x8000 + bc*0x8000
|
||||
local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end
|
||||
repeat
|
||||
local off, sz, rorg = chrmap(ci)
|
||||
sz = sz or t.chrsize - off
|
||||
local o = off + chrstart
|
||||
_ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci}
|
||||
ci = ci+1
|
||||
until off + sz >= t.chrsize
|
||||
chrrom = chrrom0
|
||||
-- RAM address of bank register copy, to switch using A instead of immediate
|
||||
-- if bankregister_shadow is negative, only immediate bankswitching is available
|
||||
bankregister_shadow = -1
|
||||
-- otherwise, just set the value directly: xxPPxxCC in A, xxxxPPCC in X with sta bankbytes0,x
|
||||
function switchroms(prgbankix, chrbankix)
|
||||
if prgbankix then
|
||||
assert(prgbankix < bc) prgbankix = bc-1-prgbankix
|
||||
assert(chrbankix, "GxROM must specify both PRG and CHR roms to switch simultaneously")
|
||||
assert(chrbankix < cc)
|
||||
local br = prgbankix<<4 | chrbankix
|
||||
ldx #br&3|br>>2 lda #br sta bankbytes0,x
|
||||
else -- A contains 00PP00CC
|
||||
if bankregister_shadow >= 0 then
|
||||
sta bankregister_shadow -- bankregister_shadow = 00PP00CC
|
||||
lsr lsr -- A = 0000PP00, c
|
||||
-- compute bc-1-prgbankix (reverse indexing)
|
||||
eor #0xff adc #(bc-1<<2)+1 -- negate and +bc-1 combined, NN = bc-1-PP: A = 0000NN00
|
||||
ora bankregister_shadow ;and #0xf tax -- X = 0000NNCC
|
||||
else -- use stack instead
|
||||
tsx sta 0x100,x lsr lsr -- A = 0000PP00, s[0] = 00PP00CC, c
|
||||
-- compute bc-1-prgbankix (reverse indexing)
|
||||
eor #0xff adc #(bc-1<<2)+1 -- negate and +bc-1 combined, NN = bc-1-PP: A = 0000NN00
|
||||
ora 0x100,x ;and #0xf tax -- X = 0000NNCC
|
||||
end
|
||||
lda bankbytes0,x sta bankbytes0,x -- A = 00NN00CC, from table
|
||||
end
|
||||
end
|
||||
for bi=0,bc-1 do
|
||||
local o,ix = 0x8000 + bi*0x8000, bc-1-bi
|
||||
_ENV['prgrom'..ix] = location{o, o+0x7fff, rorg=0x8000, name='prgrom'..ix}
|
||||
local start=section{"entry"..ix, org=o+0x8000-6-16-10} switchroms(0,0) if ix==0 then jmp main end
|
||||
section{"vectors"..ix, org=o+0x8000-6} dc.w "nmi"..ix, start, "irq"..ix
|
||||
section{"bankbytes"..ix, org=o+0x8000-6-16} samepage -- for handling bus conflicts
|
||||
dc.b 0x00, 0x01, 0x02, 0x03
|
||||
dc.b 0x10, 0x11, 0x12, 0x13
|
||||
dc.b 0x20, 0x21, 0x22, 0x23
|
||||
dc.b 0x30, 0x31, 0x32, 0x33
|
||||
end
|
||||
end
|
||||
prgrom = prgrom0
|
||||
function switchprgrom(bankix)
|
||||
if bankix then
|
||||
assert(bankix < bc)
|
||||
lda #bc-1-bankix
|
||||
else
|
||||
clc eor #0xff adc #bc -- bc-1 - A
|
||||
end
|
||||
assert(bankregister_shadow >= 0, "no RAM slot assigned to bankregister_shadow")
|
||||
tay lda bankregister_shadow ;and #3 sta bankregister_shadow
|
||||
tya asl asl ora bankregister_shadow tax
|
||||
lda bankbytes0,x sta bankregister_shadow sta bankbytes0,x
|
||||
end
|
||||
function switchchrrom(bankix)
|
||||
if bankix then
|
||||
assert(bankix < cc)
|
||||
lda #bankix
|
||||
end
|
||||
assert(bankregister_shadow >= 0, "no RAM slot assigned to bankregister_shadow")
|
||||
tay lda bankregister_shadow lsr lsr sta bankregister_shadow
|
||||
tya ora bankregister_shadow tax
|
||||
lda bankbytes0,x sta bankregister_shadow sta bankbytes0,x
|
||||
end
|
||||
end
|
||||
mappers[66] = mappers.GxROM
|
||||
|
||||
--[[
|
||||
https://wiki.nesdev.com/w/index.php/MMC1
|
||||
|
||||
t.prgmap is an optional function taking a prgrom bank index and returning its rorg value.
|
||||
Default is to create 16kB banks if prgswitchmode is not all, 32kB otherwise, and rorg them
|
||||
accordingly.
|
||||
Same goes for chrrom sizes.
|
||||
|
||||
t.prgswitchmode:
|
||||
'first' makes 0x8000-0xbfff switchable (default)
|
||||
'last' makes 0xc000-0xffff switchable
|
||||
'all' makes 0x8000-0xffff switchable
|
||||
t.chrswitchmode:
|
||||
'all' switches whole 8kB at a time
|
||||
'half' switches 2 separate 4kB banks (default)
|
||||
]]
|
||||
mappers.MMC1 = function(t)
|
||||
if not t then t = {} end
|
||||
t.mapperid = 1
|
||||
if not t.prgswitchmode then t.prgswitchmode = 'first' end
|
||||
if not t.chrswitchmode then t.chrswitchmode = 'half' end
|
||||
if not t.wramsize then t.wramsize = 0 end
|
||||
if not t.bramsize and t.wramsize == 0 then t.bramsize = 8192 end
|
||||
local prgram = t.bramsize + t.wramsize
|
||||
assert(prgram >= 0x2000 and prgram <= 0x8000, "bramsize or wramsize must be at least 8kB and at most 32kB")
|
||||
if not t.prgsize then t.prgsize = 32768 end
|
||||
assert(t.prgsize >= 0x8000 and t.prgsize <= 0x80000, "prgsize must be at least 32kB and at most 512kB")
|
||||
if not t.chrsize then t.chrsize = 8192 end
|
||||
assert(t.chrsize >= 0x2000 and t.chrsize <= 0x20000, "chrsize must be at least 8kB and at most 128kB")
|
||||
hdrrom = location{0x7FF0, 0x7FFF, name='header'}
|
||||
header(t)
|
||||
local bsz = t.prgswitchmode=='all' and 0x8000 or 0x4000
|
||||
local prgmap = t.prgmap or function(bi, bc) return t.prgswitchmode=='last' and 0xc000 or 0x8000 end
|
||||
local bc = t.prgsize//bsz
|
||||
for bi=0,bc-1 do
|
||||
local o,ix = 0x8000 + bi*bsz, bc-1-bi
|
||||
_ENV['prgrom'..ix] = location{o, o+bsz-1, rorg=prgmap(ix,bc), name='prgrom'..ix}
|
||||
end
|
||||
section{"vectors", org=0x8000+bc*bsz-6} dc.w nmi, main, irq
|
||||
prgrom = prgrom0
|
||||
local ci, chrstart = 0, 0x8000 + bc*bsz
|
||||
local csz = t.chrswitchmode=='all' and 0x2000 or 0x1000
|
||||
local cc = t.chrsize//csz
|
||||
local chrmap = t.chrmap or function(ci) return ci*0x1000, 0x1000, (ci&1)*0x1000 end
|
||||
repeat
|
||||
local off, sz, rorg = chrmap(ci)
|
||||
sz = sz or t.chrsize - off
|
||||
local o = off + chrstart
|
||||
_ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci}
|
||||
ci = ci+1
|
||||
until off + sz >= t.chrsize
|
||||
chrrom = chrrom0
|
||||
|
||||
local prgswitchmodemap = { all=0, first=3<<2, last=2<<2 }
|
||||
local chrswitchmodemap = { all=0, half=1<<4 }
|
||||
mmc1ctrl = (mappers.header.mirror==1 and 2 or 3) | prgswitchmodemap[t.prgswitchmode] | chrswitchmodemap[t.chrswitchmode]
|
||||
function mmc1write(reg)
|
||||
sta reg lsr sta reg lsr sta reg lsr sta reg lsr sta reg
|
||||
end
|
||||
-- Can be turned into a function, A must contain the bank index.
|
||||
-- eg:
|
||||
-- @@switchprgrom_func switchprgrom_f()
|
||||
-- switchprgrom_f = function() jsr switchprgrom_func rts end
|
||||
function switchprgrom_f() mmc1write(0xe000) end
|
||||
function switchprgrom(bankix)
|
||||
assert(bankix < t.prgsize//0x4000)
|
||||
lda #bankix
|
||||
switchprgrom_f()
|
||||
end
|
||||
function switchchrrom_f(slot) assert(slot<2) mmc1write(slot==0 and 0xa000 or 0xc000) end
|
||||
function switchchrrom(bankix, slot)
|
||||
assert(bankix < t.chrsize//0x1000)
|
||||
lda #bankix
|
||||
switchchrrom_f(slot or 0)
|
||||
end
|
||||
function setmirror(mirror)
|
||||
mirror = assert(({ h=3, v=2, hi=1, lo=0 })[mirror:lower()])
|
||||
mmc1ctrl = (mmc1ctrl & ~3) | mirror
|
||||
lda #mmc1ctrl mmc1write(0x8000)
|
||||
end
|
||||
function setprgswitchmode(mode)
|
||||
mode = assert(prgswitchmodemap[mode])
|
||||
mmc1ctrl = (mmc1ctrl & ~0xc) | mode
|
||||
lda #mmc1ctrl mmc1write(0x8000)
|
||||
end
|
||||
function setchrswitchmode(mode)
|
||||
mode = assert(chrswitchmodemap[mode])
|
||||
mmc1ctrl = (mmc1ctrl & ~0x10) | mode
|
||||
lda #mmc1ctrl mmc1write(0x8000)
|
||||
end
|
||||
mappers.init = function()
|
||||
lda #0x80 sta 0x8000
|
||||
lda #mmc1ctrl mmc1write(0x8000)
|
||||
if t.prgswitchmode ~= 'all' then switchprgrom(1) end
|
||||
switchchrrom(0) if t.chrswitchmode ~= 'all' then switchchrrom(1,1) end
|
||||
end
|
||||
end
|
||||
mappers[1] = mappers.MMC1
|
||||
|
||||
--[[
|
||||
https://wiki.nesdev.com/w/index.php/MMC3
|
||||
|
||||
prgroms are numbered from last (0) to first (#-1), so that adding more does not change
|
||||
prgrom0, which must contain the reset vector (main).
|
||||
Last 2 prg banks are merged into 1 16kB bank, to allow linker optimization - hence, 0 must
|
||||
always be set on bit 6 of bank select, even; also, there is no prgrom1 as a consequence.
|
||||
|
||||
chrroms are all 1kB, so they can work with chr A12 inversion enabled or not.
|
||||
|
||||
With default submapper id of 0, this defaults to revision MM3C, which generates a scanline
|
||||
interrupt at each scanline when counter is loaded with 0.
|
||||
]]
|
||||
mappers.MMC3 = function(t)
|
||||
if not t then t = {} end
|
||||
t.mapperid = 4
|
||||
if not t.bramsize then t.bramsize = 8192 end
|
||||
assert(t.bramsize == 8192, "bramsize must be 8kB")
|
||||
if not t.prgsize then t.prgsize = 32768 end
|
||||
assert(t.prgsize >= 0x8000 and t.prgsize <= 0x80000, "prgsize must be at least 32kB and at most 512kB")
|
||||
if not t.chrsize then t.chrsize = 8192 end
|
||||
assert(t.chrsize >= 0x2000 and t.chrsize <= 0x40000, "chrsize must be at least 8kB and at most 256kB")
|
||||
hdrrom = location{0x7FF0, 0x7FFF, name='header'}
|
||||
header(t)
|
||||
local prgmap = t.prgmap or function(bi, bc) return 0x8000+(bi&1)*0x2000 end
|
||||
local bc = t.prgsize//0x2000
|
||||
for bi=0,bc-3 do
|
||||
local o,ix = 0x8000 + bi*0x2000, bc-bi-1
|
||||
_ENV['prgrom'..ix] = location{o, o+0x1fff, rorg=prgmap(ix,bc), name='prgrom'..ix}
|
||||
end
|
||||
do
|
||||
local o = 0x8000 + (bc-2)*0x2000
|
||||
prgrom0 = location{o, o+0x3fff, rorg=0xc000, name='prgrom0'}
|
||||
section{"vectors", org=o+0x3ffa} dc.w nmi, main, irq
|
||||
end
|
||||
prgrom = prgrom0
|
||||
local ci, chrstart = 0, 0x8000 + bc*0x2000
|
||||
local chrmap = t.chrmap or function(ci) return ci*0x400, 0x400, (ci&7)*0x400 end
|
||||
local cc = t.chrsize//0x400
|
||||
repeat
|
||||
local off, sz, rorg = chrmap(ci)
|
||||
sz = sz or t.chrsize - off
|
||||
local o = off + chrstart
|
||||
_ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci}
|
||||
ci = ci+1
|
||||
until off + sz >= t.chrsize
|
||||
chrrom = chrrom0
|
||||
local a12inv = false
|
||||
function seta12inv(enabled) a12inv = enabled end
|
||||
function switchprgrom(bankix, slot)
|
||||
assert(slot<2)
|
||||
slot = slot + 6
|
||||
if a12inv then slot = slot | 0x80 end
|
||||
lda #slot sta 0x8000
|
||||
assert(bankix < bc)
|
||||
bankix = bc-1-bankix -- reverse index order, since we set 0 as last
|
||||
lda #bankix sta 0x8001
|
||||
end
|
||||
-- slot [0, 7], each slot is 1kB counting in order, regardless of a12inv state
|
||||
function switchchrrom(bankix, slot)
|
||||
assert(slot<8)
|
||||
if a12inv then
|
||||
assert(slot ~= 5 and slot ~= 7)
|
||||
if slot == 6 then slot = 1
|
||||
elseif slot == 4 then slot = 0
|
||||
else slot = slot + 2 end
|
||||
slot = slot | 0x80
|
||||
else
|
||||
assert(slot ~= 1 and slot ~= 3)
|
||||
if slot == 2 then slot = 1
|
||||
elseif slot > 3 then slot = slot - 2 end
|
||||
end
|
||||
lda #slot sta 0x8000
|
||||
assert(bankix < cc)
|
||||
lda #bankix sta 0x8001
|
||||
end
|
||||
function setmirror(mirror)
|
||||
mirror = assert(({ h=1, v=0 })[mirror:lower()])
|
||||
lda #mirror sta 0xa000
|
||||
end
|
||||
function protectsram() lda 0x40 sta 0xa001 end
|
||||
function scanlineirq(count) ldx #1 stx 0xe000 lda #count-1 sta 0xc000 sta 0xc001 stx 0xe001 end
|
||||
mappers.init = function()
|
||||
switchprgrom(2, 0) switchprgrom(3, 1)
|
||||
switchchrrom(0, 0) switchchrrom(2, 2)
|
||||
switchchrrom(4, 4) switchchrrom(5, 5) switchchrrom(6, 6) switchchrrom(7, 7)
|
||||
local mirror = mappers.header.mirror
|
||||
if mirror==0 or mirror==1 then lda #mirror~1 sta 0xa000 end
|
||||
lda #0x80 sta 0xa001
|
||||
end
|
||||
end
|
||||
mappers[4] = mappers.MMC3
|
||||
|
||||
--[[
|
||||
https://wiki.nesdev.com/w/index.php/MMC5
|
||||
https://forums.nesdev.com/viewtopic.php?f=9&t=16789
|
||||
|
||||
main MUST be in 0xe000-0xfff9 of last prgrom.
|
||||
prgroms are numbered from last (0) to first (#-1), so that adding more does not change
|
||||
prgrom0, which must contain the reset vector (main).
|
||||
|
||||
chrroms are all 1kB.
|
||||
]]
|
||||
mappers.MMC5 = function(t)
|
||||
if not t then t = {} end
|
||||
t.mapperid = 5
|
||||
assert(val0(t.bramsize) + val0(t.wramsize) <= 0x10000, "bramsize + wramsize must be at most 64kB")
|
||||
if not t.prgsize then t.prgsize = 8192 end
|
||||
assert(t.prgsize <= 0x100000, "prgsize must be at most 1MB")
|
||||
if not t.chrsize then t.chrsize = 8192 end
|
||||
assert(t.chrsize <= 0x100000, "chrsize must be at most 1MB")
|
||||
hdrrom = location{0x7FF0, 0x7FFF, name='header'}
|
||||
header(t)
|
||||
local prgmap = t.prgmap or function(bi, bc) return 0x8000+(bi&3)*0x2000 end
|
||||
local bc = t.prgsize//0x2000
|
||||
for bi=0,bc-2 do
|
||||
local o,ix = 0x8000 + bi*0x2000, bc-bi-1
|
||||
_ENV['prgrom'..ix] = location{o, o+0x1fff, rorg=prgmap(ix,bc), name='prgrom'..ix}
|
||||
end
|
||||
do
|
||||
local o = 0x8000 + (bc-1)*0x2000
|
||||
prgrom0 = location{o, o+0x1fff, rorg=0xe000, name='prgrom0'}
|
||||
section{"vectors", org=o+0x1ffa} dc.w nmi, main, irq
|
||||
section{"main", org=o} -- enforce entry point in last bank
|
||||
end
|
||||
prgrom = prgrom0
|
||||
local ci, chrstart = 0, 0x8000 + bc*0x2000
|
||||
local chrmap = t.chrmap or function(ci) return ci*0x400, 0x400, (ci&7)*0x400 end
|
||||
local cc = t.chrsize//0x400
|
||||
repeat
|
||||
local off, sz, rorg = chrmap(ci)
|
||||
sz = sz or t.chrsize - off
|
||||
local o = off + chrstart
|
||||
_ENV['chrrom'..ci] = location{o, o+sz-1, rorg=rorg or 0, name='chrrom'..ci}
|
||||
ci = ci+1
|
||||
until off + sz >= t.chrsize
|
||||
chrrom = chrrom0
|
||||
function prgbankmode(mode) if mode then lda #mode end sta 0x5100 end
|
||||
function chrbankmode(mode) if mode then lda #mode end sta 0x5101 end
|
||||
function wrammode(mode) if mode then lda #mode end sta 0x5104 end
|
||||
function protectsram() lda #0 sta 0x5102 sta 0x5103 end
|
||||
function screenmap(map) if map then lda #map end sta 0x5105 end
|
||||
function filltile(tile) if tile then lda #tile end sta 0x5106 end
|
||||
function fillcolor(col) if col then lda #col end sta 0x5107 end
|
||||
function switchprgram(chip, bank)
|
||||
assert(chip == 0 or chip == 1)
|
||||
assert(bank < 4)
|
||||
lda #chip<<2|bank sta 0x5113
|
||||
end
|
||||
function switchprgrom0(bank, isram) lda #bank|(isram and 0 or 0x80) sta 0x5114 end
|
||||
function switchprgrom1(bank, isram) lda #bank|(isram and 0 or 0x80) sta 0x5115 end
|
||||
function switchprgrom2(bank, isram) lda #bank|(isram and 0 or 0x80) sta 0x5116 end
|
||||
function switchprgrom3(bank, isram) lda #bank|(isram and 0 or 0x80) sta 0x5117 end
|
||||
function switchchrrom(bank, slot) -- 1kB mode
|
||||
assert(slot < 12)
|
||||
assert(bank < cc)
|
||||
lda #bank>>7 sta 0x5130
|
||||
lda #bank sta slot+0x5120
|
||||
end
|
||||
vsplitmode = 0
|
||||
function vsplitstart(starttile)
|
||||
assert(starttile < 32)
|
||||
vsplitmode = (vsplitmode & ~31) | starttile
|
||||
lda #vsplitmode sta 0x5200
|
||||
end
|
||||
function vsplitside(side)
|
||||
assert(side == 'left' or side == 'right')
|
||||
vsplitmode = (vsplitmode & ~0x40) | (side == 'right' and 1 or 0)
|
||||
lda #vsplitmode sta 0x5200
|
||||
end
|
||||
function vsplitenable(enabled)
|
||||
vsplitmode = (vsplitmode & ~0x80) | (enabled and 1 or 0)
|
||||
lda #vsplitmode sta 0x5200
|
||||
end
|
||||
function vsplitscroll(vscroll) if vscroll then lda #vscroll end sta 0x5201 end
|
||||
function vsplitbank(chrbank) if chrbank then lda #chrbank end sta 0x5202 end
|
||||
function scanlineirq(atscanline) lda #0 sta 0x5204 lda #atscanline sta 0x5203 lda #0x80 sta 0x5204 end
|
||||
function irqenable(enabled) lda #enabled and 0x80 or 0 sta 0x5204 end
|
||||
-- 0x40 set: PPU is rendering visible scanlines
|
||||
-- 0x80 set: a scanlineirq is pending (internal counter reach atscanline value set with scanlineirq())
|
||||
function irqstatus() lda 0x5204 end
|
||||
-- multiplies a*x = x:a (x hi, a lo)
|
||||
function mul() sta 0x5205 stx 0x5206 lda 0x5205 ldx 0x5206 end
|
||||
mappers.init = function()
|
||||
ldx #2 stx 0x5102 dex stx 0x5103
|
||||
lda #vsplitmode sta 0x5200
|
||||
prgbankmode(2)
|
||||
switchprgrom1(3) -- map prg rom 3, 2, x, 0
|
||||
switchprgrom2(1) -- map prg rom 3, 2, 1, 0
|
||||
chrbankmode(0) switchchrrom(0, 7) chrbankmode(3) -- map chr rom 0 to the 8kB, and set mode to 1kB slices
|
||||
end
|
||||
end
|
||||
mappers[5] = mappers.MMC5
|
|
@ -0,0 +1,5 @@
|
|||
-- set cpu to HuC6280
|
||||
cpu = require "6502"
|
||||
setmetatable(_ENV, cpu)
|
||||
cpu.zeropage = function(x) if x >= 0x2000 and x <= 0x20ff then return x&0xff end end
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
require'nes'
|
||||
|
||||
-- Press A to switch between the two PRG banks.
|
||||
|
||||
-- fixed 16kB for prgrom0, 3 other banks of 16kB each mappable at 0x8000
|
||||
mappers.UxROM{ prgsize=(16+16*3)*1024 }
|
||||
|
||||
location(chrrom0)
|
||||
|
||||
local font = section("font")
|
||||
dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -- <SPC>
|
||||
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
|
||||
charset(" abcdefghijklmnopqrstuvwxyz", \x(x + (font.org - font.location.start) // 16))
|
||||
|
||||
-- define some text of same length in both swappable PRG banks 1 and 2
|
||||
location(prgrom1)
|
||||
local text1 = "bank one"
|
||||
local sec_text1 = section("text1") byte(text1)
|
||||
|
||||
location(prgrom2)
|
||||
local text2 = "bank two"
|
||||
local sec_text2 = section("text2") byte(text2)
|
||||
-- force sec_text2 to have same offset in prgrom2 than sec_text1 in prgrom1,
|
||||
-- so we can use either address to reference the custom bank data
|
||||
relate(sec_text1, sec_text2)
|
||||
|
||||
-- fixed bank, last 16kB
|
||||
location(prgrom)
|
||||
@@nmi @irq rti
|
||||
|
||||
joya = 1
|
||||
curbank = 0
|
||||
@@switchbank
|
||||
lda #3
|
||||
eor curbank
|
||||
sta curbank
|
||||
switchprgrom()
|
||||
-- load screen text in PPU RAM 0x21CA
|
||||
ppu_addr(0x21ca)
|
||||
ldy #0 @_loadtxt lda sec_text1,y sta PPUDATA iny cpy ##text1 bne _loadtxt
|
||||
-- reset scroll position
|
||||
ppu_addr(0) sta BGSCROL sta BGSCROL
|
||||
rts
|
||||
|
||||
@@main
|
||||
init()
|
||||
vblank_waitbegin()
|
||||
-- load BG palette in PPU RAM
|
||||
ppu_addr(BGPAL)
|
||||
for _,v in ipairs{ 0x1f, 0x00, 0x10, 0x20 } do lda #v sta PPUDATA end
|
||||
-- switch between the two banks
|
||||
lda #2 sta curbank
|
||||
jsr switchbank
|
||||
-- show BG
|
||||
lda #0x0a sta PPUMASK
|
||||
-- idle
|
||||
@_loop
|
||||
vblank_waitbegin()
|
||||
read_joy_a(1) bcc _noswitch
|
||||
lda joya bne _switch
|
||||
jsr switchbank lda #1 sta joya bne _switch
|
||||
@_noswitch
|
||||
lda #0 sta joya
|
||||
@_switch
|
||||
jmp _loop
|
||||
|
||||
writebin(filename..'.nes')
|
||||
writesym(filename..'.mlb', 'mesen')
|
||||
writesym(filename..'.nes', 'fceux')
|
||||
print(stats)
|
|
@ -0,0 +1,118 @@
|
|||
require'nes'
|
||||
|
||||
-- 2 32kB PRG roms
|
||||
mappers.GxROM{ prgsize=(32*2)*1024 }
|
||||
|
||||
location(chrrom0)
|
||||
|
||||
local font = section("font")
|
||||
dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -- <SPC>
|
||||
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
|
||||
charset(" abcdefghijklmnopqrstuvwxyz", \x(x+(font.org - font.location.start) // 16))
|
||||
|
||||
local copytext = function(sec_text, text)
|
||||
-- load screen text in PPU RAM 0x21CA
|
||||
ppu_addr(0x21ca)
|
||||
ldy #0 @_loadtxt lda sec_text,y sta PPUDATA iny cpy ##text bne _loadtxt
|
||||
-- reset scroll position
|
||||
ppu_addr(0) sta BGSCROL sta BGSCROL
|
||||
end
|
||||
|
||||
-- RAM
|
||||
joya = 0
|
||||
|
||||
--[[
|
||||
BANK 0 - startup bank
|
||||
]]
|
||||
location(prgrom0)
|
||||
|
||||
local text0 = "bank one"
|
||||
@@text0s byte(text0)
|
||||
|
||||
@@nmi0 @irq0 rti
|
||||
|
||||
local toprg1s = section("toprg1")
|
||||
switchroms(1,0)
|
||||
local fromprg1s = section("fromprg1")
|
||||
jmp mainloop
|
||||
|
||||
@@main
|
||||
init()
|
||||
--lda#0x80 sta PPUSTAT -- enable VBlank IRQ on NMI vector
|
||||
vblank_waitbegin()
|
||||
-- load BG palette in PPU RAM
|
||||
ppu_addr(BGPAL)
|
||||
for _,v in ipairs{ 0x1f, 0x00, 0x10, 0x20 } do lda #v sta PPUDATA end
|
||||
-- turn screen on
|
||||
lda #0x0a sta PPUMASK -- show BG
|
||||
-- idle
|
||||
@mainloop
|
||||
vblank_waitbegin()
|
||||
copytext(text0s, text0)
|
||||
read_joy_a(1) bcc _noswitch
|
||||
lda joya bne _switch
|
||||
lda #1 sta joya jmp toprg1
|
||||
@_noswitch
|
||||
lda #0 sta joya
|
||||
@_switch
|
||||
jmp mainloop
|
||||
|
||||
|
||||
--[[
|
||||
BANK 1
|
||||
]]
|
||||
location(prgrom1)
|
||||
|
||||
local text1 = "bank two"
|
||||
@@text1s byte(text1)
|
||||
|
||||
@@nmi1 @irq1 rti
|
||||
|
||||
local toprg0s = section("toprg0")
|
||||
relate(toprg0s, fromprg1s, 7) -- switchprgrom(immediate) takes 7 bytes
|
||||
switchroms(0,0)
|
||||
|
||||
local fromprg0s = section("fromprg0")
|
||||
relate(toprg1s, fromprg0s, 7)
|
||||
jmp mainloop1
|
||||
|
||||
@mainloop1
|
||||
vblank_waitbegin()
|
||||
copytext(text1s, text1)
|
||||
read_joy_a(1) bcc _noswitch
|
||||
lda joya bne _switch
|
||||
lda #1 sta joya jmp toprg0
|
||||
@_noswitch
|
||||
lda #0 sta joya
|
||||
@_switch
|
||||
jmp mainloop1
|
||||
|
||||
writebin(filename..'.nes')
|
||||
writesym(filename..'.mlb', 'mesen')
|
||||
writesym(filename..'.nes', 'fceux')
|
||||
print(stats)
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
80!1& )
|
|
@ -0,0 +1,51 @@
|
|||
require'nes'
|
||||
|
||||
-- asm code : 2019 - mara/flush
|
||||
-- gfx : mara/flush
|
||||
|
||||
mappers.CNROM{chrmap = function(ci) return ci*0x2000, 0x2000, (ci&1)*0x2000 end}
|
||||
|
||||
|
||||
location(chrrom0)
|
||||
|
||||
-- set beer tileset for sprite
|
||||
@@beer_tileset do local f, s = assert(io.open('nes_beer.chr','rb')) s=f:read('*all') f:close() byte{s:byte(1,-1)} end
|
||||
|
||||
location(prgrom)
|
||||
@@nmi rti
|
||||
@@irq rti
|
||||
|
||||
-- the beer sprite's palette
|
||||
local beer_pal = { 0x0F,0x10,0x38,0x30,0x0F,0x01,0x21,0x31,0x0F,0x06,0x16,0x26,0x0F,0x09,0x19,0x29 }
|
||||
|
||||
-- describe oam bank for the sprite
|
||||
-- number, x position, x position, tile number, index in palette
|
||||
local sprite_beer = { 0, x=0x80, y=0x80, tile=1, palette=0 }
|
||||
|
||||
@@main
|
||||
init()
|
||||
vblank_waitbegin()
|
||||
-- activate sprite
|
||||
lda #0b00011000
|
||||
sta PPUMASK
|
||||
-- load SPRITE palette in PPU RAM
|
||||
ppu_addr(OBJPAL)
|
||||
-- set sprite palette
|
||||
for _,v in ipairs(beer_pal) do lda #v sta PPUDATA end
|
||||
ppu_addr(0x2000)
|
||||
|
||||
-- set the sprite on sprite bank 0
|
||||
oam_set(sprite_beer)
|
||||
-- reset scroll position
|
||||
ppu_addr(0) sta BGSCROL sta BGSCROL
|
||||
|
||||
-- idle
|
||||
@_loop jmp _loop
|
||||
|
||||
|
||||
|
||||
|
||||
writebin(filename..'.nes')
|
||||
writesym(filename..'.mlb', 'mesen')
|
||||
writesym(filename..'.nes', 'fceux')
|
||||
print(stats)
|
|
@ -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 -- <SPC>
|
||||
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)
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
0%!10
|
|
@ -0,0 +1,93 @@
|
|||
require'nes'
|
||||
|
||||
-- samples : extacted of the demo : Let's Go To The Very Important Party 2018
|
||||
-- original code : 2018 - mara/flush
|
||||
-- asm code : 2019 - mara/flush
|
||||
-- gfx : Exocet/Up Rough
|
||||
|
||||
mappers.CNROM{chrmap = function(ci) return ci*0x2000, 0x2000, (ci&1)*0x2000 end}
|
||||
|
||||
|
||||
location(chrrom0)
|
||||
|
||||
@@ghosts_tiles do local f, s = assert(io.open('nes_ghosts.chr','rb')) s=f:read('*all') f:close() byte{s:byte(1,-1)} end
|
||||
|
||||
location(prgrom)
|
||||
@@nmi rti
|
||||
@@irq rti
|
||||
|
||||
-- ghosts' palette
|
||||
local ghosts_pal = { 0x0F, 0x30, 0x25, 0x15, 0x0F, 0x21, 0x31, 0x30, 0x0F, 0x1B, 0x19, 0x1B, 0x0F, 0x0F, 0x0F, 0x0F }
|
||||
|
||||
-- metasprite is a set of sprites to draw a big sprite
|
||||
-- here you have two big sprites, the son and mother ghosts
|
||||
-- it's a set of oam bank
|
||||
-- index, x position, y position, tile number, palette number
|
||||
local ghosts_spr = {
|
||||
{ 2, x= 0+0x80, y= 0+0x88, tile=0x01, palette=1 }, -- begin sprite son
|
||||
{ 3, x= 8+0x80, y= 0+0x88, tile=0x02, palette=1 },
|
||||
{ 4, x=16+0x80, y= 0+0x88, tile=0x03, palette=1 },
|
||||
{ 5, x=24+0x80, y= 0+0x88, tile=0x04, palette=1 },
|
||||
{ 6, x= 0+0x80, y= 8+0x88, tile=0x07, palette=1 },
|
||||
{ 7, x= 8+0x80, y= 8+0x88, tile=0x08, palette=1 },
|
||||
{ 8, x=16+0x80, y= 8+0x88, tile=0x09, palette=1 },
|
||||
{ 9, x=24+0x80, y= 8+0x88, tile=0x0a, palette=1 },
|
||||
{ 10, x= 0+0x80, y=16+0x88, tile=0x0f, palette=1 },
|
||||
{ 11, x= 8+0x80, y=16+0x88, tile=0x10, palette=1 },
|
||||
{ 12, x=16+0x80, y=16+0x88, tile=0x11, palette=1 },
|
||||
{ 13, x=24+0x80, y=16+0x88, tile=0x12, palette=1 },
|
||||
{ 14, x= 0+0x80, y=24+0x88, tile=0x17, palette=1 },
|
||||
{ 15, x= 8+0x80, y=24+0x88, tile=0x18, palette=1 },
|
||||
{ 16, x=16+0x80, y=24+0x88, tile=0x19, palette=1 },
|
||||
{ 17, x=24+0x80, y=24+0x88, tile=0x1a, palette=1 },
|
||||
|
||||
---------------------------------------------------------------
|
||||
|
||||
{ 18, x= 8+0x40, y= 0+0x80, tile=0x05, palette=0 }, --begin sprite mother
|
||||
{ 19, x=16+0x40, y= 0+0x80, tile=0x06, palette=0 },
|
||||
{ 20, x= 0+0x40, y= 8+0x80, tile=0x0b, palette=0 },
|
||||
{ 21, x= 8+0x40, y= 8+0x80, tile=0x0c, palette=0 },
|
||||
{ 22, x=16+0x40, y= 8+0x80, tile=0x0d, palette=0 },
|
||||
{ 23, x=24+0x40, y= 8+0x80, tile=0x0e, palette=0 },
|
||||
{ 24, x= 0+0x40, y= 16+0x80, tile=0x13, palette=0 },
|
||||
{ 25, x= 8+0x40, y= 16+0x80, tile=0x14, palette=0 },
|
||||
{ 26, x=16+0x40, y= 16+0x80, tile=0x15, palette=0 },
|
||||
{ 27, x=24+0x40, y= 16+0x80, tile=0x16, palette=0 },
|
||||
{ 28, x= 0+0x40, y= 24+0x80, tile=0x1b, palette=0 },
|
||||
{ 29, x= 8+0x40, y= 24+0x80, tile=0x1c, palette=0 },
|
||||
{ 30, x=16+0x40, y= 24+0x80, tile=0x1d, palette=0 },
|
||||
{ 31, x=24+0x40, y= 24+0x80, tile=0x1e, palette=0 },
|
||||
{ 32, x= 0+0x40, y= 32+0x80, tile=0x1f, palette=0 },
|
||||
{ 33, x= 8+0x40, y= 32+0x80, tile=0x20, palette=0 },
|
||||
{ 34, x=16+0x40, y= 32+0x80, tile=0x21, palette=0 },
|
||||
{ 35, x=24+0x40, y= 32+0x80, tile=0x22, palette=0 }
|
||||
}
|
||||
|
||||
@@main
|
||||
init()
|
||||
vblank_waitbegin()
|
||||
lda #0b00011000
|
||||
sta PPUMASK
|
||||
lda #0
|
||||
sta PPUCTRL
|
||||
|
||||
-- load SPRITE palette in PPU RAM
|
||||
ppu_addr(OBJPAL)
|
||||
for _,v in ipairs(ghosts_pal) do lda #v sta PPUDATA end
|
||||
-- load the metasprites
|
||||
for _,v in ipairs(ghosts_spr) do oam_set(v) end
|
||||
|
||||
|
||||
-- reset scroll position
|
||||
ppu_addr(0) sta BGSCROL sta BGSCROL
|
||||
|
||||
-- idle
|
||||
@_loop jmp _loop
|
||||
|
||||
|
||||
|
||||
|
||||
writebin(filename..'.nes')
|
||||
writesym(filename..'.mlb', 'mesen')
|
||||
writesym(filename..'.nes', 'fceux')
|
||||
print(stats)
|
|
@ -0,0 +1,63 @@
|
|||
require'nes'
|
||||
|
||||
mappers.NROM()
|
||||
|
||||
location(chrrom0)
|
||||
|
||||
local font = section("font")
|
||||
dc.b 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -- <SPC>
|
||||
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
|
||||
|
||||
location(prgrom)
|
||||
@@nmi rti
|
||||
@@irq rti
|
||||
|
||||
charset(" abcdefghijklmnopqrstuvwxyz", \x(x + (font.org - font.location.start) // 16))
|
||||
local hello = "hello world"
|
||||
@@text byte(hello)
|
||||
|
||||
@@main
|
||||
init()
|
||||
vblank_waitbegin()
|
||||
-- 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
|
||||
lda #0x0a sta PPUMASK
|
||||
-- idle
|
||||
@_loop jmp _loop
|
||||
|
||||
writebin(filename..'.nes')
|
||||
writesym(filename..'.mlb', 'mesen')
|
||||
writesym(filename..'.nes', 'fceux')
|
||||
print(stats)
|
Binary file not shown.
|
@ -0,0 +1,80 @@
|
|||
require'nes'
|
||||
|
||||
-- samples : extacted of the demo : Let's Go To The Very Important Party 2018
|
||||
-- original code : 2018 - mara/flush
|
||||
-- asm code : 2019 - mara/flush
|
||||
-- gfx : Exocet/Up Rough
|
||||
|
||||
mappers.CNROM{chrmap = function(ci) return ci*0x2000, 0x2000, (ci&1)*0x2000 end}
|
||||
|
||||
|
||||
location(chrrom0)
|
||||
|
||||
-- create a variable house_tiles which contains tileset.chr's data (house's tiles)
|
||||
@@house_tiles do local f, s = assert(io.open('nes_house_tileset.chr','rb')) s=f:read('*all') f:close() byte{s:byte(1,-1)} end
|
||||
|
||||
-- don't define nmi and irq routines
|
||||
location(prgrom)
|
||||
@@nmi rti
|
||||
@@irq rti
|
||||
|
||||
-- create the house palette
|
||||
local home_pal = { 0x32,0x22,0x11,0x30,0x32,0x06,0x16,0x26,0x32,0x27,0x37,0x17,0x32,0x0f,0x00,0x10 }
|
||||
|
||||
-- create a variable nam which contains the house's nametable (map of house)
|
||||
do
|
||||
local f, s = assert(io.open('nes_house.nam','rb')) s=f:read('*all') f:close()
|
||||
@@nam byte{s:byte(1,-1)}
|
||||
end
|
||||
|
||||
|
||||
local nam_high = 1
|
||||
local nam_low = 0
|
||||
|
||||
@@main
|
||||
-- initialize the hardware registers
|
||||
init()
|
||||
-- wait a vertical blank
|
||||
vblank_waitbegin()
|
||||
-- load BG palette in PPU RAM
|
||||
ppu_addr(BGPAL)
|
||||
-- put the house's palette
|
||||
for _,v in ipairs(home_pal) do lda #v sta PPUDATA end
|
||||
ppu_addr(0x2000)
|
||||
|
||||
-- load and set nametable
|
||||
lda #nam>>8&0xff
|
||||
sta nam_high
|
||||
lda #nam & 0xff
|
||||
sta nam_low
|
||||
jsr loadBG
|
||||
|
||||
|
||||
-- reset scroll position
|
||||
ppu_addr(0) sta BGSCROL sta BGSCROL
|
||||
lda #0x0a sta PPUMASK
|
||||
-- idle
|
||||
@_loop jmp _loop
|
||||
|
||||
|
||||
-- routine to load a nametable with the attributes
|
||||
@loadBG
|
||||
ldy #0
|
||||
ldx #0
|
||||
|
||||
@loopLoadBG
|
||||
lda (nam_low), y -- can only be used with y
|
||||
sta 0x2007
|
||||
iny
|
||||
bne loopLoadBG
|
||||
inc nam_high
|
||||
inx
|
||||
cpx #04
|
||||
bne loopLoadBG
|
||||
rts
|
||||
|
||||
-- produce NES file, and debug symbols
|
||||
writebin(filename..'.nes')
|
||||
writesym(filename..'.mlb', 'mesen')
|
||||
writesym(filename..'.nes', 'fceux')
|
||||
print(stats)
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,40 @@
|
|||
require 'scv'
|
||||
location(0x8000, 0x8FFF)
|
||||
section{"rom", org=0x8000}
|
||||
dc.b 'H'
|
||||
@main
|
||||
di
|
||||
lxi sp,0xFFD2
|
||||
ei
|
||||
calt 0x8C
|
||||
|
||||
lxi hl,vdc_data
|
||||
lxi de,0x3400
|
||||
mvi c,0x03
|
||||
block
|
||||
|
||||
lxi hl,message
|
||||
lxi de,0x3044
|
||||
lxi bc,0x01ff
|
||||
|
||||
@loop_0
|
||||
block
|
||||
dcr b
|
||||
jr loop_0
|
||||
|
||||
-- beep
|
||||
lxi hl,0x3600
|
||||
calf 0xfb0
|
||||
|
||||
@loop_1
|
||||
nop
|
||||
jr loop_1
|
||||
|
||||
section{"vdc_data", org=0x8030}
|
||||
dc.b 0xC0,0x00,0x00,0xF2
|
||||
|
||||
section{"message", org=0x8040}
|
||||
dc.b "\t\t\t\t Hello world! \t\t\t\t"
|
||||
dc.b 0x00
|
||||
|
||||
writebin(filename .. '.bin')
|
|
@ -0,0 +1,498 @@
|
|||
----------------------------------------------------------------
|
||||
-- Plogue uPD1771C Tester
|
||||
-- BY David Viens january 2010 chipsounds R&D
|
||||
-- (from Hello World! Program Ver 1.1 by) Programmed By Enri
|
||||
-- haxored to emit 7801 instructions using
|
||||
-- Macro Assembler AS V1.42's 7810 mode
|
||||
-- http://john.ccac.rwth-aachen.de:8000/as
|
||||
-- Converted to l65
|
||||
----------------------------------------------------------------
|
||||
|
||||
require 'scv'
|
||||
location(0x8000, 0x9fff)
|
||||
section{"rom", org=0x8000}
|
||||
dc.b 'H' -- Headder
|
||||
|
||||
-- 0FF8X seems reserved, but not FF9X
|
||||
local PARAMS = 0xFF90
|
||||
local ACTIVE_PARAM = 0xFF9A
|
||||
local PLAYING = 0xFF9B
|
||||
|
||||
-------------------------
|
||||
-- Clear Text All Area --
|
||||
-------------------------
|
||||
calt 0x8c -- bios function!!!
|
||||
|
||||
-------------
|
||||
-- Set VDC --
|
||||
-------------
|
||||
lxi hl,VDC_DATA
|
||||
lxi de,0x3400
|
||||
mvi c,0x03
|
||||
block
|
||||
|
||||
---------------------
|
||||
-- Set INITIAL RAM --
|
||||
---------------------
|
||||
lxi hl,INITIAL_RAM
|
||||
lxi de,PARAMS
|
||||
mvi c,0x0F
|
||||
block
|
||||
|
||||
--------------------------------------------------------------
|
||||
----------------- END INITIALISATION POUTINE -----------------
|
||||
--------------------------------------------------------------
|
||||
|
||||
-------------------------
|
||||
-- Print Screen Format --
|
||||
-------------------------
|
||||
lxi hl,DISPLAY_DATA
|
||||
lxi de,0x3040 -- Text RAM Address
|
||||
lxi bc,0x0180
|
||||
@LOOP0
|
||||
block
|
||||
dcr b
|
||||
jr LOOP0
|
||||
|
||||
--------------------------------------------------------------
|
||||
------------------------- MAIN LOOP -------------------------
|
||||
--------------------------------------------------------------
|
||||
@LOOPN
|
||||
-- reposition pointer to current param
|
||||
lxi hl,PARAMS
|
||||
mov b,(ACTIVE_PARAM)
|
||||
dcx hl
|
||||
@POSITIONLOOP
|
||||
inx hl
|
||||
dcr b
|
||||
jmp POSITIONLOOP
|
||||
|
||||
|
||||
lxi de,0x3090
|
||||
-- SLEEP before note
|
||||
mvi c,0x10 -- wait 1 second of vblanks
|
||||
call SLEEPFUNC
|
||||
|
||||
|
||||
mvi a,0xFD -- we want to store that into PA so that portB can be later scanned i guess like the vic/c64 kb
|
||||
mov pa,a
|
||||
mov a,pb -- see which are closed upon return
|
||||
|
||||
oni a,2
|
||||
jr RIGHT_DOWN
|
||||
jr RIGHT_END
|
||||
@RIGHT_DOWN
|
||||
ldax (hl)
|
||||
adi a,1
|
||||
stax (hl)
|
||||
|
||||
-- if playing replay patter
|
||||
mov a,(PLAYING)
|
||||
eqi a,1
|
||||
jmp ALL_DONE
|
||||
-- fake not already playing
|
||||
mvi a,0
|
||||
mov (PLAYING),a -- ON
|
||||
call PLAY_PATTERN
|
||||
jmp ALL_DONE
|
||||
|
||||
@RIGHT_END
|
||||
|
||||
oni a,1
|
||||
jr DOWN_DOWN
|
||||
jr DOWN_END
|
||||
@DOWN_DOWN
|
||||
mov a,(ACTIVE_PARAM)
|
||||
adi a,1
|
||||
mov (ACTIVE_PARAM),a
|
||||
jmp ALL_DONE
|
||||
@DOWN_END
|
||||
|
||||
mvi a,0xFE
|
||||
mov pa,a -- fill port with voltages
|
||||
mov a,pb -- see which are closed upon return
|
||||
|
||||
oni a,1
|
||||
jr LEFT_DOWN
|
||||
jr LEFT_END
|
||||
@LEFT_DOWN
|
||||
ldax (hl)
|
||||
sui a,1
|
||||
stax (hl)
|
||||
|
||||
-- if playing replay patter
|
||||
mov a,(PLAYING)
|
||||
eqi a,1
|
||||
jmp ALL_DONE
|
||||
-- fake not already playing
|
||||
mvi a,0
|
||||
mov (PLAYING),a -- OFF
|
||||
call PLAY_PATTERN
|
||||
jmp ALL_DONE
|
||||
@LEFT_END
|
||||
|
||||
oni a,2
|
||||
jr UP_DOWN
|
||||
jr UP_END
|
||||
@UP_DOWN
|
||||
mov a,(ACTIVE_PARAM)
|
||||
sui a,1
|
||||
mov (ACTIVE_PARAM),a
|
||||
jmp ALL_DONE
|
||||
@UP_END
|
||||
|
||||
oni a,4
|
||||
jr BUTTON_DOWN
|
||||
jr BUTTON_UP
|
||||
@BUTTON_DOWN
|
||||
call PLAY_PATTERN
|
||||
jmp ALL_DONE
|
||||
@BUTTON_UP
|
||||
call STOP_PATTERN
|
||||
jr BUTTON_END
|
||||
@BUTTON_END
|
||||
|
||||
@ALL_DONE
|
||||
|
||||
-- check bounds on active param
|
||||
mov a,(ACTIVE_PARAM)
|
||||
lti a,0x0A
|
||||
jr ACT_GREATER
|
||||
jr ACT_DONE
|
||||
@ACT_GREATER
|
||||
mvi a,0x00
|
||||
mov (ACTIVE_PARAM),a
|
||||
jr ACT_DONE
|
||||
@ACT_DONE
|
||||
|
||||
-- print where we at!
|
||||
call DISPLAY_LOOP
|
||||
|
||||
jmp LOOPN
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- Display init data
|
||||
-------------------------------------------------------------------------
|
||||
@VDC_DATA
|
||||
dc.b 0xC0,0x00,0x00,0xF1 -- 0F1 -> black
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- lame sleep (with C param)
|
||||
-------------------------------------------------------------------------
|
||||
section{"SLEEPFUNC", org=0x8100}
|
||||
-- sleep the amount of time depending on C
|
||||
push bc
|
||||
@SLOOP
|
||||
mvi b,0xff
|
||||
@SLEEP0
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
dcr b
|
||||
jr SLEEP0
|
||||
dcr c
|
||||
jr SLOOP
|
||||
pop bc
|
||||
ret
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- wait on VBLANK
|
||||
-------------------------------------------------------------------------
|
||||
section{"WVBLANK", org=0x8200}
|
||||
skit f2 -- wait until acknowledged
|
||||
jr WVBLANK
|
||||
ret
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- wait on D1771g (wait until INT1=1)
|
||||
-------------------------------------------------------------------------
|
||||
section{"WD1771G", org=0x8220}
|
||||
skit f1
|
||||
jr WD1771G
|
||||
ret
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- Print ONE UINT8_T on screen as HEX (two screen chars printed)
|
||||
--
|
||||
-- A should contain the hex value to print (A returns unchanged)
|
||||
-- D should contain the screen value to dump the high part (D returns unchanged)
|
||||
-- V is thrashed (used as temporary val)
|
||||
-------------------------------------------------------------------------
|
||||
section{"PRINT_HEX", org=0x8240}
|
||||
mvi v,0 -- trick to do V=A
|
||||
add v,a -- trick to do V=A
|
||||
|
||||
ani a,0xF0 -- a = a & 0x0f;
|
||||
clc -- clear carry so not to fuck up the following right shifts
|
||||
rlr a --
|
||||
rlr a --
|
||||
rlr a --
|
||||
rlr a -- ok we got the HEX value for the up 4bits
|
||||
|
||||
gti a,0x09
|
||||
jr HIGH_GREATER
|
||||
jr HIGH_LOWER
|
||||
@HIGH_GREATER
|
||||
adi a,0x30 -- a+='0' (ascii)
|
||||
jr HIGH_DONE
|
||||
@HIGH_LOWER
|
||||
adi a,0x37 -- a+=('A'-10) (ascii)
|
||||
jr HIGH_DONE
|
||||
@HIGH_DONE
|
||||
|
||||
staxi (de) --
|
||||
|
||||
mvi a,0 -- trick to do A=V
|
||||
add a,v -- trick to do A=V
|
||||
ani a,0x0F -- a = a & 0x0f
|
||||
|
||||
gti a,0x09
|
||||
jr LOW_GREATER
|
||||
jr LOW_LOWER
|
||||
@LOW_GREATER
|
||||
adi a,0x30 -- a+='0' (ascii)
|
||||
jr LOW_DONE
|
||||
@LOW_LOWER
|
||||
adi a,0x37 -- a+=('A'-10) (ascii)
|
||||
jr LOW_DONE
|
||||
@LOW_DONE
|
||||
|
||||
staxd (de) --
|
||||
mvi a,0 -- trick to do A=V
|
||||
add a,v -- trick to do A=V
|
||||
ret
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- Prints all current param values
|
||||
-------------------------------------------------------------------------
|
||||
section{"DISPLAY_LOOP", org=0x8280}
|
||||
mvi b,0x0B
|
||||
lxi de,0x3090
|
||||
lxi hl,PARAMS
|
||||
@DLOOP
|
||||
|
||||
-- if b==ACTIVE_PARAM show arrow
|
||||
-- else show space
|
||||
mvi a,0x0B
|
||||
mov c,(ACTIVE_PARAM)
|
||||
sub a,c
|
||||
|
||||
eqa a,b
|
||||
jr NOCURSOR
|
||||
mvi a,0x1C
|
||||
stax (de)
|
||||
jr CURSDONE
|
||||
@NOCURSOR
|
||||
mvi a,0x20
|
||||
stax (de)
|
||||
@CURSDONE
|
||||
-- print all ram
|
||||
ldaxi (hl)
|
||||
-- increase D pos to display in hex
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
call PRINT_HEX
|
||||
dcx de
|
||||
dcx de
|
||||
dcx de
|
||||
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
inx de
|
||||
|
||||
dcr b
|
||||
jmp DLOOP
|
||||
ret
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- Stop current pattern
|
||||
-------------------------------------------------------------------------
|
||||
section{"STOP_PATTERN", org=0x82d0}
|
||||
-- if (!playing) return;
|
||||
mov a,(PLAYING)
|
||||
eqi a,1
|
||||
jr STOP_DONE
|
||||
|
||||
mvi a,0
|
||||
mov (PLAYING),a -- OFF
|
||||
|
||||
di
|
||||
lxi hl,0x3600 -- SOUND REGISTER in H
|
||||
call WVBLANK -- cwait on intf2
|
||||
skit f1
|
||||
nop
|
||||
|
||||
mvix (hl),0x00 -- note OFF
|
||||
|
||||
ei
|
||||
|
||||
@STOP_DONE
|
||||
ret
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- Plays current pattern
|
||||
-- Start with beep please
|
||||
-------------------------------------------------------------------------
|
||||
section{"PLAY_PATTERN", org=0x82f0}
|
||||
mov a,(PLAYING)
|
||||
eqi a,0
|
||||
jmp PLAY_DONE
|
||||
|
||||
-- setting variable true!
|
||||
mvi a,1
|
||||
mov (PLAYING),a -- ON
|
||||
|
||||
lxi de,PARAMS
|
||||
lxi hl,0x3600 -- SOUND REGISTER in H
|
||||
|
||||
|
||||
ldaxi (de)
|
||||
eqi a,1
|
||||
jmp PLAY_TONE
|
||||
jmp PLAY_NOISE
|
||||
|
||||
@PLAY_TONE
|
||||
------------------------ Tone Start ------------------------
|
||||
di
|
||||
call WVBLANK
|
||||
skit f1
|
||||
nop
|
||||
-- instrument params start
|
||||
mvi a,2 -- tone!
|
||||
stax (hl)
|
||||
call WD1771G -- cwait on intf1
|
||||
ldaxi (de)
|
||||
stax (hl)
|
||||
call WD1771G -- cwait on intf1
|
||||
ldaxi (de)
|
||||
stax (hl)
|
||||
call WD1771G -- cwait on intf1
|
||||
ldaxi (de)
|
||||
stax (hl)
|
||||
call WVBLANK -- cwait on intf2
|
||||
-- instrument params end
|
||||
ei
|
||||
jmp PLAY_DONE
|
||||
|
||||
@PLAY_NOISE
|
||||
------------------------ Tone Start ------------------------
|
||||
di
|
||||
call WVBLANK -- cwait on intf2
|
||||
skit f1
|
||||
nop
|
||||
-- instrument params start
|
||||
mvi a,1 -- NOISE
|
||||
stax (hl)
|
||||
call WD1771G -- cwait on intf1
|
||||
ldaxi (de)
|
||||
stax (hl)
|
||||
call WD1771G -- cwait on intf1
|
||||
ldaxi (de)
|
||||
stax (hl)
|
||||
call WD1771G -- cwait on intf1
|
||||
ldaxi (de)
|
||||
stax (hl)
|
||||
call WD1771G -- cwait on intf1
|
||||
ldaxi (de)
|
||||
stax (hl)
|
||||
call WD1771G -- cwait on intf1
|
||||
ldaxi (de)
|
||||
stax (hl)
|
||||
call WD1771G -- cwait on intf1
|
||||
ldaxi (de)
|
||||
stax (hl)
|
||||
call WD1771G -- cwait on intf1
|
||||
ldaxi (de)
|
||||
stax (hl)
|
||||
call WD1771G -- cwait on intf1
|
||||
ldaxi (de)
|
||||
stax (hl)
|
||||
call WD1771G -- cwait on intf1
|
||||
ldaxi (de)
|
||||
stax (hl)
|
||||
call WVBLANK -- cwait on intf2
|
||||
-- instrument params end
|
||||
ei
|
||||
jmp PLAY_DONE
|
||||
|
||||
@PLAY_DONE
|
||||
ret
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
-- Message Data1
|
||||
-- this version has left+audio changes working
|
||||
-------------------------------------------------------------------------
|
||||
@DISPLAY_DATA
|
||||
dc.b " Plogue uPD1771C Tester "
|
||||
dc.b " v0.0018 "
|
||||
dc.b " Param[0]: ( ) "
|
||||
dc.b " Param[1]: ( ) "
|
||||
dc.b " Param[2]: ( ) "
|
||||
dc.b " Param[3]: ( ) "
|
||||
dc.b " Param[4]: ( ) "
|
||||
dc.b " Param[5]: ( ) "
|
||||
dc.b " Param[6]: ( ) "
|
||||
dc.b " Param[7]: ( ) "
|
||||
dc.b " Param[8]: ( ) "
|
||||
dc.b " Param[9]: ( ) "
|
||||
|
||||
-- go to [FF90;FF9F]
|
||||
@INITIAL_RAM
|
||||
-- params
|
||||
dc.b 0x02
|
||||
dc.b 0x80
|
||||
dc.b 0xFF
|
||||
dc.b 0x1F
|
||||
|
||||
dc.b 0xAB
|
||||
dc.b 0xFE
|
||||
dc.b 0xCD
|
||||
dc.b 0xDC
|
||||
dc.b 0x45
|
||||
dc.b 0x01
|
||||
|
||||
dc.b 0x00 -- FF9A curent param
|
||||
dc.b 0x00 -- FF9B BOOL note on
|
||||
dc.b 0x83 -- misc
|
||||
dc.b 0x39 -- misc
|
||||
dc.b 0x44 -- misc
|
||||
dc.b 0x36 -- misc
|
||||
|
||||
writebin(filename..'.bin', genbin(0xff))
|
|
@ -0,0 +1,738 @@
|
|||
require 'scv'
|
||||
location(0x8000, 0x8FFF)
|
||||
section{"rom", org=0x8000}
|
||||
dc.b 'H'
|
||||
@main
|
||||
block
|
||||
calb
|
||||
ei
|
||||
daa
|
||||
di
|
||||
ex
|
||||
exx
|
||||
halt
|
||||
jb
|
||||
nop
|
||||
ret
|
||||
reti
|
||||
rets
|
||||
sio
|
||||
softi
|
||||
stm
|
||||
table
|
||||
dcr a
|
||||
dcr b
|
||||
dcr c
|
||||
inr a
|
||||
inr b
|
||||
inr c
|
||||
dcx sp
|
||||
inx sp
|
||||
mvi a,0xca
|
||||
mvi b,0xfe
|
||||
mvi c,0xbe
|
||||
mvi d,0xef
|
||||
mvi e,0xf0
|
||||
mvi h,0x0d
|
||||
mvi l,0x78
|
||||
mvi v,0x01
|
||||
aci a,0x0f
|
||||
adi a,0x1e
|
||||
adinc a,0x2d
|
||||
ani a,0x3c
|
||||
eqi a,0x4b
|
||||
gti a,0x5a
|
||||
lti a,0x69
|
||||
nei a,0x87
|
||||
offi a,0x96
|
||||
oni a,0xa5
|
||||
ori a,0xb4
|
||||
sbi a,0xc3
|
||||
sui a,0xd2
|
||||
suinb a,0xe1
|
||||
xri a,0xf0
|
||||
lxi bc,0xabcd
|
||||
lxi de,0xbeef
|
||||
lxi hl,0xcafe
|
||||
lxi sp,0xf00d
|
||||
call 0xd701
|
||||
jmp 0x8e07
|
||||
clc
|
||||
pen
|
||||
per
|
||||
pex
|
||||
rld
|
||||
rrd
|
||||
stc
|
||||
mov a,b
|
||||
mov a,c
|
||||
mov a,d
|
||||
mov a,e
|
||||
mov a,h
|
||||
mov a,l
|
||||
mov b,a
|
||||
mov c,a
|
||||
mov d,a
|
||||
mov e,a
|
||||
mov h,a
|
||||
mov l,a
|
||||
@l0 jre l0
|
||||
@l1 nop
|
||||
jre l1
|
||||
@l2 nop nop
|
||||
jre l2
|
||||
@l3 nop nop nop
|
||||
jre l3
|
||||
@l4 nop nop nop nop
|
||||
jre l4
|
||||
jre l5
|
||||
@l5 jre l6
|
||||
nop
|
||||
@l6 jre l7
|
||||
nop nop
|
||||
@l7 jre l8
|
||||
nop nop nop
|
||||
@l8 jre l9
|
||||
nop nop nop nop
|
||||
@l9
|
||||
ldax (bc)
|
||||
ldax (de)
|
||||
ldax (hl)
|
||||
stax (bc)
|
||||
stax (de)
|
||||
stax (hl)
|
||||
inrw (v,0x01)
|
||||
ldaw (v,0x23)
|
||||
dcrw (v,0x45)
|
||||
staw (v,0x67)
|
||||
bit0 (v,0x89)
|
||||
bit1 (v,0xab)
|
||||
bit2 (v,0xcd)
|
||||
bit3 (v,0xef)
|
||||
bit4 (v,0xfe)
|
||||
bit5 (v,0xdc)
|
||||
bit6 (v,0xba)
|
||||
bit7 (v,0x98)
|
||||
dcx bc
|
||||
dcx de
|
||||
dcx hl
|
||||
inx bc
|
||||
inx de
|
||||
inx hl
|
||||
mvix (bc),0xf9
|
||||
mvix (de),0xe8
|
||||
mvix (hl),0xd7
|
||||
mviw (v,0x9a),0x3f
|
||||
eqiw (v,0xc5),0x1b
|
||||
ldaxd (de)
|
||||
ldaxi (de)
|
||||
ldaxd (hl)
|
||||
ldaxi (hl)
|
||||
staxd (de)
|
||||
staxi (de)
|
||||
staxd (hl)
|
||||
staxi (hl)
|
||||
push bc
|
||||
push de
|
||||
push hl
|
||||
pop hl
|
||||
pop de
|
||||
pop bc
|
||||
push va
|
||||
pop va
|
||||
rll a
|
||||
rlr a
|
||||
rll c
|
||||
rlr c
|
||||
sll a
|
||||
slr a
|
||||
sll c
|
||||
slr c
|
||||
skc
|
||||
skz
|
||||
sknc
|
||||
sknz
|
||||
skit f0
|
||||
skit ft
|
||||
skit f1
|
||||
skit f2
|
||||
skit fs
|
||||
sknit f0
|
||||
sknit ft
|
||||
sknit f1
|
||||
sknit f2
|
||||
sknit fs
|
||||
in 0x00
|
||||
in 0xa7
|
||||
in 0xbf
|
||||
out 0x00
|
||||
out 0x5a
|
||||
out 0xbf
|
||||
mov a,pa
|
||||
mov a,pb
|
||||
mov a,pc
|
||||
mov a,mk
|
||||
mov a,mb
|
||||
mov a,mc
|
||||
mov a,tm0
|
||||
mov a,tm1
|
||||
mov a,s
|
||||
mov pa,a
|
||||
mov pb,a
|
||||
mov pc,a
|
||||
mov mk,a
|
||||
mov mb,a
|
||||
mov mc,a
|
||||
mov tm0,a
|
||||
mov tm1,a
|
||||
mov s,a
|
||||
ana v,a
|
||||
ana a,a
|
||||
ana b,a
|
||||
ana c,a
|
||||
ana d,a
|
||||
ana e,a
|
||||
ana h,a
|
||||
ana l,a
|
||||
xra v,a
|
||||
xra a,a
|
||||
xra b,a
|
||||
xra c,a
|
||||
xra d,a
|
||||
xra e,a
|
||||
xra h,a
|
||||
xra l,a
|
||||
ora v,a
|
||||
ora a,a
|
||||
ora b,a
|
||||
ora c,a
|
||||
ora d,a
|
||||
ora e,a
|
||||
ora h,a
|
||||
ora l,a
|
||||
addnc v,a
|
||||
addnc a,a
|
||||
addnc b,a
|
||||
addnc c,a
|
||||
addnc d,a
|
||||
addnc e,a
|
||||
addnc h,a
|
||||
addnc l,a
|
||||
gta v,a
|
||||
gta a,a
|
||||
gta b,a
|
||||
gta c,a
|
||||
gta d,a
|
||||
gta e,a
|
||||
gta h,a
|
||||
gta l,a
|
||||
subnb v,a
|
||||
subnb a,a
|
||||
subnb b,a
|
||||
subnb c,a
|
||||
subnb d,a
|
||||
subnb e,a
|
||||
subnb h,a
|
||||
subnb l,a
|
||||
lta v,a
|
||||
lta a,a
|
||||
lta b,a
|
||||
lta c,a
|
||||
lta d,a
|
||||
lta e,a
|
||||
lta h,a
|
||||
lta l,a
|
||||
add v,a
|
||||
add a,a
|
||||
add b,a
|
||||
add c,a
|
||||
add d,a
|
||||
add e,a
|
||||
add h,a
|
||||
add l,a
|
||||
adc v,a
|
||||
adc a,a
|
||||
adc b,a
|
||||
adc c,a
|
||||
adc d,a
|
||||
adc e,a
|
||||
adc h,a
|
||||
adc l,a
|
||||
sub v,a
|
||||
sub a,a
|
||||
sub b,a
|
||||
sub c,a
|
||||
sub d,a
|
||||
sub e,a
|
||||
sub h,a
|
||||
sub l,a
|
||||
nea v,a
|
||||
nea a,a
|
||||
nea b,a
|
||||
nea c,a
|
||||
nea d,a
|
||||
nea e,a
|
||||
nea h,a
|
||||
nea l,a
|
||||
sbb v,a
|
||||
sbb a,a
|
||||
sbb b,a
|
||||
sbb c,a
|
||||
sbb d,a
|
||||
sbb e,a
|
||||
sbb h,a
|
||||
sbb l,a
|
||||
eqa v,a
|
||||
eqa a,a
|
||||
eqa b,a
|
||||
eqa c,a
|
||||
eqa d,a
|
||||
eqa e,a
|
||||
eqa h,a
|
||||
eqa l,a
|
||||
ana a,v
|
||||
ana a,a
|
||||
ana a,b
|
||||
ana a,c
|
||||
ana a,d
|
||||
ana a,e
|
||||
ana a,h
|
||||
ana a,l
|
||||
xra a,v
|
||||
xra a,a
|
||||
xra a,b
|
||||
xra a,c
|
||||
xra a,d
|
||||
xra a,e
|
||||
xra a,h
|
||||
xra a,l
|
||||
ora a,v
|
||||
ora a,a
|
||||
ora a,b
|
||||
ora a,c
|
||||
ora a,d
|
||||
ora a,e
|
||||
ora a,h
|
||||
ora a,l
|
||||
addnc a,v
|
||||
addnc a,a
|
||||
addnc a,b
|
||||
addnc a,c
|
||||
addnc a,d
|
||||
addnc a,e
|
||||
addnc a,h
|
||||
addnc a,l
|
||||
gta a,v
|
||||
gta a,a
|
||||
gta a,b
|
||||
gta a,c
|
||||
gta a,d
|
||||
gta a,e
|
||||
gta a,h
|
||||
gta a,l
|
||||
subnb a,v
|
||||
subnb a,a
|
||||
subnb a,b
|
||||
subnb a,c
|
||||
subnb a,d
|
||||
subnb a,e
|
||||
subnb a,h
|
||||
subnb a,l
|
||||
lta a,v
|
||||
lta a,a
|
||||
lta a,b
|
||||
lta a,c
|
||||
lta a,d
|
||||
lta a,e
|
||||
lta a,h
|
||||
lta a,l
|
||||
add a,v
|
||||
add a,a
|
||||
add a,b
|
||||
add a,c
|
||||
add a,d
|
||||
add a,e
|
||||
add a,h
|
||||
add a,l
|
||||
ona a,v
|
||||
ona a,a
|
||||
ona a,b
|
||||
ona a,c
|
||||
ona a,d
|
||||
ona a,e
|
||||
ona a,h
|
||||
ona a,l
|
||||
adc a,v
|
||||
adc a,a
|
||||
adc a,b
|
||||
adc a,c
|
||||
adc a,d
|
||||
adc a,e
|
||||
adc a,h
|
||||
adc a,l
|
||||
offa a,v
|
||||
offa a,a
|
||||
offa a,b
|
||||
offa a,c
|
||||
offa a,d
|
||||
offa a,e
|
||||
offa a,h
|
||||
offa a,l
|
||||
sub a,v
|
||||
sub a,a
|
||||
sub a,b
|
||||
sub a,c
|
||||
sub a,d
|
||||
sub a,e
|
||||
sub a,h
|
||||
sub a,l
|
||||
nea a,v
|
||||
nea a,a
|
||||
nea a,b
|
||||
nea a,c
|
||||
nea a,d
|
||||
nea a,e
|
||||
nea a,h
|
||||
nea a,l
|
||||
sbb a,v
|
||||
sbb a,a
|
||||
sbb a,b
|
||||
sbb a,c
|
||||
sbb a,d
|
||||
sbb a,e
|
||||
sbb a,h
|
||||
sbb a,l
|
||||
eqa a,v
|
||||
eqa a,a
|
||||
eqa a,b
|
||||
eqa a,c
|
||||
eqa a,d
|
||||
eqa a,e
|
||||
eqa a,h
|
||||
eqa a,l
|
||||
ani v,0x00
|
||||
ani a,0x01
|
||||
ani b,0x02
|
||||
ani c,0x03
|
||||
ani d,0x04
|
||||
ani e,0x05
|
||||
ani h,0x06
|
||||
ani l,0x07
|
||||
xri v,0x08
|
||||
xri a,0x09
|
||||
xri b,0x0a
|
||||
xri c,0x0b
|
||||
xri d,0x0c
|
||||
xri e,0x0d
|
||||
xri h,0x0e
|
||||
xri l,0x0f
|
||||
ori v,0x10
|
||||
ori a,0x11
|
||||
ori b,0x12
|
||||
ori c,0x13
|
||||
ori d,0x14
|
||||
ori e,0x15
|
||||
ori h,0x16
|
||||
ori l,0x17
|
||||
adinc v,0x18
|
||||
adinc a,0x19
|
||||
adinc b,0x1a
|
||||
adinc c,0x1b
|
||||
adinc d,0x1c
|
||||
adinc e,0x1d
|
||||
adinc h,0x1e
|
||||
adinc l,0x1f
|
||||
gti v,0x20
|
||||
gti a,0x21
|
||||
gti b,0x22
|
||||
gti c,0x23
|
||||
gti d,0x24
|
||||
gti e,0x25
|
||||
gti h,0x26
|
||||
gti l,0x27
|
||||
suinb v,0x28
|
||||
suinb a,0x29
|
||||
suinb b,0x2a
|
||||
suinb c,0x2b
|
||||
suinb d,0x2c
|
||||
suinb e,0x2d
|
||||
suinb h,0x2e
|
||||
suinb l,0x2f
|
||||
lti v,0x30
|
||||
lti a,0x31
|
||||
lti b,0x32
|
||||
lti c,0x33
|
||||
lti d,0x34
|
||||
lti e,0x35
|
||||
lti h,0x36
|
||||
lti l,0x37
|
||||
adi v,0x38
|
||||
adi a,0x39
|
||||
adi b,0x3a
|
||||
adi c,0x3b
|
||||
adi d,0x3c
|
||||
adi e,0x3d
|
||||
adi h,0x3e
|
||||
adi l,0x4f
|
||||
oni v,0x40
|
||||
oni a,0x41
|
||||
oni b,0x42
|
||||
oni c,0x43
|
||||
oni d,0x44
|
||||
oni e,0x45
|
||||
oni h,0x46
|
||||
oni l,0x47
|
||||
aci v,0x48
|
||||
aci a,0x49
|
||||
aci b,0x4a
|
||||
aci c,0x4b
|
||||
aci d,0x4c
|
||||
aci e,0x4d
|
||||
aci h,0x4e
|
||||
aci l,0x4f
|
||||
offi v,0x50
|
||||
offi a,0x51
|
||||
offi b,0x52
|
||||
offi c,0x53
|
||||
offi d,0x54
|
||||
offi e,0x55
|
||||
offi h,0x56
|
||||
offi l,0x57
|
||||
sui v,0x58
|
||||
sui a,0x59
|
||||
sui b,0x5a
|
||||
sui c,0x5b
|
||||
sui d,0x5c
|
||||
sui e,0x5d
|
||||
sui h,0x5e
|
||||
sui l,0x5f
|
||||
nei v,0x60
|
||||
nei a,0x61
|
||||
nei b,0x62
|
||||
nei c,0x63
|
||||
nei d,0x64
|
||||
nei e,0x65
|
||||
nei h,0x66
|
||||
nei l,0x67
|
||||
sbi v,0x68
|
||||
sbi a,0x69
|
||||
sbi b,0x6a
|
||||
sbi c,0x6b
|
||||
sbi d,0x6c
|
||||
sbi e,0x6d
|
||||
sbi h,0x6e
|
||||
sbi l,0x6f
|
||||
eqi v,0x70
|
||||
eqi a,0x71
|
||||
eqi b,0x72
|
||||
eqi c,0x73
|
||||
eqi d,0x74
|
||||
eqi e,0x75
|
||||
eqi h,0x76
|
||||
eqi l,0x77
|
||||
ani pa,0x78
|
||||
ani pb,0x79
|
||||
ani pc,0x7a
|
||||
ani mk,0x7b
|
||||
xri pa,0x7c
|
||||
xri pb,0x7d
|
||||
xri pc,0x7e
|
||||
xri mk,0x7f
|
||||
ori pa,0x80
|
||||
ori pb,0x81
|
||||
ori pc,0x82
|
||||
ori mk,0x83
|
||||
adinc pa,0x84
|
||||
adinc pb,0x85
|
||||
adinc pc,0x86
|
||||
adinc mk,0x87
|
||||
gti pa,0x88
|
||||
gti pb,0x89
|
||||
gti pc,0x8a
|
||||
gti mk,0x8b
|
||||
suinb pa,0x8c
|
||||
suinb pb,0x8d
|
||||
suinb pc,0x8e
|
||||
suinb mk,0x8f
|
||||
lti pa,0x90
|
||||
lti pb,0x91
|
||||
lti pc,0x92
|
||||
lti mk,0x93
|
||||
adi pa,0x94
|
||||
adi pb,0x95
|
||||
adi pc,0x96
|
||||
adi mk,0x97
|
||||
oni pa,0x98
|
||||
oni pb,0x99
|
||||
oni pc,0x9a
|
||||
oni mk,0x9b
|
||||
aci pa,0x9c
|
||||
aci pb,0x9d
|
||||
aci pc,0x9e
|
||||
aci mk,0x9f
|
||||
offi pa,0xa0
|
||||
offi pb,0xa1
|
||||
offi pc,0xa2
|
||||
offi mk,0xa3
|
||||
sui pa,0xa4
|
||||
sui pb,0xa5
|
||||
sui pc,0xa6
|
||||
sui mk,0xa7
|
||||
nei pa,0xa8
|
||||
nei pb,0xa9
|
||||
nei pc,0xaa
|
||||
nei mk,0xab
|
||||
sbi pa,0xac
|
||||
sbi pb,0xad
|
||||
sbi pc,0xae
|
||||
sbi mk,0xaf
|
||||
eqi pa,0xb0
|
||||
eqi pb,0xb1
|
||||
eqi pc,0xb2
|
||||
eqi mk,0xb3
|
||||
anaw (v,0xf0)
|
||||
xraw (v,0xe1)
|
||||
oraw (v,0xd2)
|
||||
addncw (v,0xc3)
|
||||
gtaw (v,0xb4)
|
||||
subnbw (v,0xa5)
|
||||
ltaw (v,0x96)
|
||||
addw (v,0x87)
|
||||
onaw (v,0x78)
|
||||
adcw (v,0x69)
|
||||
offaw (v,0x5a)
|
||||
subw (v,0x4b)
|
||||
neaw (v,0x3c)
|
||||
sbbw (v,0x2d)
|
||||
eqaw (v,0x1e)
|
||||
sspd (0x8000)
|
||||
lspd (0x8001)
|
||||
sbcd (0x8010)
|
||||
lbcd (0x8011)
|
||||
sded (0x8100)
|
||||
lded (0x8101)
|
||||
shld (0x8110)
|
||||
lhld (0x8111)
|
||||
mov (0xab00),v
|
||||
mov (0xbc01),a
|
||||
mov (0xde02),b
|
||||
mov (0xfa03),c
|
||||
mov (0xeb04),d
|
||||
mov (0xdc05),e
|
||||
mov (0xcd06),h
|
||||
mov (0xbe07),l
|
||||
mov v,(0x000f)
|
||||
mov a,(0x00f0)
|
||||
mov b,(0x00ff)
|
||||
mov c,(0x0f00)
|
||||
mov d,(0x0f0f)
|
||||
mov e,(0x0ff0)
|
||||
mov h,(0x0fff)
|
||||
mov l,(0xf000)
|
||||
anax (bc)
|
||||
xrax (bc)
|
||||
orax (bc)
|
||||
addncx (bc)
|
||||
gtax (bc)
|
||||
subnbx (bc)
|
||||
ltax (bc)
|
||||
addx (bc)
|
||||
onax (bc)
|
||||
adcx (bc)
|
||||
offax (bc)
|
||||
subx (bc)
|
||||
neax (bc)
|
||||
sbbx (bc)
|
||||
eqax (bc)
|
||||
anax (de)
|
||||
xrax (de)
|
||||
orax (de)
|
||||
addncx (de)
|
||||
gtax (de)
|
||||
subnbx (de)
|
||||
ltax (de)
|
||||
addx (de)
|
||||
onax (de)
|
||||
adcx (de)
|
||||
offax (de)
|
||||
subx (de)
|
||||
neax (de)
|
||||
sbbx (de)
|
||||
eqax (de)
|
||||
anax (hl)
|
||||
xrax (hl)
|
||||
orax (hl)
|
||||
addncx (hl)
|
||||
gtax (hl)
|
||||
subnbx (hl)
|
||||
ltax (hl)
|
||||
addx (hl)
|
||||
onax (hl)
|
||||
adcx (hl)
|
||||
offax (hl)
|
||||
subx (hl)
|
||||
neax (hl)
|
||||
sbbx (hl)
|
||||
eqax (hl)
|
||||
anaxi (de)
|
||||
xraxi (de)
|
||||
oraxi (de)
|
||||
addncxi (de)
|
||||
gtaxi (de)
|
||||
subnbxi (de)
|
||||
ltaxi (de)
|
||||
addxi (de)
|
||||
onaxi (de)
|
||||
adcxi (de)
|
||||
offaxi (de)
|
||||
subxi (de)
|
||||
neaxi (de)
|
||||
sbbxi (de)
|
||||
eqaxi (de)
|
||||
anaxi (hl)
|
||||
xraxi (hl)
|
||||
oraxi (hl)
|
||||
addncxi (hl)
|
||||
gtaxi (hl)
|
||||
subnbxi (hl)
|
||||
ltaxi (hl)
|
||||
addxi (hl)
|
||||
onaxi (hl)
|
||||
adcxi (hl)
|
||||
offaxi (hl)
|
||||
subxi (hl)
|
||||
neaxi (hl)
|
||||
sbbxi (hl)
|
||||
eqaxi (hl)
|
||||
anaxd (de)
|
||||
xraxd (de)
|
||||
oraxd (de)
|
||||
addncxd (de)
|
||||
gtaxd (de)
|
||||
subnbxd (de)
|
||||
ltaxd (de)
|
||||
addxd (de)
|
||||
onaxd (de)
|
||||
adcxd (de)
|
||||
offaxd (de)
|
||||
subxd (de)
|
||||
neaxd (de)
|
||||
sbbxd (de)
|
||||
eqaxd (de)
|
||||
anaxd (hl)
|
||||
xraxd (hl)
|
||||
oraxd (hl)
|
||||
addncxd (hl)
|
||||
gtaxd (hl)
|
||||
subnbxd (hl)
|
||||
ltaxd (hl)
|
||||
addxd (hl)
|
||||
onaxd (hl)
|
||||
adcxd (hl)
|
||||
offaxd (hl)
|
||||
subxd (hl)
|
||||
neaxd (hl)
|
||||
sbbxd (hl)
|
||||
eqaxd (hl)
|
||||
|
||||
writebin(filename .. '.bin')
|
|
@ -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)
|
||||
|
|
|
@ -25,7 +25,7 @@ local wait = function() local l=label() lda INTIM bne l end
|
|||
|
||||
@@start
|
||||
-- clear zeropage
|
||||
cld ldx#0 txa @_clear dex tsx pha bne _clear
|
||||
cld ldx#0 txa @_clear dex txs pha bne _clear
|
||||
@main
|
||||
-- overscan
|
||||
sta WSYNC lda#2 sta VBLANK lda#TIM_OVERSCAN sta TIM64T wait()
|
||||
|
|
|
@ -0,0 +1,582 @@
|
|||
require'vcs'
|
||||
|
||||
-- change this if for some reason it fails to link
|
||||
-- space for 10 song pointers
|
||||
local FREESPACE = 3759-10*2
|
||||
|
||||
#pragma encapsulate far farcall
|
||||
#pragma alias and dna
|
||||
|
||||
mappers.F4()
|
||||
rom0.name = "core"
|
||||
|
||||
do
|
||||
local ram = {
|
||||
{ tt_timer=1 },
|
||||
{ tt_cur_pat_index=2 },
|
||||
{ tt_cur_note_index=2 },
|
||||
{ tt_envelope_index=2 },
|
||||
{ tt_cur_ins=2 },
|
||||
{ tt_ptr = \(tmp) },
|
||||
{ tt_song = \(tmp+2) },
|
||||
{ tt_ptrtab = \(tmp+4) },
|
||||
|
||||
{ tt_song_ix=1 }, -- index into song_table
|
||||
{ tt_song_ix_next=1 }, -- queued song to play after fade out
|
||||
{ song_delay=1 }, -- fade out timer
|
||||
{ cur_song_ix=1 }, -- current song index, cached
|
||||
{ input_prev=1 }, -- previous SWCHA 4 MSB (player 0) value >> 1 | INPT4 MSB (fire player 0)
|
||||
|
||||
{ AUDC0s=1 }, { AUDC1s=1 }, { AUDF0s=1 }, { AUDF1s=1 }, { AUDV0s=1 }, { AUDV1s=1 },
|
||||
{ vubars=16 },
|
||||
|
||||
{ tmp=30 },
|
||||
|
||||
{ print_txt = \(tmp+1) }, -- text pointer, can cross
|
||||
{ print_line_count = \(tmp) }, -- number of lines to print
|
||||
{ print_ptr = \(tmp+3) }, -- array of pointers to the characters
|
||||
}
|
||||
|
||||
local ad = 0x80
|
||||
for k,v in ipairs(ram) do
|
||||
local f,t = pairs(v)
|
||||
k,v = f(t)
|
||||
if type(v) ~= 'function' then
|
||||
_ENV[k] = ad
|
||||
ad = ad + v
|
||||
end
|
||||
end
|
||||
assert(ad <= 0x100)
|
||||
for k,v in ipairs(ram) do
|
||||
local f,t = pairs(v)
|
||||
k,v = f(t)
|
||||
if type(v) == 'function' then _ENV[k] = v() end
|
||||
symbols[k] = _ENV[k]
|
||||
end
|
||||
end
|
||||
|
||||
-- 30 bytes fixed per song
|
||||
TT_INSCTRLTABLE = 0
|
||||
TT_INSADINDEXES = 2
|
||||
TT_INSSUSTAININDEXES = 4
|
||||
TT_INSRELEASEINDEXES = 6
|
||||
TT_INSFREQVOLTABLE = 8
|
||||
TT_PERCINDEXES = 10
|
||||
TT_PERCFREQTABLE = 12
|
||||
TT_PERCCTRLVOLTABLE = 14
|
||||
TT_PATTERNSPEEDS = 16
|
||||
TT_SEQUENCETABLE = 18
|
||||
TT_PATTERNPTRLO = 20
|
||||
TT_PATTERNPTRHI = 22
|
||||
TT_PTR_SZ = 24
|
||||
TT_GLOBAL_SPEED = 0+TT_PTR_SZ
|
||||
TT_SPEED = 1+TT_PTR_SZ
|
||||
TT_ODD_SPEED = 2+TT_PTR_SZ
|
||||
TT_USE_FUNKTEMPO = 3+TT_PTR_SZ
|
||||
TT_C0INIT = 4+TT_PTR_SZ
|
||||
TT_C1INIT = 5+TT_PTR_SZ
|
||||
|
||||
TT_FREQ_MASK = 0b00011111
|
||||
TT_INS_HOLD = 8
|
||||
TT_INS_PAUSE = 16
|
||||
TT_FIRST_PERC = 17
|
||||
|
||||
-- far calls to players to generate the stubs - the section will be stripped as it's unreferenced
|
||||
location(rom0)
|
||||
local sec_farstubgen = section("farstubsgen")
|
||||
|
||||
-- full ttt player with all possible options active, one for each rom bank but 0
|
||||
for i=1,7 do
|
||||
local loc = location(_ENV['rom'..i])
|
||||
loc.songs_lo,loc.songs_hi = section("songs_lo"..i), section("songs_hi"..i)
|
||||
local tt_fetchCurrentNote = section("tt_fetchCurrentNote"..i)
|
||||
@_constructPatPtr
|
||||
ldy tt_cur_pat_index,x lda (tt_ptrtab+TT_SEQUENCETABLE),y
|
||||
bpl _noPatternGoto
|
||||
dna #0b01111111 sta tt_cur_pat_index,x bpl _constructPatPtr
|
||||
@_noPatternGoto
|
||||
tay lda (tt_ptrtab+TT_PATTERNPTRLO),y sta tt_ptr lda (tt_ptrtab+TT_PATTERNPTRHI),y sta tt_ptr+1
|
||||
clv
|
||||
lda tt_cur_note_index,x bpl _notPrefetched
|
||||
dna #0b01111111 sta tt_cur_note_index,x
|
||||
bit "bit6Set"..i
|
||||
@_notPrefetched
|
||||
tay
|
||||
lda (tt_ptr),y bne _noEndOfPattern
|
||||
sta tt_cur_note_index,x
|
||||
inc tt_cur_pat_index,x
|
||||
bne _constructPatPtr
|
||||
@_noEndOfPattern
|
||||
rts
|
||||
|
||||
local tt_CalcInsIndex = section("tt_CalcInsIndex"..i)
|
||||
lsr lsr lsr lsr lsr
|
||||
tay
|
||||
label("bit6Set"..i)
|
||||
rts
|
||||
|
||||
loc.tt_tick = section("tt_tick"..i).instructions[1] -- get label table, used as index for far stubs
|
||||
lda loc.songs_lo,y sta tt_song lda loc.songs_hi,y sta tt_song+1
|
||||
bit tt_timer bpl _nosonginit
|
||||
lda #0 sta tt_timer
|
||||
ldy #TT_C0INIT lda (tt_song),y sta tt_cur_pat_index iny lda (tt_song),y sta tt_cur_pat_index+1
|
||||
@_nosonginit
|
||||
ldy #TT_PTR_SZ-1 @_setptrs lda (tt_song),y sta tt_ptrtab,y dey bpl _setptrs
|
||||
dec tt_timer bpl _noNewNote
|
||||
ldx #1
|
||||
@_advanceLoop
|
||||
jsr tt_fetchCurrentNote
|
||||
cmp #TT_INS_PAUSE
|
||||
beq _pause
|
||||
bcs _newNote
|
||||
adc tt_cur_ins,x sec sbc #8 sta tt_cur_ins,x
|
||||
bcs _finishedNewNote
|
||||
@_pause
|
||||
lda tt_cur_ins,x jsr tt_CalcInsIndex
|
||||
lda (tt_ptrtab+TT_INSRELEASEINDEXES),y
|
||||
clc adc #1 bcc _storeADIndex
|
||||
@_newNote
|
||||
sta tt_cur_ins,x
|
||||
cmp #TT_FREQ_MASK+1 bcs _startInstrument
|
||||
tay
|
||||
lda (tt_ptrtab+TT_PERCINDEXES),y
|
||||
bne _storeADIndex
|
||||
@_startInstrument
|
||||
bvs _finishedNewNote
|
||||
jsr tt_CalcInsIndex
|
||||
lda (tt_ptrtab+TT_INSADINDEXES),y
|
||||
@_storeADIndex
|
||||
sta tt_envelope_index,x
|
||||
@_finishedNewNote
|
||||
inc tt_cur_note_index,x
|
||||
@_sequencerNextChannel
|
||||
dex
|
||||
bpl _advanceLoop
|
||||
ldy #TT_GLOBAL_SPEED lda (tt_song),y beq _localspeed
|
||||
iny lax (tt_song),y
|
||||
ldy #TT_USE_FUNKTEMPO lda (tt_song),y beq _globalnofunktempo
|
||||
lda tt_cur_note_index lsr bcc _noOddFrame ldy #TT_ODD_SPEED lax (tt_song),y @_noOddFrame
|
||||
@_globalnofunktempo
|
||||
stx tt_timer
|
||||
jmp _speeddone
|
||||
@_localspeed
|
||||
ldy tt_cur_pat_index lda (tt_ptrtab+TT_SEQUENCETABLE),y tay
|
||||
sty tt_ptr ldy #TT_USE_FUNKTEMPO lda (tt_song),y ldy tt_ptr cmp #0 beq _localnofunktempo
|
||||
lda tt_cur_note_index lsr
|
||||
lda (tt_ptrtab+TT_PATTERNSPEEDS),y
|
||||
bcc _evenFrame
|
||||
dna #0x0f
|
||||
bcs _storeFunkTempo
|
||||
@_evenFrame
|
||||
lsr lsr lsr lsr
|
||||
@_storeFunkTempo
|
||||
sta tt_timer
|
||||
jmp _speeddone
|
||||
@_localnofunktempo
|
||||
lda (tt_ptrtab+TT_PATTERNSPEEDS),y sta tt_timer
|
||||
@_speeddone
|
||||
@_noNewNote
|
||||
ldx #1
|
||||
@_updateLoop
|
||||
lda tt_cur_ins,x
|
||||
beq _afterAudioUpdate
|
||||
cmp #TT_FREQ_MASK+1 bcs _instrument
|
||||
ldy tt_envelope_index,x
|
||||
lda (tt_ptrtab+TT_PERCCTRLVOLTABLE),y beq _endOfPercussion inc tt_envelope_index,x @_endOfPercussion
|
||||
sta AUDV0s,x lsr lsr lsr lsr sta AUDC0s,x
|
||||
lda (tt_ptrtab+TT_PERCFREQTABLE),y
|
||||
sta AUDF0s,x
|
||||
bpl _afterAudioUpdate
|
||||
jsr tt_fetchCurrentNote
|
||||
cmp #TT_FREQ_MASK+1
|
||||
bcc _afterAudioUpdate
|
||||
sta tt_cur_ins,x
|
||||
jsr tt_CalcInsIndex
|
||||
lda (tt_ptrtab+TT_INSSUSTAININDEXES),y sta tt_envelope_index,x
|
||||
asl tt_cur_note_index,x sec ror tt_cur_note_index,x
|
||||
bmi _afterAudioUpdate
|
||||
@_instrument
|
||||
jsr tt_CalcInsIndex
|
||||
lda (tt_ptrtab+TT_INSCTRLTABLE),y sta AUDC0s,x
|
||||
lda tt_envelope_index,x cmp (tt_ptrtab+TT_INSRELEASEINDEXES),y bne _noEndOfSustain lda (tt_ptrtab+TT_INSSUSTAININDEXES),y @_noEndOfSustain
|
||||
tay
|
||||
lda (tt_ptrtab+TT_INSFREQVOLTABLE),y beq _endOfEnvelope iny @_endOfEnvelope
|
||||
sty tt_envelope_index,x
|
||||
sta AUDV0s,x
|
||||
lsr lsr lsr lsr clc adc tt_cur_ins,x sec sbc #8
|
||||
sta AUDF0s,x
|
||||
@_afterAudioUpdate
|
||||
dex
|
||||
bpl _updateLoop
|
||||
rts
|
||||
|
||||
section(sec_farstubgen) far loc.tt_tick
|
||||
|
||||
end
|
||||
|
||||
-- set free size in each location
|
||||
location(rom0)
|
||||
local songs_meta_lo,songs_meta_hi = section("songs_meta_lo"),section("songs_meta_hi")
|
||||
local locsz = {}
|
||||
for lix,loc in ipairs(locations) do
|
||||
if loc ~= rom0 then
|
||||
location(rom0)
|
||||
local secmeta = section("songs"..lix.."_meta")
|
||||
section(songs_meta_lo) byte_lo(secmeta)
|
||||
section(songs_meta_hi) byte_hi(secmeta)
|
||||
table.insert(locsz, { lix=lix, secmeta=secmeta, space=FREESPACE })
|
||||
end
|
||||
end
|
||||
|
||||
-- load all songs
|
||||
local songs = {}
|
||||
do
|
||||
local lfs = require'lfs'
|
||||
local dir = ... or lfs.currentdir()
|
||||
for file in lfs.dir(dir) do
|
||||
if file:sub(-4) == '.ttt' then
|
||||
local song = ttt(dir .. '/' .. file)
|
||||
table.insert(songs, song)
|
||||
local size = 0
|
||||
for _,v in pairs(song) do
|
||||
if type(v) == 'table' then
|
||||
if type(v[1]) == 'table' then
|
||||
for _,vv in ipairs(v) do size = size + #vv end
|
||||
else
|
||||
size = size + #v
|
||||
end
|
||||
end
|
||||
end
|
||||
size = size + 2*#song.patterns -- pattern pointers
|
||||
song.size = size + 30
|
||||
song.filename = file:sub(1,-5)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- place all songs
|
||||
charset(" abcdefghijklmnopqrstuvwxyz", \x(x*8))
|
||||
local meta = 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
|
||||
for i = 1, 12-#out do
|
||||
if i&1 ~= 0 then table.insert(out, 1, ' ')
|
||||
else table.insert(out, ' ')
|
||||
end
|
||||
end
|
||||
return table.concat(out):sub(1,12)
|
||||
end
|
||||
local song_ids = {} -- 4 LSB: song index, 4 MSB: bank index
|
||||
table.sort(songs, \a,b(a.size > b.size))
|
||||
for _,song in ipairs(songs) do
|
||||
table.sort(locsz, function(a,b)
|
||||
if a.space == b.space then return locations[a.lix].start < locations[b.lix].start end
|
||||
return a.space < b.space
|
||||
end)
|
||||
for _,locinfo in ipairs(locsz) do
|
||||
if locinfo.space >= song.size then
|
||||
locinfo.space = locinfo.space - song.size
|
||||
local loc = location(locations[locinfo.lix])
|
||||
local n = song.filename
|
||||
song_ids[n] = locinfo.lix-2 << 4 | #loc.songs_lo.instructions-1
|
||||
local s = function(str) return section(str..n) end
|
||||
s("tt_InsCtrlTable") byte(song.insctrltable)
|
||||
s("tt_InsADIndexes") byte(song.insadindexes)
|
||||
s("tt_InsSustainIndexes") byte(song.inssustainindexes)
|
||||
s("tt_InsReleaseIndexes") byte(song.insreleaseindexes)
|
||||
s("tt_InsFreqVolTable") byte(song.insfreqvoltable)
|
||||
s("tt_PercIndexes") byte(song.percindexes)
|
||||
s("tt_PercFreqTable") byte(song.percfreqtable)
|
||||
s("tt_PercCtrlVolTable") byte(song.percctrlvoltable)
|
||||
local patterns = {}
|
||||
for i,pattern in ipairs(song.patterns) do
|
||||
table.insert(patterns, section("pattern"..n..i)) byte(pattern)
|
||||
end
|
||||
s("tt_PatternSpeeds") byte(song.patternspeeds)
|
||||
s("tt_PatternPtrLo") byte_lo(patterns)
|
||||
s("tt_PatternPtrHi") byte_hi(patterns)
|
||||
s("tt_SequenceTable") byte(song.sequence[1]) byte(song.sequence[2])
|
||||
s("tt_song")
|
||||
local function ptr(t, v) t=t..n v=v or 0 byte_lo(\(symbols[t]-v)) byte_hi(\(symbols[t]-v)) end
|
||||
ptr("tt_InsCtrlTable", 1)
|
||||
ptr("tt_InsADIndexes", 1)
|
||||
ptr("tt_InsSustainIndexes", 1)
|
||||
ptr("tt_InsReleaseIndexes", 1)
|
||||
ptr("tt_InsFreqVolTable")
|
||||
ptr("tt_PercIndexes", 17)
|
||||
ptr("tt_PercFreqTable", 1)
|
||||
ptr("tt_PercCtrlVolTable", 1)
|
||||
ptr("tt_PatternSpeeds")
|
||||
ptr("tt_SequenceTable")
|
||||
ptr("tt_PatternPtrLo")
|
||||
ptr("tt_PatternPtrHi")
|
||||
dc.b song.globalspeed and 1 or 0, song.speed-1, song.oddspeed-1, song.usefunktempo and 1 or 0
|
||||
dc.b song.c0init, song.c1init
|
||||
-- append song pointer to pointer table
|
||||
section(loc.songs_lo) byte_lo("tt_song"..n)
|
||||
section(loc.songs_hi) byte_hi("tt_song"..n)
|
||||
-- rom0 data
|
||||
section(locinfo.secmeta)
|
||||
byte(meta(song.author))
|
||||
byte(meta(song.name and #song.name>0 and song.name or song.filename))
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- songs data
|
||||
----------------------------------------------------------------------
|
||||
|
||||
-- sort songs by alphabetic order
|
||||
local song_ixs = {}
|
||||
for k in pairs(song_ids) do song_ixs[#song_ixs+1] = k end
|
||||
table.sort(song_ixs)
|
||||
for k,v in ipairs(song_ixs) do song_ixs[k] = song_ids[v] end
|
||||
location(rom0)
|
||||
@@song_table byte(song_ixs)
|
||||
|
||||
-- player pointers table
|
||||
local song_players = {}
|
||||
for i=1,7 do
|
||||
table.insert(song_players, \(op_resolve(far_stubs[location(_ENV['rom'..i]).tt_tick][rom0])-1))
|
||||
end
|
||||
location(rom0)
|
||||
@@song_players_lo byte_lo(song_players)
|
||||
@@song_players_hi byte_hi(song_players)
|
||||
|
||||
@@song_tick
|
||||
lda cur_song_ix
|
||||
tax lsr lsr lsr lsr tay
|
||||
lda song_players_hi,y pha lda song_players_lo,y pha
|
||||
txa dna #0xf tay
|
||||
rts
|
||||
|
||||
@@song_switch
|
||||
ldy tt_song_ix_next sty tt_song_ix lda song_table,y sta cur_song_ix
|
||||
lda #0x80 sta tt_timer -- request song reset from player
|
||||
lda #0 ldx #8 @_resettt sta tt_timer,x dex bne _resettt
|
||||
ldx #5 @_resetaud sta AUDC0s,x dex bpl _resetaud
|
||||
rts
|
||||
|
||||
local song_norm = function()
|
||||
-- clamp to valid TIA range
|
||||
lda AUDC0s dna #0x0f sta AUDC0s lda AUDC1s dna #0x0f sta AUDC1s
|
||||
lda AUDF0s dna #0x1f sta AUDF0s lda AUDF1s dna #0x1f sta AUDF1s
|
||||
lda AUDV0s dna #0x0f sta AUDV0s lda AUDV1s dna #0x0f sta AUDV1s
|
||||
end
|
||||
|
||||
@volmul for x=0,255 do dc.b math.floor(15*(((x/16)/16)*((x%16)/15))) end
|
||||
|
||||
local song_transition = function()
|
||||
lda song_delay beq _transdone bpl _transapply
|
||||
inc song_delay bne _noswitch
|
||||
jsr song_switch jmp _transdone
|
||||
@_noswitch
|
||||
lda song_delay eor #0xff sec adc #0
|
||||
@_transapply
|
||||
asl asl asl dna #0xf0 ora AUDV0s tay ldx volmul,y stx AUDV0s
|
||||
dna #0xf0 ora AUDV1s tay ldx volmul,y stx AUDV1s
|
||||
@_transdone
|
||||
-- set result
|
||||
ldx #5 @_setaud lda AUDC0s,x sta AUDC0,x dex bpl _setaud
|
||||
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
|
||||
|
||||
-- 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
|
||||
|
||||
@@song_count dc.b #songs
|
||||
|
||||
local next_song = function()
|
||||
inc tt_song_ix_next lda song_count cmp tt_song_ix_next bne _nextsongdone lda#0 sta tt_song_ix_next @_nextsongdone
|
||||
end
|
||||
|
||||
local prev_song = function()
|
||||
dec tt_song_ix_next bpl _prevsongdone lda song_count sta tt_song_ix_next dec tt_song_ix_next @_prevsongdone
|
||||
end
|
||||
|
||||
local check_input = function()
|
||||
local JOYSTICK_RIGHT= 0x80
|
||||
local JOYSTICK_LEFT = 0x40
|
||||
lda SWCHA cmp input_prev beq _inputend
|
||||
sta input_prev
|
||||
lda #JOYSTICK_RIGHT bit input_prev bne _noright next_song() jmp _inputdone @_noright
|
||||
lda #JOYSTICK_LEFT bit input_prev bne _inputdone prev_song()
|
||||
@_inputdone
|
||||
lda #0xe0 sta song_delay
|
||||
@_inputend
|
||||
end
|
||||
|
||||
local tick = function()
|
||||
jsr song_tick
|
||||
song_norm()
|
||||
song_transition()
|
||||
vumeter_tick()
|
||||
end
|
||||
|
||||
@@mul24 for x=0,9 do byte(x*24) end
|
||||
|
||||
local kernel = function()
|
||||
lda#0x8a sta COLUP0 sta COLUP1
|
||||
lda cur_song_ix tax lsr lsr lsr lsr tay
|
||||
lda songs_meta_lo,y sta print_txt lda songs_meta_hi,y sta print_txt+1
|
||||
txa dna #0xf tay lda mul24,y clc adc print_txt sta print_txt lda print_txt+1 adc #0 sta print_txt+1
|
||||
lda#1 jsr print12
|
||||
|
||||
vumeter_draw()
|
||||
|
||||
lda#0xaa sta COLUP0 sta COLUP1
|
||||
lda print_txt clc adc #12 sta print_txt lda print_txt+1 adc #0 sta print_txt+1
|
||||
lda#1 jsr print12
|
||||
end
|
||||
|
||||
@@main
|
||||
init()
|
||||
lda SWCHA sta input_prev
|
||||
jsr song_switch
|
||||
@_frame
|
||||
overscan(check_input) vblank(tick) screen(kernel) jmp _frame
|
||||
|
||||
; -- needed if last instruction is implied
|
||||
writebin(filename..'.bin')
|
||||
writesym(filename..'.sym')
|
||||
print(stats)
|
|
@ -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,-- <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)
|
||||
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)
|
||||
|
|
|
@ -0,0 +1,638 @@
|
|||
M = require "asm"
|
||||
|
||||
local op_eval_byte = function(late, early) return M.byte_normalize(M.op_eval(late, early)) end
|
||||
M.op_eval_byte = op_eval_byte
|
||||
|
||||
local op_eval_word = function(late, early) return M.word_normalize(M.op_eval(late, early)) end
|
||||
M.op_eval_word = op_eval_word
|
||||
|
||||
local opimp={
|
||||
block=M.op(0x31,13),
|
||||
calb=M.op(0x63,13),
|
||||
daa=M.op(0x61,4),
|
||||
ex=M.op(0x10,4),
|
||||
exx=M.op(0x11,4),
|
||||
halt=M.op(0x01,6),
|
||||
jb=M.op(0x73,4),
|
||||
nop=M.op(0x00,4),
|
||||
ret=M.op(0x08,11),
|
||||
reti=M.op(0x62,15),
|
||||
rets=M.op(0x18,11),
|
||||
sio=M.op(0x09,4),
|
||||
softi=M.op(0x72,19),
|
||||
stm=M.op(0x19,4),
|
||||
['table']=M.op(0x21,19)
|
||||
} M.opimp = opimp
|
||||
for k,v in pairs(opimp) do
|
||||
M[k .. 'imp' ] = function()
|
||||
table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc })
|
||||
end
|
||||
end
|
||||
|
||||
local opa={
|
||||
dcr=M.op(0x51,4),
|
||||
inr=M.op(0x41,4),
|
||||
} M.opa = opa
|
||||
for k,v in pairs(opa) do
|
||||
M[k .. 'a'] = function()
|
||||
table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc })
|
||||
end
|
||||
end
|
||||
|
||||
local opb={
|
||||
dcr=M.op(0x52,4),
|
||||
inr=M.op(0x42,4),
|
||||
} M.opb = opb
|
||||
for k,v in pairs(opb) do
|
||||
M[k .. 'b'] = function()
|
||||
table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc })
|
||||
end
|
||||
end
|
||||
|
||||
local opc={
|
||||
dcr=M.op(0x53,4),
|
||||
inr=M.op(0x43,4),
|
||||
} M.opc = opc
|
||||
for k,v in pairs(opc) do
|
||||
M[k .. 'c'] = function()
|
||||
table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc })
|
||||
end
|
||||
end
|
||||
|
||||
local opsp={
|
||||
dcx=M.op(0x03,7),
|
||||
inx=M.op(0x02,7)
|
||||
} M.opsp = opsp
|
||||
for k,v in pairs(opsp) do
|
||||
M[k .. 'sp'] = function()
|
||||
table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc })
|
||||
end
|
||||
end
|
||||
|
||||
local opr16={
|
||||
dcxbc=M.op(0x13,7),
|
||||
dcxde=M.op(0x23,7),
|
||||
dcxhl=M.op(0x33,7),
|
||||
inxbc=M.op(0x12,7),
|
||||
inxde=M.op(0x22,7),
|
||||
inxhl=M.op(0x32,7),
|
||||
ldaxbc=M.op(0x29,7),
|
||||
ldaxdde=M.op(0x2e,7),
|
||||
ldaxde=M.op(0x2a,7),
|
||||
ldaxhl=M.op(0x2b,7),
|
||||
ldaxide=M.op(0x2c,7),
|
||||
ldaxdhl=M.op(0x2f,7),
|
||||
ldaxihl=M.op(0x2d,7),
|
||||
staxbc=M.op(0x39,7),
|
||||
staxde=M.op(0x3a,7),
|
||||
staxdde=M.op(0x3e,7),
|
||||
staxdhl=M.op(0x3f,7),
|
||||
staxhl=M.op(0x3b,7),
|
||||
staxide=M.op(0x3c,7),
|
||||
staxihl=M.op(0x3d,7),
|
||||
} M.opr16 = opr16
|
||||
for k,v in pairs(opr16) do
|
||||
M[k] = function()
|
||||
table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc })
|
||||
end
|
||||
end
|
||||
|
||||
local opregxx ={
|
||||
mvib=M.op(0x6a,7),
|
||||
mvic=M.op(0x6b,7),
|
||||
mvid=M.op(0x6c,7),
|
||||
mvie=M.op(0x6d,7),
|
||||
mvih=M.op(0x6e,7),
|
||||
mvil=M.op(0x6f,7),
|
||||
mviv=M.op(0x68,7),
|
||||
mvixbc=M.op(0x49,10),
|
||||
mvixde=M.op(0x4a,10),
|
||||
mvixhl=M.op(0x4b,10),
|
||||
} M.opregxx = opregxx
|
||||
for k,v in pairs(opregxx) do
|
||||
M[k] = function(late, early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local size = function() late,early = M.size_op(late,early) return 2 end
|
||||
local bin = function() local l7801dbg=l7801dbg return { v.opc, M.op_eval_byte(late,early) } end
|
||||
table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin })
|
||||
end
|
||||
end
|
||||
|
||||
local opaxx ={
|
||||
aci=M.op(0x56,7),
|
||||
adi=M.op(0x46,7),
|
||||
adinc=M.op(0x26,7),
|
||||
ani=M.op(0x07,7),
|
||||
eqi=M.op(0x77,7),
|
||||
gti=M.op(0x27,7),
|
||||
lti=M.op(0x37,7),
|
||||
mvi=M.op(0x69,7),
|
||||
nei=M.op(0x67,7),
|
||||
offi=M.op(0x57,7),
|
||||
oni=M.op(0x47,7),
|
||||
ori=M.op(0x17,7),
|
||||
sbi=M.op(0x76,7),
|
||||
sui=M.op(0x66,7),
|
||||
suinb=M.op(0x36,7),
|
||||
xri=M.op(0x16,7)
|
||||
} M.opaxx = opaxx
|
||||
for k,v in pairs(opaxx) do
|
||||
M[k .. 'a'] = function(late, early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local size = function() late,early = M.size_op(late,early) return 2 end
|
||||
local bin = function() local l7801dbg=l7801dbg return { v.opc, M.op_eval_byte(late,early) } end
|
||||
table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin })
|
||||
end
|
||||
end
|
||||
|
||||
local opr8r8 ={
|
||||
movab=M.op(0x0a,4), movac=M.op(0x0b,4),
|
||||
movad=M.op(0x0c,4), movae=M.op(0x0d,4),
|
||||
movah=M.op(0x0e,4), moval=M.op(0x0f,4),
|
||||
movba=M.op(0x1a,4), movca=M.op(0x1b,4),
|
||||
movda=M.op(0x1c,4), movea=M.op(0x1d,4),
|
||||
movha=M.op(0x1e,4), movla=M.op(0x1f,4),
|
||||
} M.opr8r8 = opr8r8
|
||||
for k,v in pairs(opr8r8) do
|
||||
M[k] = function()
|
||||
table.insert(M.section_current.instructions, { size=1, cycles=v.cycles, bin=v.opc })
|
||||
end
|
||||
end
|
||||
|
||||
local opw = {
|
||||
call=M.op(0x44,16),
|
||||
jmp=M.op(0x54,10),
|
||||
} M.opw = opw
|
||||
for k,v in pairs(opw) do
|
||||
M[k .. 'imm'] = function(late, early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local size = function() late,early = M.size_op(late,early) return 3 end
|
||||
local bin = function() local l7801dbg=l7801dbg
|
||||
local x = M.op_eval_word(late,early)
|
||||
return { v.opc, x&0xff, x>>8 }
|
||||
end
|
||||
table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin })
|
||||
end
|
||||
end
|
||||
|
||||
local opr16w = {
|
||||
lxisp=M.op(0x04,10),
|
||||
lxibc=M.op(0x14,10),
|
||||
lxide=M.op(0x24,10),
|
||||
lxihl=M.op(0x34,10)
|
||||
} M.opr16w = opr16w
|
||||
for k,v in pairs(opr16w) do
|
||||
M[k] = function(late, early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local size = function() late,early = M.size_op(late,early) return 3 end
|
||||
local bin = function() local l7801dbg=l7801dbg
|
||||
local x = M.op_eval_word(late,early)
|
||||
return { v.opc, x&0xff, x>>8 }
|
||||
end
|
||||
table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin })
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
M['calt' .. 'imm'] = function(late, early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local op = { cycles=19 }
|
||||
op.size = function() late,early = M.size_op(late,early) return 1 end
|
||||
op.bin = function()
|
||||
local l7801dbg=l7801dbg
|
||||
local x = M.op_eval_byte(late,early)
|
||||
if (x%2 == 1) then error("offset should be even : " .. x) end
|
||||
if x < 0x80 or x > 0xfe then error("offset out of range : " .. x) end
|
||||
x = (x>>1) + 0x40
|
||||
return x
|
||||
end
|
||||
table.insert(M.section_current.instructions, op)
|
||||
end
|
||||
|
||||
M['calf' .. 'imm'] = function(late, early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local op = { cycles=16 }
|
||||
op.size = function() late,early = M.size_op(late,early) return 2 end
|
||||
op.bin = function() local l7801dbg=l7801dbg
|
||||
local x = 0 + M.op_eval_word(late,early)
|
||||
if x < 0x0800 or x > 0xffff then error("subroutine address out of range [0x0800-0xffff]: " .. x) end
|
||||
x = x - 0x0800;
|
||||
return { 0x78 | ((x>>8) & 0x07), x&0xff }
|
||||
end
|
||||
table.insert(M.section_current.instructions, op)
|
||||
end
|
||||
|
||||
M.jr = function(label)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local parent,offset = M.label_current
|
||||
local section,rorg = M.section_current,M.location_current.rorg
|
||||
local op = { cycles=13 }
|
||||
op.size = function()
|
||||
offset = section.size
|
||||
label = M.size_dc(label)
|
||||
return 1
|
||||
end
|
||||
op.bin = function()
|
||||
local l7801dbg=l7801dbg
|
||||
local x,l = label,label
|
||||
if type(x) == 'function' then x=x() end
|
||||
if type(x) == 'string' then
|
||||
if x:sub(1,1) == '_' then x=parent..x l=x end
|
||||
x = symbols[x]
|
||||
end
|
||||
if type(x) ~= 'number' then error("unresolved branch target: " .. tostring(x)) end
|
||||
x = x-1 - offset - rorg(section.org)
|
||||
if x < -32 or x > 32 then error("branch target out of range for " .. l .. ": " .. x)
|
||||
elseif x >= 0 then
|
||||
x = 0xc0 + x
|
||||
return x
|
||||
else
|
||||
return x & 0xff
|
||||
end
|
||||
end
|
||||
table.insert(M.section_current.instructions, op)
|
||||
end
|
||||
|
||||
M.jre = function(label)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local parent,offset = M.label_current
|
||||
local section,rorg = M.section_current,M.location_current.rorg
|
||||
local op = { cycles=17 }
|
||||
op.size = function()
|
||||
offset = section.size
|
||||
label = M.size_dc(label)
|
||||
return 2
|
||||
end
|
||||
op.bin = function()
|
||||
local l7801dbg=l7801dbg
|
||||
local x,l = label,label
|
||||
if type(x) == 'function' then x=x() end
|
||||
if type(x) == 'string' then
|
||||
if x:sub(1,1) == '_' then x=parent..x l=x end
|
||||
x = symbols[x]
|
||||
end
|
||||
if type(x) ~= 'number' then error("unresolved branch target: " .. tostring(x)) end
|
||||
x = x-2 - offset - rorg(section.org)
|
||||
if x < -128 or x > 127 then error("branch target out of range for " .. l .. ": " .. x) end
|
||||
local opcode = x >= 0 and 0x4e or 0x4f
|
||||
return { opcode, x&0xff }
|
||||
end
|
||||
table.insert(M.section_current.instructions, op)
|
||||
end
|
||||
|
||||
local opwa = {
|
||||
inrw=M.op(0x20,13),
|
||||
ldaw=M.op(0x28,10),
|
||||
dcrw=M.op(0x30,13),
|
||||
staw=M.op(0x38,10),
|
||||
bit0=M.op(0x58,10),
|
||||
bit1=M.op(0x59,10),
|
||||
bit2=M.op(0x5a,10),
|
||||
bit3=M.op(0x5b,10),
|
||||
bit4=M.op(0x5c,10),
|
||||
bit5=M.op(0x5d,10),
|
||||
bit6=M.op(0x5e,10),
|
||||
bit7=M.op(0x5f,10)
|
||||
} M.opwa = opwa
|
||||
for k,v in pairs(opwa) do
|
||||
M[k .. 'wa'] = function(late, early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local size = function() late,early = M.size_op(late,early) return 2 end
|
||||
local bin = function() local l7801dbg=l7801dbg return { v.opc, M.op_eval_byte(late,early) } end
|
||||
table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin })
|
||||
end
|
||||
end
|
||||
|
||||
local opwaxx = {
|
||||
mviw=M.op(0x71,3,13),
|
||||
eqiw=M.op(0x75,3,13),
|
||||
} M.opwaxx = opwaxx
|
||||
for k,v in pairs(opwaxx) do
|
||||
M[k .. 'waxx'] = function(late_offset, late_data, early_offset, early_data)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local size = function()
|
||||
late_offset,early_offset = M.size_op(late_offset,early_offset)
|
||||
late_data,early_data = M.size_op(late_data,early_data)
|
||||
return 3
|
||||
end
|
||||
local bin = function()
|
||||
local l7801dbg=l7801dbg
|
||||
return { v.opc, M.op_eval_byte(late_offset,early_offset), M.op_eval_byte(late_data,early_data) }
|
||||
end
|
||||
table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin })
|
||||
end
|
||||
end
|
||||
|
||||
local op48imp = {
|
||||
ei=M.op(0x20,8),
|
||||
di=M.op(0x24,8),
|
||||
clc=M.op(0x2a,8),
|
||||
pen=M.op(0x2c,11),
|
||||
per=M.op(0x3c,11),
|
||||
pex=M.op(0x2d,11),
|
||||
rld=M.op(0x38,17),
|
||||
rrd=M.op(0x39,17),
|
||||
skc=M.op(0x0a,8),
|
||||
skz=M.op(0x0c,8),
|
||||
sknc=M.op(0x1a,8),
|
||||
sknz=M.op(0x1c,8),
|
||||
stc=M.op(0x2b,8),
|
||||
} M.op48imp = op48imp
|
||||
for k,v in pairs(op48imp) do
|
||||
M[k .. 'imp'] = function()
|
||||
table.insert(M.section_current.instructions, { size=2, cycles=v.cycles, bin={ 0x48, v.opc } })
|
||||
end
|
||||
end
|
||||
|
||||
local op48r8={
|
||||
rlla=M.op(0x30,8),
|
||||
rlra=M.op(0x31,8),
|
||||
rllc=M.op(0x32,8),
|
||||
rlrc=M.op(0x33,8),
|
||||
slla=M.op(0x34,8),
|
||||
slra=M.op(0x35,8),
|
||||
sllc=M.op(0x36,8),
|
||||
slrc=M.op(0x37,8),
|
||||
} M.op48r8 = op48r8
|
||||
for k,v in pairs(op48r8) do
|
||||
M[k] = function()
|
||||
table.insert(M.section_current.instructions, { size=2, cycles=v.cycles, bin={ 0x48, v.opc } })
|
||||
end
|
||||
end
|
||||
|
||||
local op48r16={
|
||||
pushbc=M.op(0x1e,17),
|
||||
pushde=M.op(0x2e,17),
|
||||
pushhl=M.op(0x3e,17),
|
||||
pushva=M.op(0x0e,17),
|
||||
popbc=M.op(0x1f,15),
|
||||
popde=M.op(0x2f,15),
|
||||
pophl=M.op(0x3f,15),
|
||||
popva=M.op(0x0f,15),
|
||||
} M.op48r16 = op48r16
|
||||
for k,v in pairs(op48r16) do
|
||||
M[k] = function()
|
||||
table.insert(M.section_current.instructions, { size=2, cycles=v.cycles, bin={ 0x48, v.opc } })
|
||||
end
|
||||
end
|
||||
|
||||
local op48int={
|
||||
skitf0=M.op(0x00,8),
|
||||
skitft=M.op(0x01,8),
|
||||
skitf1=M.op(0x02,8),
|
||||
skitf2=M.op(0x03,8),
|
||||
skitfs=M.op(0x04,8),
|
||||
sknitf0=M.op(0x10,8),
|
||||
sknitft=M.op(0x11,8),
|
||||
sknitf1=M.op(0x12,8),
|
||||
sknitf2=M.op(0x13,8),
|
||||
sknitfs=M.op(0x14,8),
|
||||
} M.op48int = op48int
|
||||
for k,v in pairs(op48int) do
|
||||
M[k] = function()
|
||||
table.insert(M.section_current.instructions, { size=2, cycles=v.cycles, bin={ 0x48, v.opc } })
|
||||
end
|
||||
end
|
||||
|
||||
-- IN/OUT
|
||||
local opinout={
|
||||
['in']=M.op(0x4c,10),
|
||||
out=M.op(0x4d,10),
|
||||
} M.opinout = op4inout
|
||||
for k,v in pairs(opinout) do
|
||||
M[k .. 'imm'] = function(late, early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local op = { cycles=v.cycles }
|
||||
op.size = function() late,early = M.size_op(late,early) return 2 end
|
||||
op.bin = function() local l7801dbg=l7801dbg
|
||||
local x = 0x00 + M.op_eval_byte(late,early)
|
||||
return { v.opc, 0x00, x }
|
||||
end
|
||||
table.insert(M.section_current.instructions, op)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local op4car8={
|
||||
movapa=M.op(0xc0,10),
|
||||
movapb=M.op(0xc1,10),
|
||||
movapc=M.op(0xc2,10),
|
||||
movamk=M.op(0xc3,10),
|
||||
movamb=M.op(0xc4,10),
|
||||
movamc=M.op(0xc5,10),
|
||||
movatm0=M.op(0xc6,10),
|
||||
movatm1=M.op(0xc7,10),
|
||||
movas=M.op(0xc8,10),
|
||||
} M.op4car8 = op4car8
|
||||
for k,v in pairs(op4car8) do
|
||||
M[k] = function()
|
||||
table.insert(M.section_current.instructions, { size=2, cycles=v.cycles, bin={ 0x4c, v.opc } })
|
||||
end
|
||||
end
|
||||
|
||||
local op4dr8a={
|
||||
movpaa=M.op(0xc0,10),
|
||||
movpba=M.op(0xc1,10),
|
||||
movpca=M.op(0xc2,10),
|
||||
movmka=M.op(0xc3,10),
|
||||
movmba=M.op(0xc4,10),
|
||||
movmca=M.op(0xc5,10),
|
||||
movtm0a=M.op(0xc6,10),
|
||||
movtm1a=M.op(0xc7,10),
|
||||
movsa=M.op(0xc8,10),
|
||||
} M.op4dr8a = op4dr8a
|
||||
for k,v in pairs(op4dr8a) do
|
||||
M[k] = function()
|
||||
table.insert(M.section_current.instructions, { size=2, cycles=v.cycles, bin={ 0x4d, v.opc } })
|
||||
end
|
||||
end
|
||||
|
||||
local op60names = {'ana','xra','ora','addnc','gta','subnb','lta','add','','adc','','sub','nea','sbb','eqa'}
|
||||
local register_names = {'v','a','b','c','d','e','h','l'}
|
||||
local k = 0x08
|
||||
for i,o in ipairs(op60names) do
|
||||
if o == '' then
|
||||
k = k + #register_names
|
||||
else
|
||||
for j,r in ipairs(register_names) do
|
||||
local l = k
|
||||
M[o .. r .. 'a'] = function()
|
||||
table.insert(M.section_current.instructions, { size=2, cycles=8, bin={ 0x60, l } })
|
||||
end
|
||||
k = k + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
k = 0x88
|
||||
op60names[9] = 'ona'
|
||||
op60names[11] = 'offa'
|
||||
for i,o in ipairs(op60names) do
|
||||
if o == '' then
|
||||
k = k + #register_names
|
||||
else
|
||||
for j,r in ipairs(register_names) do
|
||||
local l = k
|
||||
local name = o .. 'a' .. r
|
||||
if not M[name] then
|
||||
M[name] = function()
|
||||
table.insert(M.section_current.instructions, { size=2, cycles=8, bin={ 0x60, l } })
|
||||
end
|
||||
end
|
||||
k = k + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
k = 0x08
|
||||
local op64names = { 'ani','xri','ori','adinc','gti','suinb','lti','adi','oni','aci','offi','sui','nei','sbi','eqi' }
|
||||
for i,o in ipairs(op64names) do
|
||||
for j,r in ipairs(register_names) do
|
||||
local name = o .. r
|
||||
if not M[name] then
|
||||
local l = k
|
||||
M[name] = function(late,early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local op = { cycles=11 }
|
||||
op.size = function() late,early = M.size_op(late,early) return 3 end
|
||||
op.bin = function() local l7801dbg=l7801dbg
|
||||
local x = 0x00 + l;
|
||||
local y = 0x00 + M.op_eval_byte(late,early)
|
||||
return { 0x64, x, y }
|
||||
end
|
||||
table.insert(M.section_current.instructions, op)
|
||||
end
|
||||
end
|
||||
k = k + 1
|
||||
end
|
||||
end
|
||||
|
||||
k = 0x88
|
||||
local ex_register_names = {'pa','pb','pc','mk'}
|
||||
for i,o in ipairs(op64names) do
|
||||
for j,r in ipairs(ex_register_names) do
|
||||
local name = o .. r
|
||||
if not M[name] then
|
||||
local l = k
|
||||
M[name] = function(late,early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local op = { cycles=11 }
|
||||
op.size = function() late,early = M.size_op(late,early) return 3 end
|
||||
op.bin = function() local l7801dbg=l7801dbg
|
||||
local x = 0x00 + l;
|
||||
local y = 0x00 + M.op_eval_byte(late,early)
|
||||
return { 0x64, x, y }
|
||||
end
|
||||
table.insert(M.section_current.instructions, op)
|
||||
end
|
||||
end
|
||||
k = k + 1
|
||||
end
|
||||
k = k + 4
|
||||
end
|
||||
|
||||
local op74wa = {
|
||||
anaw=M.op(0x88,14),
|
||||
xraw=M.op(0x90,14),
|
||||
oraw=M.op(0x98,14),
|
||||
addncw=M.op(0xa0,14),
|
||||
gtaw=M.op(0xa8,14),
|
||||
subnbw=M.op(0xb0,14),
|
||||
ltaw=M.op(0xb8,14),
|
||||
addw=M.op(0xc0,14),
|
||||
onaw=M.op(0xc8,14),
|
||||
adcw=M.op(0xd0,14),
|
||||
offaw=M.op(0xd8,14),
|
||||
subw=M.op(0xe0,14),
|
||||
neaw=M.op(0xe8,14),
|
||||
sbbw=M.op(0xf0,14),
|
||||
eqaw=M.op(0xf8,14),
|
||||
} M.op74wa = op74wa
|
||||
for k,v in pairs(op74wa) do
|
||||
M[k .. 'wa'] = function(late, early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local size = function() late,early = M.size_op(late,early) return 3 end
|
||||
local bin = function() local l7801dbg=l7801dbg return { 0x74, v.opc, M.op_eval_byte(late,early) } end
|
||||
table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin })
|
||||
end
|
||||
end
|
||||
|
||||
local op70ind = {
|
||||
sspd=M.op(0x0e,20),
|
||||
lspd=M.op(0x0f,20),
|
||||
sbcd=M.op(0x1e,20),
|
||||
lbcd=M.op(0x1f,20),
|
||||
sded=M.op(0x2e,20),
|
||||
lded=M.op(0x2f,20),
|
||||
shld=M.op(0x3e,20),
|
||||
lhld=M.op(0x3f,20),
|
||||
} M.op70ind = op70ind
|
||||
for k,v in pairs(op70ind) do
|
||||
M[k] = function(late, early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local size = function() late,early = M.size_op(late,early) return 4 end
|
||||
local bin = function() local l7801dbg=l7801dbg
|
||||
local x = M.op_eval_word(late,early)
|
||||
return { 0x70, v.opc, x&0xff, x>>8 }
|
||||
end
|
||||
table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin })
|
||||
end
|
||||
end
|
||||
|
||||
local op70indr8 = {
|
||||
movindv=M.op(0x78,17),
|
||||
movinda=M.op(0x79,17),
|
||||
movindb=M.op(0x7a,17),
|
||||
movindc=M.op(0x7b,17),
|
||||
movindd=M.op(0x7c,17),
|
||||
movinde=M.op(0x7d,17),
|
||||
movindh=M.op(0x7e,17),
|
||||
movindl=M.op(0x7f,17),
|
||||
} M.op70indr8 = op70indr8
|
||||
for k,v in pairs(op70indr8) do
|
||||
M[k] = function(late, early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local size = function() late,early = M.size_op(late,early) return 4 end
|
||||
local bin = function() local l7801dbg=l7801dbg
|
||||
local x = M.op_eval_word(late,early)
|
||||
return { 0x70, v.opc, x&0xff, x>>8 }
|
||||
end
|
||||
table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin })
|
||||
end
|
||||
end
|
||||
|
||||
local op70r8ind = {
|
||||
movvind=M.op(0x68,17),
|
||||
movaind=M.op(0x69,17),
|
||||
movbind=M.op(0x6a,17),
|
||||
movcind=M.op(0x6b,17),
|
||||
movdind=M.op(0x6c,17),
|
||||
moveind=M.op(0x6d,17),
|
||||
movhind=M.op(0x6e,17),
|
||||
movlind=M.op(0x6f,17),
|
||||
} M.op70r8ind = op70r8ind
|
||||
for k,v in pairs(op70r8ind) do
|
||||
M[k] = function(late, early)
|
||||
local l7801dbg = { info=debug.getinfo(2, 'Sl'), trace=debug.traceback(nil, 1) }
|
||||
local size = function() late,early = M.size_op(late,early) return 4 end
|
||||
local bin = function() local l7801dbg=l7801dbg
|
||||
local x = M.op_eval_word(late,early)
|
||||
return { 0x70, v.opc, x&0xff, x>>8 }
|
||||
end
|
||||
table.insert(M.section_current.instructions, { size=size, cycles=v.cycles, bin=bin })
|
||||
end
|
||||
end
|
||||
|
||||
k = 0x89
|
||||
local op70names = {'anax','xrax','orax','addncx','gtax','subnbx','ltax','addx','onax','adcx','offax','subx','neax','sbbx','eqax'}
|
||||
local op70suffixes = {'bc','de','hl','ide','ihl','dde','dhl'}
|
||||
for i,o in ipairs(op70names) do
|
||||
for j,s in ipairs(op70suffixes) do
|
||||
local l = 0x00 + k + (j-1) + (8 * (i-1))
|
||||
M[o .. s] = function()
|
||||
table.insert(M.section_current.instructions, { size=2, cycles=11, bin={ 0x70, l } })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
4
vcs.l65
4
vcs.l65
|
@ -167,7 +167,7 @@ PAL = {
|
|||
}
|
||||
TV = PAL
|
||||
|
||||
init = function() cld ldx#0 txa local l=label() dex tsx pha bne l end
|
||||
init = function() cld ldx#0 txa local l=label() dex txs pha bne l end
|
||||
wait = function() local l=label() lda INTIM bne l end
|
||||
overscan_begin = function() sta WSYNC lda#2 sta VBLANK lda#TV.TIM_OVERSCAN sta TIM64T end
|
||||
overscan_end = wait
|
||||
|
@ -375,7 +375,7 @@ mappers.X07 = function()
|
|||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Image converters
|
||||
-- Data converters
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
image_scan = function(img, opt)
|
||||
|
|
|
@ -1,122 +1,122 @@
|
|||
" Vim indent file
|
||||
" Language: l65
|
||||
" URL: modified from https://github.com/tbastos/vim-lua
|
||||
|
||||
" Initialization ------------------------------------------{{{1
|
||||
|
||||
if exists("b:did_indent")
|
||||
finish
|
||||
endif
|
||||
let b:did_indent = 1
|
||||
|
||||
setlocal autoindent
|
||||
setlocal nosmartindent
|
||||
|
||||
setlocal indentexpr=GetL65Indent()
|
||||
setlocal indentkeys+=0=end,0=until,0=elseif,0=else
|
||||
|
||||
" Only define the function once.
|
||||
if exists("*GetL65Indent")
|
||||
finish
|
||||
endif
|
||||
|
||||
" Variables -----------------------------------------------{{{1
|
||||
|
||||
let s:open_patt = '\C\%(\<\%(function\|if\|repeat\|do\)\>\|(\|{\)'
|
||||
let s:middle_patt = '\C\<\%(else\|elseif\)\>'
|
||||
let s:close_patt = '\C\%(\<\%(end\|until\)\>\|)\|}\)'
|
||||
|
||||
let s:anon_func_start = '\S\+\s*[({].*\<function\s*(.*)\s*$'
|
||||
let s:anon_func_end = '\<end\%(\s*[)}]\)\+'
|
||||
|
||||
" Expression used to check whether we should skip a match with searchpair().
|
||||
let s:skip_expr = "synIDattr(synID(line('.'),col('.'),1),'name') =~# 'luaComment\\|luaString'"
|
||||
|
||||
" Auxiliary Functions -------------------------------------{{{1
|
||||
|
||||
function s:IsInCommentOrString(lnum, col)
|
||||
return synIDattr(synID(a:lnum, a:col, 1), 'name') =~# 'luaCommentLong\|luaStringLong'
|
||||
\ && !(getline(a:lnum) =~# '^\s*\%(--\)\?\[=*\[') " opening tag is not considered 'in'
|
||||
endfunction
|
||||
|
||||
" Find line above 'lnum' that isn't blank, in a comment or string.
|
||||
function s:PrevLineOfCode(lnum)
|
||||
let lnum = prevnonblank(a:lnum)
|
||||
while s:IsInCommentOrString(lnum, 1)
|
||||
let lnum = prevnonblank(lnum - 1)
|
||||
endwhile
|
||||
return lnum
|
||||
endfunction
|
||||
|
||||
" Gets line contents, excluding trailing comments.
|
||||
function s:GetContents(lnum)
|
||||
return substitute(getline(a:lnum), '\v\m--.*$', '', '')
|
||||
endfunction
|
||||
|
||||
" GetL65Indent Function -----------------------------------{{{1
|
||||
|
||||
function GetL65Indent()
|
||||
" if the line is in a long comment or string, don't change the indent
|
||||
if s:IsInCommentOrString(v:lnum, 1)
|
||||
return -1
|
||||
endif
|
||||
|
||||
let prev_line = s:PrevLineOfCode(v:lnum - 1)
|
||||
if prev_line == 0
|
||||
" this is the first non-empty line
|
||||
return 0
|
||||
endif
|
||||
|
||||
let contents_cur = s:GetContents(v:lnum)
|
||||
let contents_prev = s:GetContents(prev_line)
|
||||
|
||||
let original_cursor_pos = getpos(".")
|
||||
|
||||
let i = 0
|
||||
|
||||
" check if the previous line opens blocks
|
||||
call cursor(v:lnum, 1)
|
||||
let num_pairs = searchpair(s:open_patt, s:middle_patt, s:close_patt,
|
||||
\ 'mrb', s:skip_expr, prev_line)
|
||||
if num_pairs > 0
|
||||
let i += num_pairs
|
||||
endif
|
||||
|
||||
" special case: call(with, {anon = function() -- should indent only once
|
||||
if num_pairs > 1 && contents_prev =~# s:anon_func_start
|
||||
let i = 1
|
||||
endif
|
||||
|
||||
" check if current line closes blocks
|
||||
call cursor(prev_line, col([prev_line,'$']))
|
||||
let num_pairs = searchpair(s:open_patt, s:middle_patt, s:close_patt,
|
||||
\ 'mr', s:skip_expr, v:lnum)
|
||||
if num_pairs > 0
|
||||
let i -= num_pairs
|
||||
endif
|
||||
|
||||
" special case: end}) -- end of call with anon func should unindent once
|
||||
if num_pairs > 1 && contents_cur =~# s:anon_func_end
|
||||
let i = -1
|
||||
endif
|
||||
|
||||
" if the previous line closed a paren, unindent (except with anon funcs)
|
||||
call cursor(prev_line - 1, col([prev_line - 1, '$']))
|
||||
let num_pairs = searchpair('(', '', ')', 'mr', s:skip_expr, prev_line)
|
||||
if num_pairs > 0 && contents_prev !~ s:anon_func_end
|
||||
let i -= 1
|
||||
endif
|
||||
|
||||
" if this line closed a paren, indent (except with anon funcs)
|
||||
call cursor(prev_line, col([prev_line, '$']))
|
||||
let num_pairs = searchpair('(', '', ')', 'mr', s:skip_expr, v:lnum)
|
||||
if num_pairs > 0 && contents_cur !~ s:anon_func_end
|
||||
let i += 1
|
||||
endif
|
||||
|
||||
" restore cursor
|
||||
call setpos(".", original_cursor_pos)
|
||||
|
||||
return indent(prev_line) + (&sw * i)
|
||||
|
||||
endfunction
|
||||
" Vim indent file
|
||||
" Language: l65
|
||||
" URL: modified from https://github.com/tbastos/vim-lua
|
||||
|
||||
" Initialization ------------------------------------------{{{1
|
||||
|
||||
if exists("b:did_indent")
|
||||
finish
|
||||
endif
|
||||
let b:did_indent = 1
|
||||
|
||||
setlocal autoindent
|
||||
setlocal nosmartindent
|
||||
|
||||
setlocal indentexpr=GetL65Indent()
|
||||
setlocal indentkeys+=0=end,0=until,0=elseif,0=else
|
||||
|
||||
" Only define the function once.
|
||||
if exists("*GetL65Indent")
|
||||
finish
|
||||
endif
|
||||
|
||||
" Variables -----------------------------------------------{{{1
|
||||
|
||||
let s:open_patt = '\C\%(\<\%(function\|if\|repeat\|do\)\>\|(\|{\)'
|
||||
let s:middle_patt = '\C\<\%(else\|elseif\)\>'
|
||||
let s:close_patt = '\C\%(\<\%(end\|until\)\>\|)\|}\)'
|
||||
|
||||
let s:anon_func_start = '\S\+\s*[({].*\<function\s*(.*)\s*$'
|
||||
let s:anon_func_end = '\<end\%(\s*[)}]\)\+'
|
||||
|
||||
" Expression used to check whether we should skip a match with searchpair().
|
||||
let s:skip_expr = "synIDattr(synID(line('.'),col('.'),1),'name') =~# 'luaComment\\|luaString'"
|
||||
|
||||
" Auxiliary Functions -------------------------------------{{{1
|
||||
|
||||
function s:IsInCommentOrString(lnum, col)
|
||||
return synIDattr(synID(a:lnum, a:col, 1), 'name') =~# 'luaCommentLong\|luaStringLong'
|
||||
\ && !(getline(a:lnum) =~# '^\s*\%(--\)\?\[=*\[') " opening tag is not considered 'in'
|
||||
endfunction
|
||||
|
||||
" Find line above 'lnum' that isn't blank, in a comment or string.
|
||||
function s:PrevLineOfCode(lnum)
|
||||
let lnum = prevnonblank(a:lnum)
|
||||
while s:IsInCommentOrString(lnum, 1)
|
||||
let lnum = prevnonblank(lnum - 1)
|
||||
endwhile
|
||||
return lnum
|
||||
endfunction
|
||||
|
||||
" Gets line contents, excluding trailing comments.
|
||||
function s:GetContents(lnum)
|
||||
return substitute(getline(a:lnum), '\v\m--.*$', '', '')
|
||||
endfunction
|
||||
|
||||
" GetL65Indent Function -----------------------------------{{{1
|
||||
|
||||
function GetL65Indent()
|
||||
" if the line is in a long comment or string, don't change the indent
|
||||
if s:IsInCommentOrString(v:lnum, 1)
|
||||
return -1
|
||||
endif
|
||||
|
||||
let prev_line = s:PrevLineOfCode(v:lnum - 1)
|
||||
if prev_line == 0
|
||||
" this is the first non-empty line
|
||||
return 0
|
||||
endif
|
||||
|
||||
let contents_cur = s:GetContents(v:lnum)
|
||||
let contents_prev = s:GetContents(prev_line)
|
||||
|
||||
let original_cursor_pos = getpos(".")
|
||||
|
||||
let i = 0
|
||||
|
||||
" check if the previous line opens blocks
|
||||
call cursor(v:lnum, 1)
|
||||
let num_pairs = searchpair(s:open_patt, s:middle_patt, s:close_patt,
|
||||
\ 'mrb', s:skip_expr, prev_line)
|
||||
if num_pairs > 0
|
||||
let i += num_pairs
|
||||
endif
|
||||
|
||||
" special case: call(with, {anon = function() -- should indent only once
|
||||
if num_pairs > 1 && contents_prev =~# s:anon_func_start
|
||||
let i = 1
|
||||
endif
|
||||
|
||||
" check if current line closes blocks
|
||||
call cursor(prev_line, col([prev_line,'$']))
|
||||
let num_pairs = searchpair(s:open_patt, s:middle_patt, s:close_patt,
|
||||
\ 'mr', s:skip_expr, v:lnum)
|
||||
if num_pairs > 0
|
||||
let i -= num_pairs
|
||||
endif
|
||||
|
||||
" special case: end}) -- end of call with anon func should unindent once
|
||||
if num_pairs > 1 && contents_cur =~# s:anon_func_end
|
||||
let i = -1
|
||||
endif
|
||||
|
||||
" if the previous line closed a paren, unindent (except with anon funcs)
|
||||
call cursor(prev_line - 1, col([prev_line - 1, '$']))
|
||||
let num_pairs = searchpair('(', '', ')', 'mr', s:skip_expr, prev_line)
|
||||
if num_pairs > 0 && contents_prev !~ s:anon_func_end
|
||||
let i -= 1
|
||||
endif
|
||||
|
||||
" if this line closed a paren, indent (except with anon funcs)
|
||||
call cursor(prev_line, col([prev_line, '$']))
|
||||
let num_pairs = searchpair('(', '', ')', 'mr', s:skip_expr, v:lnum)
|
||||
if num_pairs > 0 && contents_cur !~ s:anon_func_end
|
||||
let i += 1
|
||||
endif
|
||||
|
||||
" restore cursor
|
||||
call setpos(".", original_cursor_pos)
|
||||
|
||||
return indent(prev_line) + (&sw * i)
|
||||
|
||||
endfunction
|
||||
|
|
1284
vim/syntax/l65.vim
1284
vim/syntax/l65.vim
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue