Brass 3

Published October 02, 2007
Advertisement
Quake isn't dead, but I've shifted my concentration to trying to get Brass 3 (the assembler project) out.



Brass 2 didn't really work, but I've taken a lot of its ideas - namely the plugin system - and kept some of the simplicity from Brass 1. The result works, and is easy to extend and maintain. Last night I got it to compile all of the programs I used for testing Brass 1 against TASM successfully.

I'm taking advantage of .NET's excellent reflection capabilities; one such example is marking plugin functions with attributes for documentation purposes, meaning that all you need to get Brass documentation is to drop your plugin collection assemblies (DLLs) into the Brass directory then open the help viewer app.



The source code examples are embedded as text, but compiled by the viewer (and thus syntax-highlighted) so you can click on directives or functions and it'll jump to their definitions automatically.

Native function support and a much-improved parser means that complex control structures can be built up, like:

file = fopen("somefile.txt")#while !feof(file)    .db fread(file)#loopfclose(file)


The compiler invokes the plugins, and the plugins talk back to the compiler ("remember your current position", "OK, we need to loop, so go back to this position", "this loop fails, so switch yourself off until you hit the #loop directive again").

The compiler natively works with project files (rather than some horrible command-line syntax) which specify which plugins to load, which include directories to search and so on and so forth. There are a number of different plugin classes:
  • IAssembler - CPU-specific assembler.
  • IDirective - assembler directive.
  • IFunction - functions like abs() or fopen().
  • IOutputWriter - writes the object file to disk (eg raw, intel hex, TI-83+ .8xp).
  • IOutputModifier - modifies each output byte (eg "unsquishing" bytes to two ASCII charcters for the TI-83).
  • IStringEncoder - handles the conversion of strings to byte[] arrays (ascii, utf8, arcane mappings for strange OS).

Unlike Brass 2, though, I actually have working output from this, so hopefully it'll get released!

As a bonus, to compare outputs between this and TASM (to check it was assembling properly) I hacked together a binary diff tool from the algorithm on Wikipedia (with the recursion removed) - it's not great, but it's been useful to me. [smile]

