Brass 3 (Under Development)
- benryves
- Maxcoderz Staff
- Posts: 3089
- Joined: Thu 16 Dec, 2004 10:06 pm
- Location: Croydon, England
- Contact:
I've been comparing binaries output by old Brass and this new assembler (pixelmad.z80, eliza.z80, CoBB's pinball.asm, kv's slip.z80, Joe P's asteroid.z80), and finally(!) the two are matching (the usual errors of reading ld de,(xyz)+1 as ld de,(*) and so on seem to have been fixed).
There's a large amount of error trapping to add (for example, bcall(xyz) where bcall hasn't been defined creates a new label, bcall(xyz), rather than raising an error), more testing and the addition of the many missing features (.defcont, .deflong, .var, structs, reusable labels, ...) that I haven't got around to yet.
I hope to get a demo out soon for testing
There's a large amount of error trapping to add (for example, bcall(xyz) where bcall hasn't been defined creates a new label, bcall(xyz), rather than raising an error), more testing and the addition of the many missing features (.defcont, .deflong, .var, structs, reusable labels, ...) that I haven't got around to yet.
I hope to get a demo out soon for testing
- benryves
- Maxcoderz Staff
- Posts: 3089
- Joined: Thu 16 Dec, 2004 10:06 pm
- Location: Croydon, England
- Contact:
Various parser updates and fixes have been implemented (including reintroduced support for the ternary conditional operator ?: ).
Slightly strangely, ?: has very low operator precedence. This means that in the following expression:
y is 0, not 1. The reason for this is that as ?: has low precedence, it gets evaluated after both ++x and --x.
To further confuse issues, before adding ternary operator support to the parser I implemented an if() function, like this:
This was inspired by VB's IIf() function. However, as this function can pick and choose which expressions to evaluate, y becomes 1. This is backwards to convention where the C-style ?: operator is lazy (only executing the successful side) and the BASIC-style if() isn't, executing both sides first.
Multi-line statements are the next area of contention. Currently a statement is terminated with a newline or a \ character. However, in some special cases, this termination is disabled - for example, by the .define directive. Another directive that does this is the .enum directive, which results in syntax like this:
I think that using the \ termination character is rather neat. Others may disagree, so I'd like to hear your ideas.
In a similar vein, I've added support for runtime-aliased plugins. This can be used by a user-defined function plugin, so the following is now possible:
Finally, for silliness, I've added a new plugin collection that handles images:
"l2" in imggetpixel is the pixel format you want - for example, "argb" or "b2g2r2" (if no width is specified, 8 bits is assumed).
It might look useless, but it at least gives me some decent parser testing material.
Slightly strangely, ?: has very low operator precedence. This means that in the following expression:
Code: Select all
x = 0
y = true() ? ++x : --x
To further confuse issues, before adding ternary operator support to the parser I implemented an if() function, like this:
Code: Select all
x = 0
y = if(true(), ++x, --x)
Multi-line statements are the next area of contention. Currently a statement is terminated with a newline or a \ character. However, in some special cases, this termination is disabled - for example, by the .define directive. Another directive that does this is the .enum directive, which results in syntax like this:
Code: Select all
.enum Days
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
\
/*
This creates the following labels:
Days.Sunday = 0
Days.Monday = 1
Days.Tuesday = 2
...
Days.Saturday = 6
*/
.enum Numbers
Eleven = 11,
Twelve,
Thirteen,
Twenty = 20,
Vingt = 20,
Zwanzig = 20,
TwentyOne,
Four = 4,
Five
\
.echoln Numbers.Eleven ; 11
.echoln Numbers.Twelve ; 12
.echoln Numbers.Thirteen ; 13
.echoln Numbers.Twenty ; 20
.echoln Numbers.Vingt ; 20
.echoln Numbers.Zwanzig ; 20
.echoln Numbers.TwentyOne ; 21
.echoln Numbers.Four ; 4
.echoln Numbers.Five ; 5
In a similar vein, I've added support for runtime-aliased plugins. This can be used by a user-defined function plugin, so the following is now possible:
Code: Select all
; A recursive function that calculates n!
.function factorial(n)
if(n <= 0, 1, n * factorial(n - 1))
\
; 1.5511210043331E+25
.echoln factorial(25)
Code: Select all
; Open the image:
img = imgopen("delete.png")
; Loop over each pixel in the image:
.for y = 0, y < imgheight(img), ++y
.for x = 0, x < imgwidth(img), ++x
; Grab the luminosity of each pixel to two bits:
l = imggetpixel(img, x, y, 'l2')
; Convert to an 'ASCII-art' brightness:
c = choose(l + 1, ' ', '.', '+', '#')
.echochar c, c
.loop
.echoln
.loop
/*
....++++....
++++########++..
++######++++++##++..
++++##++++++++++++##++..
..++##++++++++++++++++##..
..##++++++++++++++++++++++..
++##++++############++++++..
++##++++############++++++..
..++++++++++++++++++++++++..
....##++++++++++++++++++..
..++++++++++++++++++++..
..++++++++++++++++..
....++++++++....
........
*/
It might look useless, but it at least gives me some decent parser testing material.
Last edited by benryves on Wed 03 Oct, 2007 11:55 am, edited 1 time in total.
Dude, seriously What's next? A can opener?
I think the backslash is a bit confusing, being the Unix minded guy that I am, as it usually represents that the line continues beyond the \n, not that it ends...
You'd expect:
not
I think the backslash is a bit confusing, being the Unix minded guy that I am, as it usually represents that the line continues beyond the \n, not that it ends...
You'd expect:
Code: Select all
.define bcall(xyz) \
rst 28h \
.dw xyz
Code: Select all
.define bcall(xyz)
rst 28h
.dw xyz
\
http://clap.timendus.com/ - The Calculator Link Alternative Protocol
http://api.timendus.com/ - Make your life easier, leave the coding to the API
http://vera.timendus.com/ - The calc lover's OS
http://api.timendus.com/ - Make your life easier, leave the coding to the API
http://vera.timendus.com/ - The calc lover's OS
- benryves
- Maxcoderz Staff
- Posts: 3089
- Joined: Thu 16 Dec, 2004 10:06 pm
- Location: Croydon, England
- Contact:
The parser sees both \ and the carriage return as statement terminators; it needs to see \ as a statement terminator (rather than line continuation) for TASM's sake (hence the bcall macro expanding to "rst 28h \ .dw label").
Requiring an explicit end-of-statement character (like C's ';') isn't going to work (would break compatibility with all other source files, assembly is line based). We rather need to keep \ as a terminator, but one could introduce a line continuation character like BASIC's _:
Then the enum would have to look like:
Requiring an explicit end-of-statement character (like C's ';') isn't going to work (would break compatibility with all other source files, assembly is line based). We rather need to keep \ as a terminator, but one could introduce a line continuation character like BASIC's _:
Code: Select all
.define bcall(xyz) _
rst 28h _
.dw xyz
Code: Select all
.enum Days
Sunday, _
Monday, _
Tuesday, _
Wednesday, _
Thursday, _
Friday, _
Saturday
-
- 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:
Yes, I was originally going to use .end*, but that requires more stuff hacked into the parser to catch that sort if thing.
It's appealing in the sense that other directives, such as the repetition ones (.for/.loop/.while or .if) require a terminating directive (.loop or .endif).
The choices are there, this is why I'm asking you lot what you think I should go for
It's appealing in the sense that other directives, such as the repetition ones (.for/.loop/.while or .if) require a terminating directive (.loop or .endif).
The choices are there, this is why I'm asking you lot what you think I should go for
- driesguldolf
- Extreme Poster
- Posts: 395
- Joined: Thu 17 May, 2007 4:49 pm
- Location: $4080
- Contact:
how about:
basically if a "," is appended then there is a next statement (eventually on the next line)
Nice and clean ^^
This can be generalised:
This would then be valid, or (if you don't want this) you could give these kind of directives a flag:
true=allows statements to continue on the next line
false=the opposite of true
And all TASM source files will compile properly under the new Brass
Though this approach does not work with .define
and I don't see a way (other than \ and .defcont) to handle this
.function shouldn't be a problem for backward compatibility (because it doesn't exists in TASM )
Code: Select all
.enum days
Monday, Tuesday, Wednesday, ; Though comments should be allowed here
Thursday, Friday,
Saturday, Sunday
; Done because there is no ","
Nice and clean ^^
This can be generalised:
Code: Select all
.db 0,0,0,0,
0,0,0
true=allows statements to continue on the next line
false=the opposite of true
And all TASM source files will compile properly under the new Brass
Though this approach does not work with .define
and I don't see a way (other than \ and .defcont) to handle this
.function shouldn't be a problem for backward compatibility (because it doesn't exists in TASM )
Code: Select all
.function test (par1, par2) {/*code goes here*/ xor a\ ld b, a\ ld c, a
ld h, par1
ld l, par2
; Note that I have no idea how the function system works
; thus the last 2 lines can be completely illegal :P
}
- benryves
- Maxcoderz Staff
- Posts: 3089
- Joined: Thu 16 Dec, 2004 10:06 pm
- Location: Croydon, England
- Contact:
I do rather like that idea (if last non-whitespace/non-comment token is a comma = ignore newlines).
Functions currently only allow mathematical expressions (so not even directives work in them) which needs to be fixed anyway; I'd rather use a .function/.endfunction pair and let the user put anything they like inside them. I'll need to add a public method to the compiler that will let a plugin author "inject" some code.
Functions currently only allow mathematical expressions (so not even directives work in them) which needs to be fixed anyway; I'd rather use a .function/.endfunction pair and let the user put anything they like inside them. I'll need to add a public method to the compiler that will let a plugin author "inject" some code.
-
- Calc King
- Posts: 1513
- Joined: Sat 05 Aug, 2006 7:22 am
- driesguldolf
- Extreme Poster
- Posts: 395
- Joined: Thu 17 May, 2007 4:49 pm
- Location: $4080
- Contact:
XD that is one to remember, the most hilarious things happen when you least expect itKing Harold wrote:I'll remember this foreverDries wrote:false=the opposite of true
One little thing though:
Do you have to start the enumeration on the next line you write .enum? maybe you can have one big list and take the first statement as the name. This allows more flexibility:
Code: Select all
.enum name,
one, two, three,
test1, test2, test3
Code: Select all
.enum name, one, two, three
- benryves
- Maxcoderz Staff
- Posts: 3089
- Joined: Thu 16 Dec, 2004 10:06 pm
- Location: Croydon, England
- Contact:
Code: Select all
.enum name item1, item2, item3
I implemented your comma trick and it works very well, so thanks for the idea.
Functions now work "properly" (and in about a third of the lines of the original function implementation!) The compiler pulls in source code, tokenises and caches it (after matching assembly source code and directives) in a big list. You can recall the current statement and tell the compiler to jump to a particular statement (by index), which is how the looping directives work.
The function plugin, then, simply has to remember the current position +1 (entry point), switch off the compiler (which means that nothing is executed until a matching directive is hit, in this case .endfunction), remember the current position -1 (exit point). When the function is invoked it just asks "recompile statements between entry point and exit point".
A few sample snippets:
Code: Select all
/* Output the eight bits of value as 1s or 0s */
.function echobinary(value)
.for bit is 7 to 0
.if value & 1 << bit
.echo 1
.else
.echo 0
.endif
.loop
.endfunction
/* Output the eight bits of value as 1s or 0s with a % prefix */
.function echobinarybyte(value)
.echo '%' \ echobinary(value)
.endfunction
/* Output the sixteen bits of value as 1s or 0s with a % prefix */
.function echobinaryword(value)
.echo '%'
echobinary(value >> 8)
echobinary(value)
.endfunction
/* Output the four bits of value as a hex digit */
.function echohexnybble(value)
.echochar choose(1 + (value & %1111),
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F')
.endfunction
/* Output the eight bits of value as two hex digits */
.function echohex(value)
echohexnybble(value >> 4)
echohexnybble(value)
.endfunction
/* Output the eight bits of value as two hex digits */
.function echohexbyte(value)
.echo '$' \ echohex(value)
.endfunction
/* Output the eight bits of value as two hex digits */
.function echohexword(value)
.echo '$'
echohex(value >> 8)
echohex(value)
.endfunction
echobinaryword(1234) ; Outputs %0000010011010010
.echoln
echohexword(%1011111011101111) ; Outputs $BEEF
.echoln
Code: Select all
/* Enumeration for different calculator models */
#enum Model
TI8X, ; TI-83 Plus
TI83 ; TI-83
/* Set to TI8X or TI83 */
Model = Model.TI83
/* Use bcall() to invoke ROM calls */
#function bcall(label)
.if Model == Model.TI8X
rst 28h
.dw label
.else
call label
.endif
#endfunction
Code: Select all
#function f(n)
.if n == 0
f = 1
.else
f = n * f(n - 1)
.endif
#endfunction
.echoln f(30) ; Outputs 2.65252859812191E+32.
Code: Select all
.function distance(x,y)
distance = sqrt(x * x + y * y)
.endfunction
.echoln distance(3, 4) ; Outputs 5.
Code: Select all
.function lda(value)
.if defined(value) && value == 0
xor a
.else
ld a,value
.endif
.endfunction
lda(fowardreference) ; Error
forwardreference = 100
Code: Select all
.function lda(value) where defined(value) && value ==0
xor a
.endfunction
.function lda(value)
ld a,value
.endfunction
Code: Select all
.function lda(macro value)
; ...
.endfunction
- driesguldolf
- Extreme Poster
- Posts: 395
- Joined: Thu 17 May, 2007 4:49 pm
- Location: $4080
- Contact:
No problem at allbenryves wrote:I implemented your comma trick and it works very well, so thanks for the idea.
I got nothing to addbenryves wrote:The function plugin, then, simply has to remember the current position +1 (entry point), switch off the compiler (which means that nothing is executed until a matching directive is hit, in this case .endfunction), remember the current position -1 (exit point). When the function is invoked it just asks "recompile statements between entry point and exit point".
as long as in ".for bit is 7 to 0" (there's no need to be able to modify 7 in the loop) 0 can be modified in the loop itself, like:benryves wrote:Code: Select all
/* Output the eight bits of value as 1s or 0s */ .function echobinary(value) .for bit is 7 to 0 .if value & 1 << bit .echo 1 .else .echo 0 .endif .loop .endfunction
Code: Select all
value=5
.for par is 0 to value
.db value
value=value-1
.loop
Code: Select all
.db 5,4,3,2 ;I suppose it terminates when par>value (and not >=)
Ah, you can still choose between "." and "#" for directive prefixes
Some notes:
why do you use textual delimiters instead of commas? (no idea if that sentence makes sense ^^)
this is feels better imo:
Code: Select all
;(1)
.for bit=7, bit<=0, bit=bit-1
.loop
;(2)
.for bit,7,0
.loop
I don't understand this part...benryves wrote:When called, the arguments are evaluated then passed (as local labels in a new, temporary module) into the function.
...
...
...
Both have their advantages, so I might go for both, but - as always - I'd like to know your opinions.
parameters of a function shouldn't be labels (as labels can only store numbers), rather some textual representations of some content. though how will you make the difference between labels and parameters...
I'm confused about it...
Anyway I am glad I can help you make Brass even better (and it's looking so good already!!!)
- benryves
- Maxcoderz Staff
- Posts: 3089
- Joined: Thu 16 Dec, 2004 10:06 pm
- Location: Croydon, England
- Contact:
Two different for loops are supplied:
The use of commas vs text is just to differentate between the two more easily.
"x" is not evaluated and gets macro-replaced into the ".db" statement, but "repeat" gets evaluated as a number.
Code: Select all
; "C-style"
.for <initialisation>, <condition>, <increment>
.loop
; "BASIC-style"
.for <label> is <start> to <end> [step <increment>]
.loop
This is why I suggested the use of a "macro" argument, so:...shouldn't be labels (as labels can only store numbers), rather some textual representations of some content. though how will you make the difference between labels and parameters...
Code: Select all
.function repeatstring(macro x, repeat)
.for i=0, i < repeat, ++i
.db x
.loop
.endfunction
repeatstring("Hello", 3) ; Outputs "HelloHelloHello"
- driesguldolf
- Extreme Poster
- Posts: 395
- Joined: Thu 17 May, 2007 4:49 pm
- Location: $4080
- Contact:
Just PERFECT!
you can just find/replace macro arguments, but you can't do that with the numbers because they can be changed during the function (macro arguments cannot)
Ok that isn't very good English...
All I want to say is find/replace macro arguments and always re-evaluate labels
Which is just as you said
yes you will need to support them both:benryves wrote:"x" is not evaluated and gets macro-replaced into the ".db" statement, but "repeat" gets evaluated as a number.
you can just find/replace macro arguments, but you can't do that with the numbers because they can be changed during the function (macro arguments cannot)
Ok that isn't very good English...
All I want to say is find/replace macro arguments and always re-evaluate labels
Which is just as you said
-
- Calc King
- Posts: 1513
- Joined: Sat 05 Aug, 2006 7:22 am