Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Tree: 874be190bd
Fetching contributors…

Cannot retrieve contributors at this time

executable file 278 lines (252 sloc) 8.484 kB
#!/usr/bin/env coffee
fibers = require 'fibers'
fs = require 'fs'
nopt = require 'nopt'
sync = require 'sync'
util = require 'util'
sprintf = require("sprintf").sprintf
Assembler = require('../src/d16bunny').Assembler
version = "2.0.0"
header =
"""
d16bunny /)/)
VVVVV ('.'=)
("("_)o
"""
header = header.replace(/VVVVV/, version)
headerColors =
"""
4444444443333333
4444444443636333
4444444443333333
"""
# KROYGBVW
colorChart = [ "30", "31", "33", "1;33", "32", "34", "35", "37" ]
color = (c, text) -> "\u001b[0;" + colorChart[c] + "m" + text + "\u001b[0m"
red = (text) -> color(1, text)
zip = (lists...) ->
len = Math.min((x.length for x in lists)...)
out = []
for i in [0...len] then out.push(x[i] for x in lists)
out
# return [ folder, filename, ext ]
parseFilename = (path) ->
segments = path.split("/")
filename = segments[segments.length - 1]
n = filename.lastIndexOf('.')
basename = if n > 0 then filename.slice(0, n) else filename
ext = if n > 0 then filename.slice(n + 1) else ""
if basename == "-" then basename = "out"
folder = if segments.length > 1
segments.slice(0, segments.length - 1).join("/")
else
"."
[ folder, basename, ext ]
banner = ->
console.log ""
for [ line, colormap ] in zip(header.split("\n"), headerColors.split("\n"))
currentColor = 7
out = for [ ch, c ] in zip(line, colormap)
if c != currentColor
currentColor = c
"\u001b[0;" + colorChart[c] + "m" + ch
else
ch
console.log(out.join("") + "\u001b[0m")
console.log ""
help = ->
banner()
console.log "usage: d16basm [options] <filenames...>"
console.log "options:"
console.log " --quiet (-q)"
console.log " don't write anything to stdout unless there's an error"
console.log " --debug (-D)"
console.log " write *intensive* debugging output about the assembler to stdout"
console.log " --dest FOLDER"
console.log " write output files into a specific folder"
console.log " --out FILENAME"
console.log " write output file into a specific (single) file"
console.log " --max-errors N"
console.log " stop trying to compile after N errors"
console.log " --org ADDR"
console.log " start compiling into ADDR instead of starting at 0"
console.log " --symtab"
console.log " dump a list of defined symbols, in lexical and value order"
console.log ""
console.log "options for emitting alternate formats (default is a memory image):"
console.log " --little-endian"
console.log " write memory image in little-endian format (default is network order)"
console.log " --dat"
console.log " create generic DCPU assembly made out of DAT statements (useful for"
console.log " copying into less intelligent assemblers/emulators)"
console.log " --simple"
console.log " create simplified DCPU assembly with only notchian commands, labels,"
console.log " and DAT commands, for maximum portability"
exit 0
hex = (n) -> sprintf("0x%04x", n)
parseIntArg = (s) ->
if s[0...2] == "0x"
parseInt(s[2...], 16)
else
parseInt(s)
streamToString = (stream) ->
out = ""
stream.setEncoding("UTF-8")
stream.on 'data', (data) -> out += data
fiber = fibers.current
stream.on 'end', -> fiber.run()
stream.resume()
fibers.yield()
out
logError = (text) ->
console.error(red("error: ") + text)
# may need to adjust pos because of literal tabs in the text :(
correctedPos = (text, pos) ->
cpos = 0
for i in [0...pos]
if text[i] == '\t'
cpos += 8 - (cpos % 8)
else
cpos++
cpos
logPosition = (text, pos) ->
cpos = correctedPos(text, pos)
spacer = if pos == 0 then "" else (" " for i in [0...cpos]).join("")
left = text.slice(0, pos)
trouble = text.slice(pos, pos + 1)
right = text.slice(pos + 1)
console.error(left + red(trouble) + right)
console.error(spacer + red("^"))
exit = (code) ->
unless options.quiet? and code == 0 then console.log ""
process.exit(code)
readFile = (filename) ->
try
if filename == "-"
streamToString(process.stdin)
else
fs.readFileSync(filename, "UTF-8")
catch e
logError("Can't read file: " + filename + " - " + e)
exit(1)
assemble = (filename, folder) ->
files = {}
files[filename] = readFile(filename).split("\n")
logger = (fname, y, x, message) ->
logError("#{fname} (#{y + 1}) - #{message}")
if x >= 0 then logPosition(files[fname][y], x)
asm = new Assembler(logger, maxErrors)
asm.includer = (filename) =>
unless options.quiet? then console.log "+ Including #{filename}"
if files[filename] then return files[filename]
fullpath = if ("/" in filename) then filename else "#{folder}/#{filename}"
files[filename] = readFile(fullpath).split("\n")
files[filename]
if options.debug? then asm.debugger = console.log
asm.compile(files[filename], org, filename)
outputDat = (output) ->
out = []
out.push "; generated by d16bunny #{version}"
out.push ""
for block in output.pack()
out.push " ORG #{hex(block.address)}"
i = 0
while i < block.data.length
data = (hex(x) for x in block.data.slice(i, i + 8))
out.push " DAT #{data.join(', ')}"
i += data.length
out.push ""
out.join("\n")
outputSimple = (output) ->
out = []
out.push "; generated by d16bunny #{version}"
out.push ""
out = out.concat(output.disassemble())
out.join("\n")
outputImage = (output) ->
memory = output.createImage()
buffer = new Buffer(0x20004)
for i in [0...0x10000]
if memory[i] > 0xffff then console.log "GODDAMMIT: #{hex(i)} - #{hex(memory[i])}"
if options["little-endian"]?
buffer[i * 2] = memory[i] & 0xff
buffer[i * 2 + 1] = (memory[i] >> 8) & 0xff
else
buffer[i * 2] = (memory[i] >> 8) & 0xff
buffer[i * 2 + 1] = memory[i] & 0xff
# add byte order indicator
if options["little-endian"]?
buffer[0x20000] = 0xff
buffer[0x20001] = 0xfe
else
buffer[0x20000] = 0xfe
buffer[0x20001] = 0xff
# header to identify a memory image
buffer[0x20002] = 0x10
buffer[0x20003] = 0x16
buffer
symtab = (output) ->
console.log ""
list = ([ k, v ] for k, v of output.symtab)
console.log "Symbols (by name):"
list.sort((a, b) -> if a[0] > b[0] then 1 else -1)
for [ k, v ] in list
unless k == "." then console.log(" #{hex(v)} #{k}")
console.log ""
console.log "Symbols (by value):"
list.sort((a, b) -> if a[1] > b[1] then 1 else -1)
for [ k, v ] in list
unless k == "." then console.log(" #{hex(v)} #{k}")
console.log ""
possibleOptions =
"dat": Boolean
"simple": Boolean
"debug": Boolean
"quiet": Boolean
"symtab": Boolean
"max-errors": Number
"org": String
"dest": String
"out": String
"little-endian": Boolean
shortHands =
"D": "--debug"
"q": "--quiet"
options = nopt possibleOptions
filenames = options.argv.remain
if options.help? then help()
if filenames.length == 0 then filenames = [ "-" ]
unless options.quiet? then banner()
maxErrors = if options["max-errors"]? then options["max-errors"] else 10
org = if options.org? then parseIntArg(options.org) else 0
extension = if options.dat? then "-dat.dasm" else (if options.simple? then "-simple.dasm" else ".d16")
sync ->
success = true
tstart = Date.now()
for filename in filenames
[ folder, basename, oldext ] = parseFilename(filename)
unless options.quiet? then console.log "Assembling #{basename}"
out = assemble(filename, folder)
if out.errors.length > 0
console.error(red("Errors: #{out.errors.length}"))
console.error("")
success = false
else
unless options.quiet?
for block in out.pack()
console.log " #{hex(block.address)} - #{hex(block.address + block.data.length - 1)}"
if options.symtab? then symtab(out)
newFilename = if options.out?
options.out
else
(if options.dest? then options.dest else folder) + "/#{basename}#{extension}"
unless options.quiet? then console.log "Writing #{newFilename}"
if options.dat?
fs.writeFileSync(newFilename, outputDat(out), "UTF-8")
else if options.simple?
fs.writeFileSync(newFilename, outputSimple(out), "UTF-8")
else
fs.writeFileSync(newFilename, outputImage(out))
tend = Date.now()
unless options.quiet? then console.log "Finished in #{tend - tstart} msec."
exit(if success then 0 else 1)
Jump to Line
Something went wrong with that request. Please try again.