| @@ -81,6 +81,7 @@ name = "ceres-asm" | |||||
| version = "0.1.0" | version = "0.1.0" | ||||
| dependencies = [ | dependencies = [ | ||||
| "logos 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", | "logos 0.11.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| "thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
| ] | ] | ||||
| [[package]] | [[package]] | ||||
| @@ -428,7 +429,7 @@ version = "0.1.0" | |||||
| dependencies = [ | dependencies = [ | ||||
| "image 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", | "image 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| "minifb 0.16.0 (git+https://github.com/emoon/rust_minifb)", | "minifb 0.16.0 (git+https://github.com/emoon/rust_minifb)", | ||||
| "thiserror 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
| "thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
| ] | ] | ||||
| [[package]] | [[package]] | ||||
| @@ -829,15 +830,15 @@ dependencies = [ | |||||
| [[package]] | [[package]] | ||||
| name = "thiserror" | name = "thiserror" | ||||
| version = "1.0.19" | |||||
| version = "1.0.20" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | dependencies = [ | ||||
| "thiserror-impl 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
| "thiserror-impl 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
| ] | ] | ||||
| [[package]] | [[package]] | ||||
| name = "thiserror-impl" | name = "thiserror-impl" | ||||
| version = "1.0.19" | |||||
| version = "1.0.20" | |||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | dependencies = [ | ||||
| "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", | "proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| @@ -1109,8 +1110,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" | |||||
| "checksum syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" | "checksum syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a" | ||||
| "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" | "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" | ||||
| "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" | "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" | ||||
| "checksum thiserror 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "b13f926965ad00595dd129fa12823b04bbf866e9085ab0a5f2b05b850fbfc344" | |||||
| "checksum thiserror-impl 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479" | |||||
| "checksum thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" | |||||
| "checksum thiserror-impl 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" | |||||
| "checksum tiff 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f3b8a87c4da944c3f27e5943289171ac71a6150a79ff6bacfff06d159dfff2f" | "checksum tiff 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f3b8a87c4da944c3f27e5943289171ac71a6150a79ff6bacfff06d159dfff2f" | ||||
| "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" | "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" | ||||
| "checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" | "checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" | ||||
| @@ -2,6 +2,7 @@ | |||||
| a shitty fantasy console written in rust using a proprietary MIPS based asm instruction set. a lot of inspiration from PICO-8. ceres is structured of these crates: | a shitty fantasy console written in rust using a proprietary MIPS based asm instruction set. a lot of inspiration from PICO-8. ceres is structured of these crates: | ||||
| - ceres-sys: the core system structure of ceres-16 | - ceres-sys: the core system structure of ceres-16 | ||||
| - ceres-asm: the assembler for ceres-16 | |||||
| ### Graphics | ### Graphics | ||||
| @@ -24,4 +25,18 @@ all registers are unsigned 16 bit | |||||
| ### Memory map and info | ### Memory map and info | ||||
| god oh fuck what am i even doing | |||||
| god oh fuck what am i even doing | |||||
| ### instructions | |||||
| ##### load - ld:signifier | |||||
| can be vram/cram/imed | |||||
| - immediate `ld:immd $dest immediate` | |||||
| | opcode | signifier | destination | padding | immediate | | |||||
| | ------- | --------- | ----------- | ------- | ------------------ | | |||||
| | `00001` | `100` | `0000` | `0000` | `0000000000000000` | | |||||
| @@ -8,3 +8,4 @@ edition = "2018" | |||||
| [dependencies] | [dependencies] | ||||
| logos = "0.11.4" | logos = "0.11.4" | ||||
| thiserror = "1.0.20" | |||||
| @@ -1,14 +1,107 @@ | |||||
| use logos::Logos; | |||||
| // izzy do not forget to remove these later dummy | |||||
| #![allow(dead_code)] | |||||
| #![allow(unused_imports)] | |||||
| #![allow(unused_variables)] | |||||
| // namespacing | |||||
| use logos::{Lexer, Logos}; | |||||
| /// ceres-asm result type | |||||
| pub type Result<T> = std::result::Result<T, CeresAsmError>; | |||||
| /// ceres-asm error type | |||||
| #[derive(Debug, thiserror::Error)] | |||||
| pub enum CeresAsmError { | |||||
| #[error("wrong token found:\nexpected: {expected:?}\n found: {found:?}")] | |||||
| BadToken { expected: Token, found: Token }, | |||||
| #[error("bad token: {token:?}")] | |||||
| LazyBadToken { token: Token }, | |||||
| #[error("was looking for token, found nothing ???")] | |||||
| NoToken, | |||||
| } | |||||
| /// assembler struct | |||||
| pub struct Assembler<'a> { | |||||
| lexer: Lexer<'a, Token>, | |||||
| } | |||||
| impl<'a> Assembler<'a> { | |||||
| /// create a new assembler | |||||
| pub fn new(data: &'a str) -> Assembler<'a> { | |||||
| let lexer = Token::lexer(data); | |||||
| Assembler { lexer } | |||||
| } | |||||
| /// assemble | |||||
| pub fn assemble(&mut self) -> Result<Vec<u32>> { | |||||
| let mut machine_code: Vec<u32> = Vec::new(); | |||||
| while let Some(token) = self.lexer.next() { | |||||
| match token { | |||||
| // cases that should continue | |||||
| Token::Comment => continue, | |||||
| Token::Newline => continue, | |||||
| // cases that like, actually make sense to process | |||||
| Token::Load => { | |||||
| if let Some(new_token) = self.lexer.next() { | |||||
| let signifier = Signifier::from_token(new_token)?; | |||||
| machine_code.push(self.load(signifier)?); | |||||
| } else { | |||||
| return Err(CeresAsmError::NoToken); | |||||
| } | |||||
| } | |||||
| // cases that should straight up not happen | |||||
| _ => return Err(CeresAsmError::LazyBadToken { token }), | |||||
| } | |||||
| } | |||||
| Ok(machine_code) | |||||
| } | |||||
| // load instruction assembly | |||||
| fn load(&mut self, signifier: Signifier) -> Result<u32> { | |||||
| let opcode: u32 = 0b00001; | |||||
| let signifier: u32 = signifier.as_bits() as u32; | |||||
| let instruction: u32 = (opcode << 27) | (signifier << 24); | |||||
| Ok(instruction) | |||||
| } | |||||
| } | |||||
| enum Signifier { | |||||
| VideoMemory, | |||||
| CartMemory, | |||||
| Immediate, | |||||
| } | |||||
| impl Signifier { | |||||
| fn from_token(token: Token) -> Result<Self> { | |||||
| match token { | |||||
| Token::VideoMemory => Ok(Self::VideoMemory), | |||||
| Token::CartMemory => Ok(Self::CartMemory), | |||||
| Token::Immediate => Ok(Self::Immediate), | |||||
| _ => Err(CeresAsmError::LazyBadToken { token }), | |||||
| } | |||||
| } | |||||
| // returns the signifier as bits stored in a u8 | |||||
| fn as_bits(self) -> u8 { | |||||
| match self { | |||||
| Self::VideoMemory => 0b010, | |||||
| Self::CartMemory => 0b001, | |||||
| Self::Immediate => 0b100, | |||||
| } | |||||
| } | |||||
| } | |||||
| /// token | |||||
| #[derive(Logos, Debug, PartialEq)] | #[derive(Logos, Debug, PartialEq)] | ||||
| pub enum Token { | pub enum Token { | ||||
| // general stuff | // general stuff | ||||
| #[regex(";.+")] | #[regex(";.+")] | ||||
| Comment, | Comment, | ||||
| #[token("@")] | |||||
| At, | |||||
| #[regex("/[a-z-_]+:/g")] | |||||
| Label, | |||||
| #[token("\n")] | |||||
| Newline, | |||||
| // registers | // registers | ||||
| #[token("$t0")] | #[token("$t0")] | ||||
| ZeroRegister, | ZeroRegister, | ||||
| @@ -25,13 +118,21 @@ pub enum Token { | |||||
| #[token("$a2")] | #[token("$a2")] | ||||
| ArgumentTwo, | ArgumentTwo, | ||||
| #[token("$v0")] | #[token("$v0")] | ||||
| ReturnOne, | |||||
| ReturnZero, | |||||
| #[token("$v1")] | #[token("$v1")] | ||||
| ReturnTwo, | |||||
| #[regex("\\$t[0-6]")] | |||||
| TemoraryRegister, | |||||
| #[regex("\\$[0-9]+")] | |||||
| RegisterIndex, | |||||
| ReturnOne, | |||||
| #[regex("\\$t[0-6]", |lex| lex.slice()[2..].parse::<u16>())] | |||||
| TemporaryRegister(u16), | |||||
| #[regex("\\$[0-9]+", |lex| lex.slice()[1..].parse::<u16>())] | |||||
| RegisterIndex(u16), | |||||
| // types | |||||
| #[token("immd")] | |||||
| Immediate, | |||||
| #[token("vram")] | |||||
| VideoMemory, | |||||
| #[token("cram")] | |||||
| CartMemory, | |||||
| // literals | // literals | ||||
| #[regex("0x[a-fA-F0-9]+", |lex| u16::from_str_radix(&lex.slice()[2..], 16).unwrap() )] | #[regex("0x[a-fA-F0-9]+", |lex| u16::from_str_radix(&lex.slice()[2..], 16).unwrap() )] | ||||
| @@ -42,18 +143,80 @@ pub enum Token { | |||||
| DecimalLiteral(u16), | DecimalLiteral(u16), | ||||
| // instructions | // instructions | ||||
| #[token("add")] | |||||
| Add, | |||||
| #[token("sub")] | |||||
| Sub, | |||||
| #[token("mul")] | |||||
| Mul, | |||||
| #[token("div")] | |||||
| Div, | |||||
| #[token("jmp")] | |||||
| Jmp, | |||||
| #[token("ld:")] | |||||
| Load, | |||||
| // logos error | // logos error | ||||
| #[error] | #[error] | ||||
| #[regex(" +", logos::skip)] | |||||
| Error, | Error, | ||||
| } | } | ||||
| impl Token { | |||||
| // returns the token as a str | |||||
| fn as_str(&self) -> String { | |||||
| match self { | |||||
| // general stuff | |||||
| Self::Comment => "a comment".to_owned(), | |||||
| Self::Newline => "\\n".to_owned(), | |||||
| // registers | |||||
| Self::ZeroRegister => "$z0".to_owned(), | |||||
| Self::ProgramCounter => "$pc".to_owned(), | |||||
| Self::StackPointer => "$sp".to_owned(), | |||||
| Self::ReturnAddress => "$ra".to_owned(), | |||||
| Self::ArgumentZero => "$a0".to_owned(), | |||||
| Self::ArgumentOne => "$z1".to_owned(), | |||||
| Self::ArgumentTwo => "$a2".to_owned(), | |||||
| Self::ReturnZero => "$v0".to_owned(), | |||||
| Self::ReturnOne => "$v1".to_owned(), | |||||
| Self::TemporaryRegister(idx) => format!("$t{}", idx), | |||||
| Self::RegisterIndex(idx) => format!("${}", idx), | |||||
| // types | |||||
| Self::Immediate => "immd".to_owned(), | |||||
| Self::VideoMemory => "vram".to_owned(), | |||||
| Self::CartMemory => "cram".to_owned(), | |||||
| // literals | |||||
| Self::HexLiteral(_) => "hex lit".to_owned(), | |||||
| Self::BinaryLiteral(_) => "bin lit".to_owned(), | |||||
| Self::DecimalLiteral(_) => "dec lit".to_owned(), | |||||
| // instructions | |||||
| Self::Load => "ld:".to_owned(), | |||||
| // errors | |||||
| Self::Error => "ERR".to_owned(), | |||||
| } | |||||
| } | |||||
| // returns true if given token is a register | |||||
| fn is_register(&self) -> bool { | |||||
| match self { | |||||
| Self::ZeroRegister | |||||
| | Self::ProgramCounter | |||||
| | Self::StackPointer | |||||
| | Self::ReturnAddress | |||||
| | Self::ArgumentZero | |||||
| | Self::ArgumentOne | |||||
| | Self::ArgumentTwo | |||||
| | Self::ReturnZero | |||||
| | Self::ReturnOne | |||||
| | Self::TemporaryRegister(_) | |||||
| | Self::RegisterIndex(_) => true, | |||||
| _ => false, | |||||
| } | |||||
| } | |||||
| // returns true if given token is a literal | |||||
| fn is_literal(&self) -> bool { | |||||
| match self { | |||||
| Self::HexLiteral(_) | Self::BinaryLiteral(_) | Self::DecimalLiteral(_) => true, | |||||
| _ => false, | |||||
| } | |||||
| } | |||||
| // returns true if it's a valid memory location or immediate | |||||
| fn is_signifier(&self) -> bool { | |||||
| match self { | |||||
| Self::VideoMemory | Self::CartMemory | Self::Immediate => true, | |||||
| _ => false, | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -8,6 +8,16 @@ impl VideoMemory { | |||||
| pub fn init() -> VideoMemory { | pub fn init() -> VideoMemory { | ||||
| VideoMemory { data: [0x0000; crate::VIDEO_MEMORY_LEN] } | VideoMemory { data: [0x0000; crate::VIDEO_MEMORY_LEN] } | ||||
| } | } | ||||
| /// an iterator | |||||
| pub fn iter(&self) -> impl Iterator<Item = &u16> { | |||||
| self.data.iter() | |||||
| } | |||||
| /// mutable iterator | |||||
| pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut u16> { | |||||
| self.data.iter_mut() | |||||
| } | |||||
| } | } | ||||
| impl std::ops::Index<u16> for VideoMemory { | impl std::ops::Index<u16> for VideoMemory { | ||||
| @@ -0,0 +1,10 @@ | |||||
| alias r := run | |||||
| alias b := build | |||||
| build: | |||||
| clear | |||||
| cargo build | |||||
| run: | |||||
| clear | |||||
| cargo run | |||||
| @@ -6,12 +6,18 @@ use structopt::StructOpt; | |||||
| struct Opt { | struct Opt { | ||||
| /// times the screen should be scaled by | /// times the screen should be scaled by | ||||
| #[structopt(short = "s", long = "scale", default_value = "4")] | #[structopt(short = "s", long = "scale", default_value = "4")] | ||||
| scale_factor: usize, | |||||
| _scale_factor: usize, | |||||
| } | } | ||||
| fn wrapper() -> Result<()> { | fn wrapper() -> Result<()> { | ||||
| let opt = Opt::from_args(); | |||||
| // load options | |||||
| let _opt = Opt::from_args(); | |||||
| // initialize the system | |||||
| let _sys = ceres_sys::System::init(); | |||||
| /* | |||||
| // create a new graphics context | |||||
| let mut ctx = offbrand::Context::new( | let mut ctx = offbrand::Context::new( | ||||
| ceres_sys::SCREEN_WIDTH, | ceres_sys::SCREEN_WIDTH, | ||||
| ceres_sys::SCREEN_HEIGHT, | ceres_sys::SCREEN_HEIGHT, | ||||
| @@ -19,25 +25,12 @@ fn wrapper() -> Result<()> { | |||||
| Some(opt.scale_factor), | Some(opt.scale_factor), | ||||
| )?; | )?; | ||||
| let mut sys = ceres_sys::System::init(); | |||||
| let mut colors: Vec<u16> = Vec::new(); | |||||
| for r in 0x0..0xf { | |||||
| for g in 0x0..0xf { | |||||
| for b in 0x0..0xf { | |||||
| colors.push((r << 8) | (g << 4) | b); | |||||
| } | |||||
| } | |||||
| } | |||||
| colors.iter().enumerate().for_each(|(addr, color)| { | |||||
| sys.video_memory[addr as u16] = *color; | |||||
| }); | |||||
| // loop while the context window is open | |||||
| while ctx.is_open() { | while ctx.is_open() { | ||||
| // clear the context | |||||
| ctx.clear(None); | ctx.clear(None); | ||||
| // print a pixel for ever word in the video memory buffer | |||||
| for x in 0..ceres_sys::SCREEN_WIDTH { | for x in 0..ceres_sys::SCREEN_WIDTH { | ||||
| for y in 0..ceres_sys::SCREEN_HEIGHT { | for y in 0..ceres_sys::SCREEN_HEIGHT { | ||||
| let color = | let color = | ||||
| @@ -46,19 +39,14 @@ fn wrapper() -> Result<()> { | |||||
| } | } | ||||
| } | } | ||||
| // render the context | |||||
| ctx.present()?; | ctx.present()?; | ||||
| } | } | ||||
| */ | |||||
| Ok(()) | Ok(()) | ||||
| } | } | ||||
| fn color_from_u16(color: u16) -> Color { | |||||
| let r = (((color >> 8) & 0x000f) as u8) * 17; | |||||
| let g = (((color >> 4) & 0x000f) as u8) * 17; | |||||
| let b = ((color & 0x000f) as u8) * 17; | |||||
| Color { r, g, b } | |||||
| } | |||||
| fn main() { | fn main() { | ||||
| if let Err(err) = wrapper() { | if let Err(err) = wrapper() { | ||||
| eprintln!("error: {:?}", err); | eprintln!("error: {:?}", err); | ||||
| @@ -1,5 +1,2 @@ | |||||
| ; a simple test application | |||||
| main: | |||||
| load $t0 0xcccc | |||||
| load $t1 0xaaaa | |||||
| sub $t0 $t1 $t3 | |||||
| ; a simple test | |||||
| ld:immd $t0 0x5a5a | |||||