using System;using System.Collections.Generic;using System.Text;using System.IO;namespace Differ {	class Program {		static void Main(string[] args) {			// Prompt syntax:			if (args.Length != 2) {				Console.WriteLine("Usage: Differ  ");				return;			}			// Load both files into byte arrays (sloppy, but whatever).			byte[][] Data = new byte[2][];			for (int i = 0; i < Data.Length; ++i) {				try {					byte[] Source = File.ReadAllBytes(args);					Data = new byte[Source.Length + 1];					Array.Copy(Source, 0, Data, 1, Source.Length);				} catch (Exception ex) {					Console.WriteLine("File load error: " + args + " (" + ex.Message + ")");					return;				}			}			// Quick-and-dirty equality test:			if (Data[0].Length == Data[1].Length) {				bool IsIdentical = true;				for (int i = 0; i < Data[0].Length; ++i) {					if (Data[0] != Data[1]) {						IsIdentical = false;						break;					}				}				if (IsIdentical) {					Console.WriteLine("Files are identical.");					return;				}			}			if (Data[0].Length != Data[1].Length) {				Console.WriteLine("Files are different sizes.");			}			// Analysis:			int[,] C = new int[Data[0].Length, Data[1].Length];			for (int i = 1; i < Data[0].Length; ++i) {				if ((i - 1) % 1000 == 0) Console.Write("\rAnalysing: {0:P}...", (float)i / (float)Data[0].Length);				for (int j = 1; j < Data[1].Length; ++j) {					if (Data[0] == Data[1][j]) {						C[i, j] = C + <span class="cpp-literal"><span class="cpp-number">1</span></span>;<br>					} <span class="cpp-keyword">else</span> {<br>						C[i, j] = Math.Max(C[i, j - <span class="cpp-literal"><span class="cpp-number">1</span></span>], C);<br>					}<br>				}<br>			}<br>			Console.WriteLine(<span class="cpp-literal">"\rResults:"</span>.PadRight(Console.BufferWidth - <span class="cpp-literal"><span class="cpp-number">1</span></span>));<br><br>			List<DiffData> CollectedDiffData = <span class="vb-function">new</span> List<DiffData>(Math.Max(Data[<span class="cpp-literal"><span class="cpp-number">0</span></span>].Length, Data[<span class="cpp-literal"><span class="cpp-number">1</span></span>].Length));<br>			<br>			<span class="cpp-keyword">for</span> (<span class="cpp-keyword">int</span> i = Data[<span class="cpp-literal"><span class="cpp-number">0</span></span>].Length - <span class="cpp-literal"><span class="cpp-number">1</span></span>, j = Data[<span class="cpp-literal"><span class="cpp-number">1</span></span>].Length - <span class="cpp-literal"><span class="cpp-number">1</span></span>; ; ) {<br>				<span class="cpp-keyword">if</span> (i > <span class="cpp-literal"><span class="cpp-number">0</span></span> && j > <span class="cpp-literal"><span class="cpp-number">0</span></span> && Data[<span class="cpp-literal"><span class="cpp-number">0</span></span>]<span style="font-weight:bold;"> == Data[<span class="cpp-literal"><span class="cpp-number">1</span></span>][j]) {<br>					CollectedDiffData.Add(<span class="vb-function">new</span> DiffData(DiffData.DiffType.NoChange, Data[<span class="cpp-literal"><span class="cpp-number">0</span></span>]<span style="font-weight:bold;">, i, j));<br>					–i; –j;<br>				} <span class="cpp-keyword">else</span> {<br>					<span class="cpp-keyword">if</span> (j > <span class="cpp-literal"><span class="cpp-number">0</span></span> && (i == <span class="cpp-literal"><span class="cpp-number">0</span></span> || C[i, j - <span class="cpp-literal"><span class="cpp-number">1</span></span>] >= C)) {<br>						CollectedDiffData.Add(<span class="vb-function">new</span> DiffData(DiffData.DiffType.Addition, Data[<span class="cpp-literal"><span class="cpp-number">1</span></span>][j], i, j));<br>						–j;<br>					} <span class="cpp-keyword">else</span> <span class="cpp-keyword">if</span> (i > <span class="cpp-literal"><span class="cpp-number">0</span></span> && (j == <span class="cpp-literal"><span class="cpp-number">0</span></span> || C[i, j - <span class="cpp-literal"><span class="cpp-number">1</span></span>] < C)) {<br>						CollectedDiffData.Add(<span class="vb-function">new</span> DiffData(DiffData.DiffType.Removal, Data[<span class="cpp-literal"><span class="cpp-number">0</span></span>]<span style="font-weight:bold;">, i, j));<br>						–i;<br>					} <span class="cpp-keyword">else</span> {<br>						CollectedDiffData.Reverse();<br>						<span class="cpp-keyword">break</span>; <span class="cpp-comment">// Done!</span><br>					}<br>				}<br>			}<br><br><br>			DiffData.DiffType LastType = (DiffData.DiffType)(-<span class="cpp-literal"><span class="cpp-number">1</span></span>);<br><br>			<span class="cpp-keyword">int</span> PrintedData = <span class="cpp-literal"><span class="cpp-number">0</span></span>;<br>			<span class="cpp-keyword">foreach</span> (DiffData D <span class="cpp-keyword">in</span> CollectedDiffData) {<br>				<span class="cpp-keyword">if</span> (LastType != D.Type) {<br>					Console.WriteLine();<br>					Console.Write(<span class="cpp-literal">"{0:X4}:{1:X4}"</span>, D.AddressA - <span class="cpp-literal"><span class="cpp-number">1</span></span>, D.AddressB - <span class="cpp-literal"><span class="cpp-number">1</span></span>);<br>					LastType = D.Type;<br>					PrintedData = <span class="cpp-literal"><span class="cpp-number">0</span></span>;<br>				} <span class="cpp-keyword">else</span> <span class="cpp-keyword">if</span> (PrintedData >= <span class="cpp-literal"><span class="cpp-number">16</span></span>) {<br>					Console.WriteLine();<br>					Console.Write(<span class="cpp-literal">"         "</span>);<br>					PrintedData = <span class="cpp-literal"><span class="cpp-number">0</span></span>;<br>				}<br>				ConsoleColor OldColour = Console.ForegroundColor;<br><br>				<span class="cpp-keyword">switch</span> (D.Type) {<br>					<span class="cpp-keyword">case</span> DiffData.DiffType.NoChange:<br>						Console.ForegroundColor = ConsoleColor.White;<br>						<span class="cpp-keyword">break</span>;<br>					<span class="cpp-keyword">case</span> DiffData.DiffType.Addition:<br>						Console.ForegroundColor = ConsoleColor.Green;<br>						<span class="cpp-keyword">break</span>;<br>					<span class="cpp-keyword">case</span> DiffData.DiffType.Removal:<br>						Console.ForegroundColor = ConsoleColor.Red;<br>						<span class="cpp-keyword">break</span>;<br>				}<br>				Console.Write(<span class="cpp-literal">" "</span> + D.Data.ToString(<span class="cpp-literal">"X2"</span>));<br>				++PrintedData;<br>				Console.ForegroundColor = OldColour;<br>			}<br>			Console.WriteLine();<br><br>		}<br><br>		<span class="cpp-keyword">private</span> <span class="cpp-keyword">struct</span> DiffData {<br><br>			<span class="cpp-keyword">public</span> <span class="cpp-keyword">enum</span> DiffType {<br>				NoChange,<br>				Addition,<br>				Removal,<br>			}<br><br>			<span class="cpp-keyword">public</span> DiffType Type;<br><br>			<span class="cpp-keyword">public</span> <span class="cpp-keyword">byte</span> Data;<br><br>			<span class="cpp-keyword">public</span> <span class="cpp-keyword">int</span> AddressA;<br>			<span class="cpp-keyword">public</span> <span class="cpp-keyword">int</span> AddressB;<br><br>			<span class="cpp-keyword">public</span> DiffData(DiffType type, <span class="cpp-keyword">byte</span> data, <span class="cpp-keyword">int</span> addressA, <span class="cpp-keyword">int</span> addressB) {<br>				<span class="cpp-keyword">this</span>.Type = type;<br>				<span class="cpp-keyword">this</span>.Data = data;<br>				<span class="cpp-keyword">this</span>.AddressA = addressA;<br>				<span class="cpp-keyword">this</span>.AddressB = addressB;<br>			}<br><br>			<br>		}<br>	}<br>}<br></pre></div><!–ENDSCRIPT–><br><br>Removals are shown in red, additions are shown in green, data that's the same is in white.<div>


</div>
0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement