Brass 2: Dropping TASM backwards compatibility.
- silver calc
- New Member
- Posts: 73
- Joined: Tue 28 Mar, 2006 10:50 pm
- Location: Wouldn't you like to know?
-
- Calc King
- Posts: 1513
- Joined: Sat 05 Aug, 2006 7:22 am
- benryves
- Maxcoderz Staff
- Posts: 3089
- Joined: Thu 16 Dec, 2004 10:06 pm
- Location: Croydon, England
- Contact:
Emphasis mine. Brass doesn't need to know about the 0104.key file - Wappsign does.Brass Website wrote:Setting up the SDK
Brass does not sign applications itself - it delegates the job to the Wappsign utility supplied as part of the TI-83 Plus SDK. You must have the SDK installed, therefore, to sign applications.
You need to run Wappsign. It should be listed somewhere in the TI-83 Plus Flash Debugger start menu entry, or in the \Utils subdirectory of the Flash Debugger's installation folder. For signing to work, the Wappsign application needs to know where your key file resides. Click the [...] button next to the 'Key File' box, then browse for the 0104.key file. Click 'Yes' on the 'This directory is not in your search path. Add it now?' dialog box. Tick the 'Save Settings on Exit' box, then click 'Close'.
As Brass 2 can load custom output plugins, somebody clever might be able to write a self-signing application output plugin. For the moment, using Wappsign's COM interface is the way it's done.
- silver calc
- New Member
- Posts: 73
- Joined: Tue 28 Mar, 2006 10:50 pm
- Location: Wouldn't you like to know?
Code: Select all
.binarymode ti8xapp ; TI-83+ Application
Code: Select all
.binarymode intelhex ; hex file
- benryves
- Maxcoderz Staff
- Posts: 3089
- Joined: Thu 16 Dec, 2004 10:06 pm
- Location: Croydon, England
- Contact:
Sorry, I didn't see this post until now... did you ever get it working?
Good grief. I can see what Spencer was getting at when he went to write his own assembler. This is really rather embarrassing.
Pretty simple stuff. Rather meaningless, but there you go. Here's the result of oldschool Brass:
You're not reading that wrong. Nearly half a minute to assemble that code. Keep that in mind. Also keep in mind that that's a simple, single-assembly project built in the optimised release mode.
I've had a look back into Brass again. I've written quite a lot of code for it, and it's not doing an awful lot thus far. However, it can build the above code and produce identical output -That's almost 50 times faster; running in Debug mode, within the VS IDE, loading three other (also Debug) DLLs as plugins. I'm so, so, sorry.
Anyhow. So far, I have these directives in the 'Core Plugins' collection -
'TI Program Files' contains a series of .8?p output plugins, an 'Unsquish' byte transformer plugin and two directive plugins (.unsquish and .squish) that switch the byte transformer plugin on and off. (A byte transformer plugin takes a byte input and returns an array of bytes).
Do people have any particular favourite directives they use that I haven't listed above? (I know it's a rather incomplete list).
Good grief. I can see what Spencer was getting at when he went to write his own assembler. This is really rather embarrassing.
Code: Select all
.rept 9000
ld a,1
.unsquish
ld a,2
.squish
ret
.loop
Code: Select all
Brass Z80 Assembler 1.0.4.9 - Ben Ryves 2005-2006
-------------------------------------------------
Assembling...
Pass 1 complete. (2093ms).
Pass 2 complete. (22062ms).
Writing output file...
Errors: 0, Warnings: 0.
Done!
I've had a look back into Brass again. I've written quite a lot of code for it, and it's not doing an awful lot thus far. However, it can build the above code and produce identical output -
Code: Select all
Brass Assembler - Copyright © Bee Development 2005-2007
-------------------------------------------------------
ZiLOG Z80 - Copyright © Bee Development 2005-2006
TI Program Files - Copyright © Bee Development 2005-2006
Core Plugins - Copyright © Bee Development 2005-2006
Parsing source...
Building...
Writing output...
Time taken: 484.38ms.
Done!
Anyhow. So far, I have these directives in the 'Core Plugins' collection -
- .if/.elseif/.else/.endif - conditional compilation.
- .org/.porg - set the origin (.org) or pad up to the origin (.porg).
- .align - align to a particular boundary (pads).
- .rept/.loop - usual loop-unrolling stuff.
- .push/.pop - pushing and popping a value onto a stack.
- .db/.dw - the usual byte definition stuff.
'TI Program Files' contains a series of .8?p output plugins, an 'Unsquish' byte transformer plugin and two directive plugins (.unsquish and .squish) that switch the byte transformer plugin on and off. (A byte transformer plugin takes a byte input and returns an array of bytes).
Do people have any particular favourite directives they use that I haven't listed above? (I know it's a rather incomplete list).
- benryves
- Maxcoderz Staff
- Posts: 3089
- Joined: Thu 16 Dec, 2004 10:06 pm
- Location: Croydon, England
- Contact:
Well, include files usually have a ".equ". I might put .equ into a 'TASM' plugin collection, as for most people using the '=' operator should be good enough.kv83 wrote:to be honest, that's all I ever used
There's also TASM-style #define. I'm puzzling over how best to do this. Do I build function support into the emulator (so calling a function can output code and return a value?) or use a TASM-style text-replacement?
- benryves
- Maxcoderz Staff
- Posts: 3089
- Joined: Thu 16 Dec, 2004 10:06 pm
- Location: Croydon, England
- Contact:
(This post is partially to indicate progress, and partially to post some sample code from the plugin system).
I decided to add functions. They're not that tricky, really... if a constant token is found (a constant being something like 'abcd' or '"fish"' or 763) with an opening parenthesis directly after it, all the stuff between that parenthesis and the matching closing one is removed from the expression and dumped 'inside' the constant token, which is turned into a new 'function' token. When the token is executed, rather than return the label or numeric value associated with it, it's name is looked up from the loaded plugins, and if one matches the arguments that were extracted are sent to the plugin.
I've wrapped up the majority of .NET's Math class as plugins. The Core Plugins package comes with a few classes of its own that can be use to aid plugin development (such as ways to hook in to its conditional compilation system, and in this case a specialised template that accepts a fixed number of comma-delimited arguments, called CommaDelimitedFunction where the third argument added to the constructor is the number of arguments required).
All plugins need the Identifier, so Brass knows when to use it. EasyExecute in this case takes over from the primitive 'Execute' that's exposed by Brass to automatically handle incorrect numbers of arguments and to strip out the commas.
Why do I feel it worth supporting functions? Well, there's the old mess of the trig table generation functions (and remembering argument orders), so you have finer control over them with the above...
(I haven't written a for-loop directive yet).
All plugins have a reference to 'Builder compiler'. This is the current compiler, so when called they can control the compiler in some way. In other words, if I was feeling very lazy, and didn't add macro support...Sadly this won't work as you can't have floating expressions that don't perform an assignment.
I decided to add functions. They're not that tricky, really... if a constant token is found (a constant being something like 'abcd' or '"fish"' or 763) with an opening parenthesis directly after it, all the stuff between that parenthesis and the matching closing one is removed from the expression and dumped 'inside' the constant token, which is turned into a new 'function' token. When the token is executed, rather than return the label or numeric value associated with it, it's name is looked up from the loaded plugins, and if one matches the arguments that were extracted are sent to the plugin.
I've wrapped up the majority of .NET's Math class as plugins. The Core Plugins package comes with a few classes of its own that can be use to aid plugin development (such as ways to hook in to its conditional compilation system, and in this case a specialised template that accepts a fixed number of comma-delimited arguments, called CommaDelimitedFunction where the third argument added to the constructor is the number of arguments required).
Code: Select all
using System;
using System.Reflection;
using Brass2;
using Brass2.Plugins;
using Brass2.Compiler;
namespace MixedCore {
public class Min : FunctionHelper.CommaDelimitedFunction {
public Min(Assembly containingAssembly, Builder compiler)
: base(containingAssembly, compiler, 2) {
}
public override string Identifier {
get { return "min"; }
}
protected override double EasyExecute(Builder.ExpressionGroup[] arguments) {
return Math.Min(arguments[0].ValueDouble, arguments[1].ValueDouble);
}
}
public class Max : FunctionHelper.CommaDelimitedFunction {
public Max(Assembly containingAssembly, Builder compiler)
: base(containingAssembly, compiler, 2) {
}
public override string Identifier {
get { return "max"; }
}
protected override double EasyExecute(Builder.ExpressionGroup[] arguments) {
return Math.Max(arguments[0].ValueDouble, arguments[1].ValueDouble);
}
}
}
Why do I feel it worth supporting functions? Well, there's the old mess of the trig table generation functions (and remembering argument orders), so you have finer control over them with the above...
Code: Select all
theta = -1
.rept 256
.db floor(127 * sin(++theta * pi() / 128))
.loop
All plugins have a reference to 'Builder compiler'. This is the current compiler, so when called they can control the compiler in some way. In other words, if I was feeling very lazy, and didn't add macro support...
Code: Select all
public class BCall : Function {
public BCall(Assembly containingAssembly, Builder compiler)
: base(containingAssembly, compiler) { }
public override string Identifier { get { return "bcall"; } }
public override double Execute(Builder.ExpressionGroup[] arguments) {
if (Compiler.IsWritingOutput) {
if (arguments.Length != 1) {
throw new Exception("BCALL expects one argument.");
} else {
ushort RomCallIndex = (ushort)arguments[0].ValueLong;
Compiler.WriteData(0xEF); // RST $28
Compiler.WriteData(new byte[] { (byte)RomCallIndex, (byte)(RomCallIndex >> 8) });
}
}
Compiler.CurrentInstructionPointer.Value += 3;
return double.NaN;
}
}
- benryves
- Maxcoderz Staff
- Posts: 3089
- Joined: Thu 16 Dec, 2004 10:06 pm
- Location: Croydon, England
- Contact:
I've updated a fixed a number of the directives, and added a file operations plugin.
I've added while and for loops too.
Alternatively, that could be written as:
The advantage of the Basic-styled loops is that they have a higher chance of success when detecting infinite loops, and also don't have floating-point creep when using non-integer steps.
There's also the ability to declare your own functions at runtime, though I haven't figured out a clean way to return a value. For the moment they can be used like TASM's macros;
Code: Select all
fhnd = fopen("test.txt", r)
#while !feof(fhnd)
chr = freadbyte(fhnd)
.if chr >= 'a' && data <= 'z'
#db chr + 'A' - 'a'
.else
#db chr
.endif
#loop
fclose(fhnd)
Code: Select all
#for theta = 0, theta < 360, ++theta
.db min(127, round(128 * sin(deg2rad(theta))))
#loop
Code: Select all
#for theta is 0 to 359
.db min(127, round(128 * sin(deg2rad(theta))))
#loop
There's also the ability to declare your own functions at runtime, though I haven't figured out a clean way to return a value. For the moment they can be used like TASM's macros;
Code: Select all
_PutS = $450A
.function bcall(label)
rst $28
.dw label
.endfunction
bcall(_PutS)
- silver calc
- New Member
- Posts: 73
- Joined: Tue 28 Mar, 2006 10:50 pm
- Location: Wouldn't you like to know?
- benryves
- Maxcoderz Staff
- Posts: 3089
- Joined: Thu 16 Dec, 2004 10:06 pm
- Location: Croydon, England
- Contact:
There are a lot of things to sort out - least of all the Z80 assembler plugin isn't even finished - so "when it's done".
I realise it's rude to post previews but not deliver, but I wanted to demonstrate that this complete rewrite is worth it in at least it's a more robust system.
The biggest worry with releasing it is if someone decides to start writing plugins and I revise the way they're handled.
For starters, the current way a source file is represented (as a LinkedList<Command>) is not great as the functionality for compiling the list is built in to it - so you can't have two such lists of commands and build them into a single output, so this is where the returning values from a function problem comes in (it would be easier if I could split the function body into its own list of commands and execute that whenever the function is called) or even support include files!
I realise it's rude to post previews but not deliver, but I wanted to demonstrate that this complete rewrite is worth it in at least it's a more robust system.
The biggest worry with releasing it is if someone decides to start writing plugins and I revise the way they're handled.
For starters, the current way a source file is represented (as a LinkedList<Command>) is not great as the functionality for compiling the list is built in to it - so you can't have two such lists of commands and build them into a single output, so this is where the returning values from a function problem comes in (it would be easier if I could split the function body into its own list of commands and execute that whenever the function is called) or even support include files!
- benryves
- Maxcoderz Staff
- Posts: 3089
- Joined: Thu 16 Dec, 2004 10:06 pm
- Location: Croydon, England
- Contact:
I've broken down the compiler into an "Overseer" (which still contains all of the methods for controlling the assembly process, such as switching the assembler on/off or jumping around the source using bookmarks) and "Source Segments" (which are a series of commands).
A source segment is typically a single assembly file, but by using two new methods - StartRecording and StopRecording - you can extract a block of code from the main assembly process and run it at a later date. You can run it alongside the main body of code (in the case of an include file) or externally (in the case of a function). Now, this sort of thing is possible:
(I'm using the VB-style return mechanism where the return value is set by assigning to a variable with the same name of the function).
Something's not quite right with running the functions away from the main body of code, and calling another user-defined function from your own doesn't work, and so there's no recursion either. It should hopefully be a simple enough fix. A 'return' statement would also be easy enough; when encountered set the value as required and switch the assembler off for the remainder of the function.
I think the problem is the large number of static variables that still exist from the "one source segment only" idea. It should hopefully be easy enough to fix.
A source segment is typically a single assembly file, but by using two new methods - StartRecording and StopRecording - you can extract a block of code from the main assembly process and run it at a later date. You can run it alongside the main body of code (in the case of an include file) or externally (in the case of a function). Now, this sort of thing is possible:
Code: Select all
.function slow_mul(op1, op2)
slow_mul = 0
.rept abs(op1)
.if sign(op1) > 0
slow_mul += op2
.else
slow_mul -= op2
.endif
.loop
.endfunction
.echo slow_mul(log(100, 10), slow_mul(5, 4))
Something's not quite right with running the functions away from the main body of code, and calling another user-defined function from your own doesn't work, and so there's no recursion either. It should hopefully be a simple enough fix. A 'return' statement would also be easy enough; when encountered set the value as required and switch the assembler off for the remainder of the function.
I think the problem is the large number of static variables that still exist from the "one source segment only" idea. It should hopefully be easy enough to fix.