Skip to content

Commit

Permalink
refactor in anticipation of adding further output targets
Browse files Browse the repository at this point in the history
  • Loading branch information
yeastplume committed Feb 20, 2020
1 parent 4302332 commit b41e194
Show file tree
Hide file tree
Showing 12 changed files with 446 additions and 255 deletions.
78 changes: 58 additions & 20 deletions src/cmd/asm/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::iter::Iterator;

use crate::cmd::common::{self, GlobalArgs};
use crate::{Error, ErrorKind};
use vera::{AsmFormat, Assemblable, VeraBitmap, VeraSprite};
Expand All @@ -22,6 +24,34 @@ pub struct AsmArgs {
pub format: AsmFormat,
}

fn perform_assemble<T>(
values: &mut dyn Iterator<Item = &T>,
output_format: &AsmFormat,
out_dir: &str,
line_start: &mut usize,
) -> Result<(), Error>
where
T: Assemblable,
{
for v in values {
let code = v.assemble()?;
let asm_meta = code.assemble_meta(output_format.clone())?;
let meta_lc = asm_meta.line_count();
let asm_data = code.assemble_data(output_format.clone())?;
let data_lc = asm_data.line_count();
let mut output = asm_meta.to_string(Some(*line_start));
output = format!(
"{}{}",
output,
asm_data.to_string(Some(*line_start + meta_lc))
);
let file_name = format!("{}/{}.{}.inc", out_dir, v.id(), output_format);
common::output_to_file(&file_name, &output)?;
*line_start += meta_lc + data_lc;
}
Ok(())
}

/// Assemble
pub fn asm(g_args: &GlobalArgs, args: &AsmArgs) -> Result<(), Error> {
let proj = common::load_project(g_args.project_file.clone())?;
Expand All @@ -32,30 +62,37 @@ pub fn asm(g_args: &GlobalArgs, args: &AsmArgs) -> Result<(), Error> {
if !proj.palettes.is_empty() {
let pal_dir = format!("{}/palettes", args.out_dir);
common::create_dir(&pal_dir)?;
for p in proj.palettes.values() {
let asm = p.assemble(&args.format, &mut line_start)?;
common::output_to_file(&format!("{}/{}.{}.inc", pal_dir, p.id, args.format), &asm)?;
}
perform_assemble(
&mut proj.palettes.values(),
&args.format,
&pal_dir,
&mut line_start,
)?;
}
if !proj.imagesets.is_empty() {
let img_dir = format!("{}/imagesets", args.out_dir);
common::create_dir(&img_dir)?;
for i in proj.imagesets.values() {
let asm = i.assemble(&args.format, &mut line_start)?;
common::output_to_file(&format!("{}/{}.{}.inc", img_dir, i.id, args.format), &asm)?;
}
perform_assemble(
&mut proj.imagesets.values(),
&args.format,
&img_dir,
&mut line_start,
)?;
}
if !proj.tilemaps.is_empty() {
let tm_dir = format!("{}/tilemaps", args.out_dir);
common::create_dir(&tm_dir)?;
for i in proj.tilemaps.values() {
let asm = i.assemble(&args.format, &mut line_start)?;
common::output_to_file(&format!("{}/{}.{}.inc", tm_dir, i.id, args.format), &asm)?;
}
perform_assemble(
&mut proj.tilemaps.values(),
&args.format,
&tm_dir,
&mut line_start,
)?;
}
let mut sprites = vec![];
if !proj.sprites.is_empty() {
let tm_dir = format!("{}/sprites", args.out_dir);
common::create_dir(&tm_dir)?;
let sp_dir = format!("{}/sprites", args.out_dir);
common::create_dir(&sp_dir)?;
for s in proj.sprites.values() {
//Repopulate references
let imageset = match proj.imagesets.get(&s.imageset_id) {
Expand All @@ -69,13 +106,14 @@ pub fn asm(g_args: &GlobalArgs, args: &AsmArgs) -> Result<(), Error> {
}
};
let sprite = VeraSprite::init_from_imageset(&s.id, &imageset)?;
let asm = sprite.assemble(&args.format, &mut line_start)?;
common::output_to_file(&format!("{}/{}.{}.inc", tm_dir, s.id, args.format), &asm)?;
sprites.push(sprite);
}
perform_assemble(&mut sprites.iter(), &args.format, &sp_dir, &mut line_start)?;
}
let mut bitmaps = vec![];
if !proj.bitmaps.is_empty() {
let tm_dir = format!("{}/bitmaps", args.out_dir);
common::create_dir(&tm_dir)?;
let bm_dir = format!("{}/bitmaps", args.out_dir);
common::create_dir(&bm_dir)?;
for b in proj.bitmaps.values() {
//Repopulate references
let imageset = match proj.imagesets.get(&b.imageset_id) {
Expand All @@ -89,9 +127,9 @@ pub fn asm(g_args: &GlobalArgs, args: &AsmArgs) -> Result<(), Error> {
}
};
let bitmap = VeraBitmap::init_from_imageset(&b.id, &imageset)?;
let asm = bitmap.assemble(&args.format, &mut line_start)?;
common::output_to_file(&format!("{}/{}.{}.inc", tm_dir, b.id, args.format), &asm)?;
bitmaps.push(bitmap);
}
perform_assemble(&mut bitmaps.iter(), &args.format, &bm_dir, &mut line_start)?;
}

Ok(())
Expand Down
226 changes: 226 additions & 0 deletions vera/src/asm.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
// Copyright 2020 Revcore Technologies Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Holds intermediate assembly of Aloevera primitives,
//! which can then be given their final transformations
//! to target output

use crate::{Error, ErrorKind};
use std::fmt;
use std::str::FromStr;

/// Trait for object that outputs its data to an assembled format
pub trait Assemblable {
/// Assemble to raw byte data + meta data statements
fn assemble(&self) -> Result<AssembledPrimitive, Error>;

/// Return ID of assembled asset
fn id(&self) -> &str;
}

/// Enum for variations ToAsm implementors can assemble to
#[derive(Clone, Debug)]
pub enum AsmFormat {
/// To Basic
Basic,
/// ca65 assembly
Ca65,
}

impl fmt::Display for AsmFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let out = match self {
AsmFormat::Basic => "basic",
AsmFormat::Ca65 => "ca65",
};
write!(f, "{}", out)
}
}

