Glass Z80 assembler 0.5

Glass Z80 assembler 0.5

by Grauw on 18-01-2017, 21:32
Topic: Development
Languages:

Glass is a cross-platform assembler for the Z80 processor. It’s been a while since the last release, with quite a number of improvements since, so a release was past due!

What’s new in Glass 0.5:

  • The incbin directive is now supported.
  • The ternary ?: operator is now supported.
  • The . member operator is now an official operator.
  • Sequences can now be indexed with the [] operator.
  • Symbols can now also start with the $ character.
  • Include now supports the once annotation.
  • The 0x and 0X prefix can now be used for hexadecimal numbers. Thanks to Paul Bosselaar.
  • Macro arguments can now specify default values with =.
  • Expressions can now span multiple lines.
  • The org statement no longer affects the address of a preceding label (bc).
  • Some invalid instructions now throw errors (e.g. bit 7,ixh).
  • Contexts are now resolved through macro arguments.
  • Instructions and macros can now be passed into macro arguments.
  • Section identifiers are now resolved like any other expression.
  • Examples for COM, ROM and BIN files are now included in the source code.
  • Error messages were improved.
  • Java 8 is now required.

Relevant link: Glass project page

Comments (38)

By snout

Ascended (15187)

snout's picture

20-01-2017, 01:02

Kudos to Grauw for taking MSX assembly to the next level.

By Grauw

Ascended (10821)

Grauw's picture

20-01-2017, 09:47

Trying to... not quite there yet, but trying to... Smile

By sd_snatcher

Prophet (3677)

sd_snatcher's picture

20-01-2017, 21:07

Thank you for all these nice software you create, Grauw. Smile

By gdx

Enlighted (6445)

gdx's picture

22-01-2017, 14:49

I'm looking for a good disassembler.

By sd_snatcher

Prophet (3677)

sd_snatcher's picture

04-02-2017, 00:52

Is it possible to do OOP with Glass? That would be a really nice feature for a new assembler.

By Grauw

Ascended (10821)

Grauw's picture

05-02-2017, 13:47

I try to be as supportive of OOP style in Glass as I can, because it is how I write my software myself. However things like polymorphic calls and how objects are created etc. is more about the libraries and implementation choices of code and not so much about the assembler itself.

E.g. there are several ways to instantiate objects, statically, on a heap, with or without a template, etc. And there are also several ways to do virtual calls, with different performance, memory usage and flexibility trade-offs. The assembler isn’t much of a help there except to try and facilitate it.

For example I define and use a class as follows:

ClassTest: MACRO
	dummy:
		db 0
	_size:
	ENDM

ClassTest_class: Class ClassTest, Heap_main

; ix = this
ClassTest_Construct:
	ld (ix + ClassTest.dummy),255
	ret

; ix = this
ClassTest_Destruct:
	ret

ClassTest_Test:
	call ClassTest_TestConstructDestruct
	ret

ClassTest_TestConstructDestruct:
	call ClassTest_class.New
	call ClassTest_Construct
	call ClassTest_Destruct
	call ClassTest_class.Delete
	ret

There are some syntactic sugar things where Glass is helpful here, e.g. being able to pass ClassTest into the Class definition macro, to determine its size, instantiate a template, and to reference .New and .Delete static members on it. In principle OOP can be done in any assembler, however definitely I started work on Glass to simplify the syntax of these things for me.

By Wlcracks

Hero (572)

Wlcracks's picture

07-01-2019, 19:17

Checking Glass for my (new) project now. I was wondering why there is a 0x... for hex notation but no 0bxxxx for binary. Coming mainly from C coding, i didn't get why that was no option, while other options are available like #$ and %?

By Grauw

Ascended (10821)

Grauw's picture

07-01-2019, 20:17

Hi Wlcracks,

Thanks for checking out Glass.

I reluctantly added the 0x support that was submitted because there’s one or two assemblers (e.g. Sjasm) which support it as well, so there was some argument for interoperability with them. However it’s not standard assembly syntax at all, so code using the 0x prefix will not be portable, and 0b even less so. I don’t think it’s good to introduce more C syntax into assembly (like 0b or // comments).

I don’t recommend using 0x. I recommend to use H and B suffixes, or # / $ and %, which are fairly universally supported. You will get used to it very quickly.

By Wlcracks

Hero (572)

Wlcracks's picture

07-01-2019, 20:18

Hi Grauw,
Thank YOU for providing the software.
I will use H en B noproblem.

By Wlcracks

Hero (572)

Wlcracks's picture

08-01-2019, 09:58

Just a noobisch question.
1000H is fine
9000H is fine
but A000H gives an error
seems it needs a leading zero, so 0A000H
Can you please tell me the story behind this?
When do you add this extra zero and why?
loading 16bits registers require a extra 8 bit?

By Edevaldo

Master (156)

Edevaldo's picture

08-01-2019, 17:45

I can't answer it for Glass. But it was a common restriction of old assemblers that numbers needed to start with a digit and end with a letter defining the base (if non-decimal). This does not add a byte at all (or a nibble) to your program. Maybe Glass is enforcing that 0 (zero) or maybe it is a glitch. It just made it easier to parse the files in machines with very limited memory. Today it may feel a little nonsensical.

By Grauw

Ascended (10821)

Grauw's picture

08-01-2019, 19:44

Feel free to ask!

Wlcracks wrote:

When do you add this extra zero

It is only required when the hexadecimal number starts with a letter.

It’s certainly not a glitch, nor has to do with limited memory Smile. Every assembler requires this extra 0 for H/B suffix notation, and there’s a very logical reason for it:

In the grammar of assembly language, every symbol (label) starts with a letter, so number literals can not. Otherwise they are not distinguishable. E.g. consider a symbol ADDH, it would be very surprising if that would be generating the number 2781. A000H is a symbol as well, not a number.

If the leading 0 bothers you, you can also use # and % prefixes, which don’t require a leading zero because the prefix already distinguishes it from a symbol.

p.s. This isn’t an extra 4 bits, A0 and 0A0 are the same number, just like in decimal 0010 is exactly the same number as 10. Glass actually stores 32-bit numbers internally, and truncates them to 8 or 16-bit when output. This is so you can do math more easily (like 1000 * 555 / 111) without running into precision problems.

By Wlcracks

Hero (572)

Wlcracks's picture

08-01-2019, 20:06

cool, clear, thanks again!

By syn

Prophet (2135)

syn's picture

08-01-2019, 22:15

Interesting, I did not know this was an issue. I guess not all assemblers have this problem/issue, at least not the one I use (tniasm 1.0).

I tried sjasm earlier but that resulted in error in labels when I changed 0xsomething to somethingh so for now I guess tniasm 1.0 is the only exception to the rule?

Are there any other oddness/quirks similar to this in general? Would be nice to know if before I think there is something wrong with the code i've written Wink

By Grauw

Ascended (10821)

Grauw's picture

09-01-2019, 00:42

Every assembler that I know works like this (Gen80, M80, Compass, Pasmo, Sjasm, MASM, …), and there’s no way to support that other than either forbidding symbols that match “[A-F][0-9A-F]H”, breaking compatibility with sources for other assemblers, or only trying to parse undefined labels as hex, which can lead to very unexpected programming bugs. Not to mention complicating the parsing rules. I also don’t see a need for it, if people don’t like it then they can use # and % notation which is the second-most universal syntax. Are you saying that tniasm does one of those two things?

One syntactic thing I think is strange is that the colon after a label is optional. I don’t understand why that is. I support it for compatibility’s sake, but it unnecessarily complicates the grammar and parsing, and reduces consistency in the source code you write. So yeah, I don’t like those at all. The support for colon-less labels is the reason why instructions must be indented with white space (because at the moment of parsing I can’t distinguish a label without colon from an instruction or macro). Which is good practice anyway, but still it’s annoying that it matters.

By Wlcracks

Hero (572)

Wlcracks's picture

09-01-2019, 12:28

I guess not a bug in glass, its a logical response but i am not sure this is what the ideal situation would be:

1.

in glass:

equ_SB1_Color_data equ ((equ_VDP_Color_Gray << 4) + equ_VDP_Color_Yellow)

ld hl, equ_VDP_color_table_screenblock1_address ; HL - Destination
ld bc, equ_VDP_color_table_screenblock1_lenght ; BC - Fill lenght
ld a, equ_SB1_Color_data ; A - Fill data
call FILVRM ; fill vram

OpenMSX debugger reports:

ld hl,#2000
ld bc,#0800
ld a,(#00ea)
call #0056

2.

in glass:

equ_SB1_Color_data equ (equ_VDP_Color_Gray << 4) + equ_VDP_Color_Yellow

ld hl, equ_VDP_color_table_screenblock1_address ; HL - Destination
ld bc, equ_VDP_color_table_screenblock1_lenght ; BC - Fill lenght
ld a, equ_SB1_Color_data ; A - Fill data
call FILVRM ; fill vram

OpenMSX debugger reports:

ld hl,#2000
ld bc,#0800
ld a,#ea
call #0056

Situation 1. would be expected when ld a,(equ_SB1_Color_data)
?

Grauw, you getting bored of me already? Wink

By Grauw

Ascended (10821)

Grauw's picture

09-01-2019, 14:03

Not at all! Smile

That’s a feature actually, e.g.:

one: equ a
two: equ hl
three: equ (hl)

    ld one,1
    ld two,2
    ld three,3

Also useful when you need to pass (hl) to macro parameters, etc.

Load_0_M: MACRO ?where
    ld ?where,0
    ENDM

    Load_0_M a
    Load_0_M hl
    Load_0_M (hl)

But yeah, don’t unnecessarily write outer parentheses, or you could get what you’re getting.

By Wlcracks

Hero (572)

Wlcracks's picture

09-01-2019, 14:13

ok cool thanks.
next q

I want to reserve a area for a konami mega rom mapper mechanism. I want to lock the 0x4000-0x7fff for now and be able to page as shown in:
http://112-226-144-85.ftth.glasoperator.nl/msx/MegaROM.htm

This small example doesnt want build a file greater then 64kb.
---
ROM_Page00_01: ds #4000
ROM_Page02: ds #2000
ROM_Page03: ds #2000
ROM_Page04: ds #2000
ROM_Page05: ds #2000
ROM_Page06: ds #2000
;ROM_Page07: ds #2000
;ROM_Page08: ds #2000
;ROM_Page09: ds #2000
;ROM_Page10: ds #2000
RAM: ds VIRTUAL #1000

; ROM Page 0, 1 locked on 0x4000 - 0x7fff

SECTION ROM_Page00_01

org #4000 ; expansion ROM header

db #41 ; "A"
db #42 ; "B"
dw Init ; start of the init code, 0 if no initcode
dw #0000 ; pointer to CALL statement handler, 0 if no such handler
dw #0000 ; pointer to expansion device handler, 0 if no such handler
dw #0000 ; pointer to the start of a tokenized basicprogram, 0 if no basicprogram
dw #0000 ; room reserved for future extensions
dw #0000
dw #0000

Init:
ret

ENDS

SECTION ROM_Page02
db "PAGE02"
ENDS

SECTION ROM_Page03
db "PAGE03"
ENDS

SECTION ROM_Page04
db "PAGE04"
ENDS

SECTION ROM_Page05
db "PAGE05"
ENDS

SECTION ROM_Page06
db "PAGE06"
ENDS

;SECTION ROM_Page07
;db "PAGE07"
;ENDS

SECTION RAM
db 0
ENDS
---

Adding page07 and higher doesnt work. I seems fine for the lower pages.

Errors reported:
---
>java -jar c:\prg\glass\glass-0.5.jar test_mega_rom.asm test_mega_rom.rom
Exception in thread "main" nl.grauw.glass.AssemblyException: Address out of range: 10000H
[at test_mega_rom.asm:7]
;ROM_Page08: ds #2000
at nl.grauw.glass.Scope.setAddress(Scope.java:42)
at nl.grauw.glass.instructions.InstructionObject.resolve(InstructionObject.java:18)
at nl.grauw.glass.Line.resolve(Line.java:113)
at nl.grauw.glass.Source.resolve(Source.java:82)
at nl.grauw.glass.Source.resolve(Source.java:77)
at nl.grauw.glass.Source.assemble(Source.java:60)
at nl.grauw.glass.Assembler.writeObject(Assembler.java:55)
at nl.grauw.glass.Assembler.main(Assembler.java:44)
---
Thanks again.

By Grauw

Ascended (10821)

Grauw's picture

09-01-2019, 18:42

Hi Wlcracks,

I removed the address out of range check recently in development builds, it was more annoying than useful (your specific example being one of the common encounters). Now the address will just wrap around. However the current released version of Glass doesn't have that change yet.

In Glass 0.5 you can work around it by putting a few ORG directives between the ds'es at the top, resetting the address counter so that it never goes above 64K.

By Wlcracks

Hero (572)

Wlcracks's picture

09-01-2019, 19:53

Hello Grauw, Thank you, nice to hear glass is still in development. I will try your solution.

By Wlcracks

Hero (572)

Wlcracks's picture

13-01-2019, 21:35

Yes with this trick, the pages for the megarom work in glass thank you.

By Wlcracks

Hero (572)

Wlcracks's picture

21-01-2019, 13:43

Sorry to bother again, working hard for Njimegen!!. But is there a C like way to rem - out large blocks of code in Glass

e.g.
/*
disabled or long stories
*/

Thanks

By Grauw

Ascended (10821)

Grauw's picture

21-01-2019, 14:35

There’s no block comment in assembly syntax. However many editors have a multi-line editing function, in Visual Studio and Notepad++ this is ALT + click-drag, in Eclipse this mode is activated by ALT-SHIFT-A. That way you can easily add or remove a ; in front of many lines at once.

By Wlcracks

Hero (572)

Wlcracks's picture

21-01-2019, 14:44

Ok, thanks, just making sure i am doing it right.

By Wlcracks

Hero (572)

Wlcracks's picture

22-01-2019, 07:36

Yes another question!

What is the best way to (re) decare RAM memory, static memory if you will.

Is this legal/allowed/advised in Glass?

org #c000

var_ScratchMemory:

ds #800

org var_ScratchMemory

var_A:
db 0

var_B:
dw 0

org var_ScratchMemory

var_C:
dw 0

var_D:
db 0

var_E:
db 0

By Grauw

Ascended (10821)

Grauw's picture

22-01-2019, 09:45

That is legal, but probably doesn’t do exactly what you want, because it will output 807H bytes.

Replace the variables with DS VIRTUAL to increment the address pointer without actually outputting any data:

var_ScratchMemory: equ #c000
    ds #800

    org var_ScratchMemory
var_A: ds virtual 1
var_B: ds virtual 2

    org var_ScratchMemory
var_C: ds virtual 2
var_D: ds virtual 1
var_E: ds virtual 1

Alternatively you could use macros to define offsets and use those, e.g.:

structure1: MACRO
    var_A: db 0
    var_B: dw 0
    ENDM

structure2: MACRO
    var_C: dw 0
    var_D: db 0
    var_E: db 0
    ENDM

    org #c000
var_ScratchMemory:
    ds #800

And then access it like:

    ld a,(var_ScratchMemory + structure1.var_A)
    ld ix,var_ScratchMemory
    ld e,(ix + structure2.var_E)

Etc.

By Wlcracks

Hero (572)

Wlcracks's picture

22-01-2019, 10:38

Awesome thanks again.

The code above is a part of "ram.asm"
The main code includes the ram.asm through:

RAM: ds VIRTUAL #1000

.....

SECTION RAM

include "RAM.asm"

ENDS

Is the "virtual" addition still needed as you describe in your first example?

By Wlcracks

Hero (572)

Wlcracks's picture

02-02-2019, 11:27

tok tok is this on LOL!

Ofcourse an other question
what is the "correct" way of loading the low byte of a 16bitter.

ld a, equ_VDP_pattern_name_table_screenblock1_address ; only lowest byte?

or

ld a, equ_VDP_pattern_name_table_screenblock1_address&#ff ; only lowest byte?

? i dont have a clue

high byte seems fine:

ld a, (equ_VDP_pattern_name_table_screenblock1_address>>8)|#40

Seems to work but to avoid future unexplainable bugs i would like to do it correctly.

Thanks again

By Wlcracks

Hero (572)

Wlcracks's picture

02-02-2019, 12:08

Is this a bug or a feature?

This works fine;

macro_Debug_BorderColor: MACRO ?color

ld a, ?color
out (equ_VDP99),a
ld a,#87
out (equ_VDP99),a

ENDM

but this is not happy;

macro_Debug_BorderColor: MACRO ?color

; Only when Debug has been enabled
IF ctime_Debug

ld a, ?color
out (equ_VDP99),a
ld a,#87
out (equ_VDP99),a

ENDIF

ENDM

Thanks again

By Grauw

Ascended (10821)

Grauw's picture

02-02-2019, 12:29

Wlcracks wrote:

what is the "correct" way of loading the low byte of a 16bitter.

Both are ok. In principle it’s this:

ld e,address & 0FFH       ; lsb
ld d,address >> 8         ; msb

But as you showed you can also omit the & 0FFH since the value is clipped down to 8 bits anyway. It’s a matter of preference, how explicit you want to be. Personally I prefer to be explicit because that works in all contexts (also calculations and 16-bit loads), and the pattern of >> 8 and & 0FFH is very recognisable so the intent is clear quickly.

p.s. Here’s what I do the other way around, composing a 16-bit value out of two 8-bit ones:

ld de,msb << 8 | lsb

By Grauw

Ascended (10821)

Grauw's picture

02-02-2019, 12:43

Wlcracks wrote:

Is this a bug or a feature?

Hm, that’s a bug, because it should work and I use the same pattern in my code and it works there. Can you send me an .asm file with a small test case where this error can be reproduced?

By Wlcracks

Hero (572)

Wlcracks's picture

02-02-2019, 14:11

Here you are;

macro.zip

Changing the the flag in enum.inc will get it assembling or not. Not sure its a bug could be an "nesting" or a wrong approach from my side.

Thank you again, also for the other answers, very helpful. Glass is spot on and a delight to work with.

By Grauw

Ascended (10821)

Grauw's picture

02-02-2019, 14:35

Thanks, I can reproduce it now. I’ve made a ticket for it in the issue tracker.

For now you can use the following workaround:

macro_BorderColor: MACRO ?color
color: equ ?color
    IF ctime_Debug
    ld a,color
    out (#99),a
    ld a,#87
    out (#99),a
    ENDIF
    ENDM

By Wlcracks

Hero (572)

Wlcracks's picture

02-02-2019, 14:35

awesome thanks!

By Wlcracks

Hero (572)

Wlcracks's picture

10-02-2019, 12:37

Ok another one.

I need to import some SC2.

	; SC2 include macro
	
macro_SC2_include: MACRO ?filename

data_?filename_SC2:	
	
data_?filename_PGT_B1:
	incbin "gfx/?filename.PGT_B1.mom"
	
data_?filename_CT_B1:
	incbin "gfx/?filename.CT_B1.mom"

data_?filename_PGT_B2:
	incbin "gfx/?filename.PGT_B2.mom"
	
data_?filename_CT_B2:
	incbin "gfx/?filename.CT_B2.mom"
	
data_?filename_PGT_B3:
	incbin "gfx/?filename.PGT_B3.mom"
	
data_?filename_CT_B3:
	incbin "gfx/?filename.CT_B3.mom"
	
	ENDM

Glass doesnt like this. Please what is the correct syntax for incbin?

By Grauw

Ascended (10821)

Grauw's picture

10-02-2019, 17:35

The macros in Glass are not treated like preprocessing directives as in other assemblers, so variables can’t be “pasted” into labels or strings like that.

There are two things here, firstly the string concatenation, in db pseudoinstructions you can combine strings with the , operator but that doesn’t work for incbin at the moment. I should add that, shouldn’t even be very difficult, so I’ve made an issue for it. I don’t think I’ll have time for it before the Nijmegen fair next week, but I should be able to after.

Secondly, labels can’t be built from strings. However they do exist in the scope of the macro, so you can define the macro like this and access the fields inside as a structure:

macro_SC2_include: MACRO ?filename
SC2:
PGT_B1:
    incbin "gfx/",?filename,".PGT_B1.mom"
CT_B1:
    incbin "gfx/",?filename,".CT_B1.mom"
PGT_B2:
    incbin "gfx/",?filename,".PGT_B2.mom"
CT_B2:
    incbin "gfx/",?filename,".CT_B2.mom"
PGT_B3:
    incbin "gfx/",?filename,".PGT_B3.mom"
CT_B3:
    incbin "gfx/",?filename,".CT_B3.mom"
    ENDM

Field: macro_SC2_include "field"
House: macro_SC2_include "house"
Basement: macro_SC2_include "basement"

And then access the fields as follows:

    ld hl,Field.PGT_B1
    ld de,House.CT_B3
    ld ix,Basement.SC2

Etc.

But as said, the string concatenation doesn’t work yet, so until I implement that you’d need some other way. One option is to not use a macro for this for now and copy/paste the code that’s currently in it. Alternatively you could consider though to concatenate the six files into one so they can be included in one go, and make a macro just for the offsets in it:

macro_SC2_include: MACRO ?filename
SC2:
    incbin ?filename
PGT_B1: equ SC2
CT_B1: equ PGT_B1 + 2048
PGT_B2: equ CT_B1 + 2048
CT_B2: equ PGT_B2 + 2048
PGT_B3: equ CT_B2 + 2048
CT_B3: equ PGT_B3 + 2048
    ENDM

Field: macro_SC2_include "gfx/field.mom"

By Wlcracks

Hero (572)

Wlcracks's picture

10-02-2019, 20:38

Great tips and explaning. I don't want to stress you out, i was wondering if there was a way. These macros are addicting. Thanks again for the help!

By Wlcracks

Hero (572)

Wlcracks's picture

24-02-2019, 12:41

Having fun in glass.
But it would be cool to have some kind of general reporting.
WARNING could work
But just a general PRINT
and the possibility to do something like:
PRINT ?define_LastRomByte
PRINT ?code_Lenght
or PRINT "Codelenght: "?code_Lenght
would be awesome!
I know the .sym is there but (calculated) equ's are not in the file.
I guess this could be an easy one since WARNING is already there??
When you have time? Sometime? Wink