/// From String
impl FromStr for AsmFormat {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"ca65" => Ok(AsmFormat::Ca65),
"basic" => Ok(AsmFormat::Basic),
other => Err(ErrorKind::UnknownAsmFormat(other.to_owned()).into()),
}
}
}

/// Holds and can format 'assembled' strings
#[derive(Clone, Debug)]
pub struct AssembledString {
/// Target format
target_format: AsmFormat,
/// to load
assembled_data: Vec<String>,
}

impl AssembledString {
/// New blank assembly
pub fn new(target_format: &AsmFormat) -> Self {
Self {
target_format: target_format.clone(),
assembled_data: vec![],
}
}

/// Add meta information
pub fn add(&mut self, new_meta: String) {
self.assembled_data.push(new_meta);
}

/// Output final string
pub fn to_string(self, line_start: Option<usize>) -> String {
let line_start = match line_start {
Some(v) => v,
None => 1,
};
let mut retval = String::from("");
for (i, l) in self.assembled_data.iter().enumerate() {
retval += &match self.target_format {
AsmFormat::Ca65 => format!("{}\n", l),
AsmFormat::Basic => format!("{} {}\n", line_start + i, l),
}
}
retval
}

/// number of lines
pub fn line_count(&self) -> usize {
self.assembled_data.len()
}
}

/// Holds raw assembled data, pre-formatting
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AssembledPrimitive {
/// Metadata about the struct, to be output
/// as comments, a meta file or even code
/// to load
meta: Vec<String>,
/// Assembled raw binary data
data: Vec<u8>,
}

impl AssembledPrimitive {
/// New blank assembly
pub fn new() -> Self {
Self {
meta: vec![],
data: vec![],
}
}

/// Add another prim to this one, consuming second
pub fn add_prim(&mut self, mut other: AssembledPrimitive) {
self.meta.append(&mut other.meta);
self.data.append(&mut other.data);
}

/// Add meta information
pub fn add_meta(&mut self, new_meta: String) {
self.meta.push(new_meta);
}

/// Add assembled byte data
pub fn add_data(&mut self, new_data: &[u8]) {
for b in new_data {
self.data.push(*b);
}
}

/// Output Meta, formatted for assembly target
pub fn assemble_meta(&self, out_format: AsmFormat) -> Result<AssembledString, Error> {
let mut retval = AssembledString::new(&out_format);
for m in self.meta.iter() {
retval.add(match out_format {
AsmFormat::Ca65 => format!(";{}", m),
AsmFormat::Basic => format!("REM {}", m.to_uppercase()),
});
}
Ok(retval)
}

/// Output data, formatted for assembly target
pub fn assemble_data(&self, out_format: AsmFormat) -> Result<AssembledString, Error> {
let mut retval = AssembledString::new(&out_format);
for i in (0..self.data.len()).step_by(8) {
let mut curval = String::from("");
curval += &match out_format {
AsmFormat::Ca65 => format!(".byte "),
AsmFormat::Basic => format!("DATA "),
};
for j in 0..8 {
let index = i + j;
if index == self.data.len() {
break;
}
curval += &match out_format {
AsmFormat::Ca65 => format!("${:02X}", self.data[index]),
AsmFormat::Basic => format!("{}", self.data[index]),
};
if j < 7 && index < self.data.len() - 1 {
curval += ",";
}
}
retval.add(curval);
}
Ok(retval)
}
}

#[test]
fn test_assemble() -> Result<(), Error> {
let mut prim = AssembledPrimitive::new();
prim.add_meta("here is some metadata 1".to_owned());
prim.add_meta("here is some metadata 2".to_owned());
prim.add_meta("here is some metadata 4".to_owned());
prim.add_data(&[16u8; 34]);

let mut line_count = 1;
let meta_strs = prim.assemble_meta(AsmFormat::Basic)?;
let num_lines = meta_strs.line_count();
let meta_str = meta_strs.to_string(Some(line_count));
line_count += num_lines;

let data_strs = prim.assemble_data(AsmFormat::Basic)?;
let data_str = data_strs.to_string(Some(line_count));

println!("Meta Basic");
println!("{}", meta_str);

println!("Data Basic");
println!("{}", data_str);
assert!(data_str.ends_with("8 DATA 16,16\n"));

let meta_strs = prim.assemble_meta(AsmFormat::Ca65)?;
let meta_str = meta_strs.to_string(None);

let data_strs = prim.assemble_data(AsmFormat::Ca65)?;
let data_str = data_strs.to_string(None);

println!("Meta Ca65");
println!("{}", meta_str);

println!("Data Ca65");
println!("{}", data_str);

assert!(data_str.starts_with(".byte $10,"));
assert!(data_str.ends_with(".byte $10,$10\n"));

Ok(())
}
Loading

0 comments on commit b41e194

Please sign in to comment.