Skip to content

Commit

Permalink
initial support for asMSX dialect, fixed many bugs concerning macros,…
Browse files Browse the repository at this point in the history
… added support for pragma directives in comments to prevent optimizations
  • Loading branch information
santiontanon committed Jul 2, 2020
1 parent 45d985c commit 31f2815
Show file tree
Hide file tree
Showing 21 changed files with 697 additions and 125 deletions.
2 changes: 1 addition & 1 deletion data/pbo-patterns.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
; Author: Santiago Ontañón; - Patterns are separated by a blank line; - Patterns suggested by others are credited to them below. ; - If you do a pull request with more patterns, please credit yourself in a comment before the pattern.; thanks to pgimeno for noticing the needed constraintpattern: jp<=0: cp ?const11: jp c,?const22: jp z,?const2replacement:0: cp ?const1+11: jp c,?const2constraints:notEqual(?const1,255); thanks to pgimeno for noticing the needed constraintpattern: jr<=0: cp ?const11: jr c,?const22: jr z,?const2replacement:0: cp ?const1+11: jr c,?const2constraints:notEqual(?const1,255)pattern: cp 0 -> or a0: cp 0replacement:0: or apattern: cp 1 -> dec a0: cp 1replacement:0: dec aconstraints:regNotUsed(0,A)pattern: cp 255 -> inc a0: cp 255replacement:0: inc aconstraints:regNotUsed(0,A)pattern: ld a,0 -> xor a0: ld a,0replacement:0: xor aconstraints:flagsNotUsed(0,S,Z,H,P/V,N,C)pattern: 3*srl -> 3*rrca+and0: srl a1: srl a2: srl areplacement:0: rrca1: rrca2: rrca2: and #1fpattern: unused ld r,value0: ld ?reg,?anyreplacement:constraints:regNotUsed(0,?reg)pattern: pop-transfer-bc-de0: pop bc1: ld d,b2: ld e,creplacement:0: pop deconstraints:regNotUsed(2,bc)pattern: pop-transfer-bc-hl0: pop bc1: ld h,b2: ld l,creplacement:0: pop hlconstraints:regNotUsed(2,bc)pattern: inc (nn)0: ld a,(?const1)1: inc a2: ld (?const1),areplacement:0: ld hl,?const11: inc (hl)constraints:regNotUsed(2,A,HL)pattern: inc (nn) [2]0: ld a,(?const1)1: inc a2: ld (?const1),areplacement:0: ld hl,?const11: inc (hl)2: ld a,(hl)constraints:regNotUsed(2,HL)pattern: b,c -> ld bc0: ld b,?const11: ld c,?const2replacement:0: ld bc,?const2 + 256 * ?const1pattern: ld (hl),const0: ld a,?const11: ld (hl),areplacement:0: ld (hl),?const1constraints:regNotUsed(1,A)pattern: djnz0: dec b1: jr nz,?const1replacement:0: djnz ?const1pattern: ldi0: ld a,(hl)1: ld (de),a2: inc hl3: inc dereplacement:0: ldiconstraints:regNotUsed(3,A,BC); thanks to pgimeno for suggesting this permutationpattern: ldi0: ld a,(hl)2: inc hl1: ld (de),a3: inc dereplacement:0: ldiconstraints:regNotUsed(3,A,BC)pattern: ldi0: ld a,(hl)1: ld (de),a3: inc de2: inc hlreplacement:0: ldiconstraints:regNotUsed(3,A,BC)pattern: ldi+bc0: ld a,(hl)1: ld (de),a2: inc hl3: inc dereplacement:0: ldi1: inc bcconstraints:regNotUsed(3,A); thanks to pgimeno for suggesting this permutationpattern: ldi+bc0: ld a,(hl)2: inc hl1: ld (de),a3: inc dereplacement:0: ldi1: inc bcconstraints:regNotUsed(3,A)pattern: ldi+bc0: ld a,(hl)1: ld (de),a3: inc de2: inc hlreplacement:0: ldi1: inc bcconstraints:regNotUsed(3,A) pattern: sbc->add0: ld de,?const11: or a2: sbc hl,dereplacement:0: ld de,-?const11: add hl,de ; thanks to grauw for suggesting this variationpattern: sbc->add0: ld de,?const11: and a2: sbc hl,dereplacement:0: ld de,-?const11: add hl,de pattern: neg->cpl0: neg1: add a,?const1replacement:0: cpl1: add a,?const1+1pattern: xor->cpl0: xor 255replacement:0: cplconstraints:flagNotUsed(0,S,Z,P/V,C)pattern: neg->sub0: ld a,b1: negreplacement:0: xor a1: sub bpattern: sub-neg0: ld a,?reg1: sub ?const2: negreplacement:0: ld a,?const1: sub ?regpattern: hl-left-shift0: sla l1: rl hreplacement:0: add hl,hlconstraints:flagsNotUsed(1,S,Z,P/V,C); thanks to grauw for suggesting this patternpattern: sla a -> add a,a0: sla areplacement:0: add a,aconstraints:flagsNotUsed(0,H,P/V)
; Author: Santiago Ontañón; - Patterns are separated by a blank line; - Patterns suggested by others are credited to them below. ; - If you do a pull request with more patterns, please credit yourself in a comment before the pattern.; thanks to pgimeno for noticing the needed constraintpattern: jp<=0: cp ?const11: jp c,?const22: jp z,?const2replacement:0: cp ?const1+11: jp c,?const2constraints:notEqual(?const1,255); thanks to pgimeno for noticing the needed constraintpattern: jr<=0: cp ?const11: jr c,?const22: jr z,?const2replacement:0: cp ?const1+11: jr c,?const2constraints:notEqual(?const1,255)pattern: cp 0 -> or a0: cp 0replacement:0: or apattern: cp 1 -> dec a0: cp 1replacement:0: dec aconstraints:regNotUsed(0,A)pattern: cp 255 -> inc a0: cp 255replacement:0: inc aconstraints:regNotUsed(0,A)pattern: ld a,0 -> xor a0: ld a,0replacement:0: xor aconstraints:flagsNotUsed(0,S,Z,H,P/V,N,C)pattern: 3*srl -> 3*rrca+and0: srl a1: srl a2: srl areplacement:0: rrca1: rrca2: rrca2: and #1fpattern: unused ld r,value0: ld ?reg,?anyreplacement:constraints:regNotUsed(0,?reg)pattern: pop-transfer-bc-de0: pop bc1: ld d,b2: ld e,creplacement:0: pop deconstraints:regNotUsed(2,bc)pattern: pop-transfer-bc-hl0: pop bc1: ld h,b2: ld l,creplacement:0: pop hlconstraints:regNotUsed(2,bc)pattern: inc (nn)0: ld a,(?const1)1: inc a2: ld (?const1),areplacement:0: ld hl,?const11: inc (hl)constraints:regNotUsed(2,A,HL)pattern: inc (nn) [2]0: ld a,(?const1)1: inc a2: ld (?const1),areplacement:0: ld hl,?const11: inc (hl)2: ld a,(hl)constraints:regNotUsed(2,HL)pattern: b,c -> ld bc0: ld b,?const11: ld c,?const2replacement:0: ld bc,?const2 + 256 * ?const1pattern: c,b -> ld bc0: ld c,?const21: ld b,?const1replacement:0: ld bc,?const2 + 256 * ?const1pattern: d,e -> ld de0: ld d,?const11: ld e,?const2replacement:0: ld de,?const2 + 256 * ?const1pattern: e,d -> ld de0: ld e,?const21: ld d,?const1replacement:0: ld de,?const2 + 256 * ?const1pattern: h,l -> ld hl0: ld h,?const11: ld l,?const2replacement:0: ld hl,?const2 + 256 * ?const1pattern: l,h -> ld hl0: ld l,?const21: ld h,?const1replacement:0: ld hl,?const2 + 256 * ?const1pattern: ld (hl),const0: ld a,?const11: ld (hl),areplacement:0: ld (hl),?const1constraints:regNotUsed(1,A)pattern: djnz0: dec b1: jr nz,?const1replacement:0: djnz ?const1pattern: ldi0: ld a,(hl)1: ld (de),a2: inc hl3: inc dereplacement:0: ldiconstraints:regNotUsed(3,A,BC); thanks to pgimeno for suggesting this permutationpattern: ldi0: ld a,(hl)2: inc hl1: ld (de),a3: inc dereplacement:0: ldiconstraints:regNotUsed(3,A,BC)pattern: ldi0: ld a,(hl)1: ld (de),a3: inc de2: inc hlreplacement:0: ldiconstraints:regNotUsed(3,A,BC)pattern: ldi+bc0: ld a,(hl)1: ld (de),a2: inc hl3: inc dereplacement:0: ldi1: inc bcconstraints:regNotUsed(3,A); thanks to pgimeno for suggesting this permutationpattern: ldi+bc0: ld a,(hl)2: inc hl1: ld (de),a3: inc dereplacement:0: ldi1: inc bcconstraints:regNotUsed(3,A)pattern: ldi+bc0: ld a,(hl)1: ld (de),a3: inc de2: inc hlreplacement:0: ldi1: inc bcconstraints:regNotUsed(3,A) pattern: sbc->add0: ld de,?const11: or a2: sbc hl,dereplacement:0: ld de,-?const11: add hl,de ; thanks to grauw for suggesting this variationpattern: sbc->add0: ld de,?const11: and a2: sbc hl,dereplacement:0: ld de,-?const11: add hl,de pattern: neg->cpl0: neg1: add a,?const1replacement:0: cpl1: add a,?const1+1pattern: xor->cpl0: xor 255replacement:0: cplconstraints:flagNotUsed(0,S,Z,P/V,C)pattern: neg->sub0: ld a,b1: negreplacement:0: xor a1: sub bpattern: sub-neg0: ld a,?reg1: sub ?const2: negreplacement:0: ld a,?const1: sub ?regpattern: hl-left-shift0: sla l1: rl hreplacement:0: add hl,hlconstraints:flagsNotUsed(1,S,Z,P/V,C); thanks to grauw for suggesting this patternpattern: sla a -> add a,a0: sla areplacement:0: add a,aconstraints:flagsNotUsed(0,H,P/V)
Expand Down
17 changes: 17 additions & 0 deletions data/tests/test23.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
; Test case: making sure pre-processor parses code correctly (this case didn't work in an early version)


IFDEF symbol
ld b,1
ld c,2
ELSE
ld b,3
ld c,4
ENDIF
ld (symbol),bc

end:
jp end

symbol:
dw 0
17 changes: 17 additions & 0 deletions data/tests/test24.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
; Test case: check code annotations work


IFDEF symbol
ld b,1 ; mdl:no-opt
ld c,2
ELSE
ld b,3
ld c,4
ENDIF
ld (symbol),bc

end:
jp end

symbol:
dw 0
2 changes: 1 addition & 1 deletion data/z80-instruction-set.tsv

Large diffs are not rendered by default.

37 changes: 30 additions & 7 deletions src/cl/MDLConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
import parser.ExpressionParser;
import parser.LineParser;
import parser.PreProcessor;
import parser.dialects.ASMSXDialect;
import parser.dialects.GlassDialect;
import parser.dialects.Dialect;

public class MDLConfig {

// constants:
public static int HEX_STYLE_HASH = 0;
public static int HEX_STYLE_HASH_CAPS = 1;
Expand All @@ -30,6 +30,7 @@ public class MDLConfig {

public static int DIALECT_MDL = 0;
public static int DIALECT_GLASS = 1;
public static int DIALECT_ASMSX = 2;

// arguments:
public String inputFile = null;
Expand All @@ -48,6 +49,10 @@ public class MDLConfig {

public boolean warningLabelWithoutColon = true;
public boolean warningJpHlWithParenthesis = true;

// code annotations:
public String PRAGMA_NO_OPTIMIZATION = "mdl:no-opt";


// utils:
public MDLLogger logger;
Expand All @@ -60,23 +65,24 @@ public class MDLConfig {
List<MDLWorker> workers = new ArrayList<>();


public static String docString
public String docString
= "MDL (A Z80 assembler optimizer) by Santiago Ontañón (Brain Games, 2020)\n"
+ "https://github.com/santiontanon/mdlz80optimizer\n"
+ "\n"
+ "arguments: <input assembler file> [options]\n"
+ " -cpu <type>: to select a different CPU (z80/z80msx/z80cpc) (default: z80msx).\n"
+ " -dialect <type>: to allow parsing different assembler dialects (mdl/glass) (default: mdl, which supports some basic code idioms common to various assemblers).\n"
+ " -dialect <type>: to allow parsing different assembler dialects (mdl/glass/asmsx) (default: mdl, which supports some basic code idioms common to various assemblers).\n"
+ " Note that even when selecting a dialect, not all syntax of a given assembler might be supported.\n"
+ " -I <folder>: adds a folder to the include search path.\n"
+ " -debug: turns on debug messages.\n"
+ " -warn-off-labelnocolon: turns off warnings for not placing colons after labels.\n"
+ " -warn-off-jp(rr): turns off warnings for using confusing 'jp (hl)' instead of 'jp hl' (this is turned off by default in dialects that do not support this).\n"
+ " -hex#: hex numbers render like #ffff (default).\n"
+ " -HEX#: hex numbers render like #FFFF.\n"
+ " -hexh: hex numbers render like 0ffffh.\n"
+ " -HEXH: hex numbers render like 0FFFFh.\n"
+ " -HEX#: hex numbers render like #FFFF.\n"
+ " -hexh: hex numbers render like 0ffffh.\n"
+ " -HEXH: hex numbers render like 0FFFFh.\n"
+ " -+bin: includes binary files (incbin) in the output analyses.\n"
+ " -no-opt-pragma <value>: changes the pragma to be inserted in a comment on a line to prevent optimizing it (default: " + PRAGMA_NO_OPTIMIZATION + ")"
+ "\n";


Expand Down Expand Up @@ -156,6 +162,11 @@ public boolean parse(String argsArray[]) throws Exception {
dialect = DIALECT_GLASS;
dialectParser = new GlassDialect(this);
break;
case "asmsx":
dialect = DIALECT_ASMSX;
dialectParser = new ASMSXDialect(this);
warningJpHlWithParenthesis = false;
break;
default:
error("Unrecognized dialect " + dialectString);
return false;
Expand Down Expand Up @@ -216,6 +227,16 @@ public boolean parse(String argsArray[]) throws Exception {
args.remove(0);
break;

case "-no-opt-pragma":
if (args.size()>=2) {
args.remove(0);
PRAGMA_NO_OPTIMIZATION = args.remove(0);
} else {
error("Missing pragma after " + arg);
return false;
}
break;


default:
{
Expand Down Expand Up @@ -253,7 +274,9 @@ public boolean parse(String argsArray[]) throws Exception {
lineParser = new LineParser(this, codeBaseParser);
expressionParser = new ExpressionParser(this);
opParser = new CPUOpParser(opSpecParser.parseSpecs(), this);


if (dialectParser !=null) dialectParser.init(this);

return verify();
}

Expand Down
55 changes: 52 additions & 3 deletions src/code/Expression.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,17 @@ public class Expression {
public static final int EXPRESSION_RSHIFT = 21;
public static final int EXPRESSION_BITOR = 22;
public static final int EXPRESSION_BITAND = 23;
public static final int EXPRESSION_BITNEGATION = 24;
public static final int EXPRESSION_BITXOR = 25;

// indexed by the numbers above:
public static final int OPERATOR_PRECEDENCE[] = {
-1, -1, -1, -1, -1,
-1, 6, 6, 5, 5,
5, 13, 11, 10, 9,
9, 9, 9, 10, 16,
7, 7, 11, 13};
7, 7, 11, 13, 3,
12};

public int type;
public int numericConstant;
Expand Down Expand Up @@ -272,6 +275,20 @@ public Integer evaluate(SourceStatement s, CodeBase code, boolean silent)
if (v1 == null || v2 == null) return null;
return v1 & v2;
}
case EXPRESSION_BITNEGATION:
{
Integer v = args.get(0).evaluate(s, code, silent);
if (v == null) return null;
return ~v;
}
case EXPRESSION_BITXOR:
{
if (args.size() != 2) return null;
Integer v1 = args.get(0).evaluate(s, code, silent);
Integer v2 = args.get(1).evaluate(s, code, silent);
if (v1 == null || v2 == null) return null;
return v1 ^ v2;
}

}

Expand Down Expand Up @@ -518,7 +535,28 @@ public String toString()
}
return str;
}

case EXPRESSION_BITNEGATION:
if (args.get(0).type == EXPRESSION_REGISTER_OR_FLAG ||
args.get(0).type == EXPRESSION_NUMERIC_CONSTANT ||
args.get(0).type == EXPRESSION_STRING_CONSTANT ||
args.get(0).type == EXPRESSION_PARENTHESIS ||
args.get(0).type == EXPRESSION_SYMBOL) {
return "~" + args.get(0).toString();
} else {
return "~(" + args.get(0).toString() + ")";
}
case EXPRESSION_BITXOR:
{
String str = null;
for(Expression arg:args) {
if (str == null) {
str = arg.toString();
} else {
str += " ^ " + arg.toString();
}
}
return str;
}
default:
return "<UNSUPPORTED TYPE "+type+">";
}
Expand Down Expand Up @@ -563,7 +601,9 @@ public boolean evaluatesToNumericConstant()
type == EXPRESSION_LSHIFT ||
type == EXPRESSION_RSHIFT ||
type == EXPRESSION_BITOR ||
type == EXPRESSION_BITAND) {
type == EXPRESSION_BITAND ||
type == EXPRESSION_BITNEGATION ||
type == EXPRESSION_BITXOR) {
for(Expression arg:args) {
if (!arg.evaluatesToNumericConstant()) return false;
}
Expand Down Expand Up @@ -625,6 +665,15 @@ public static Expression negationExpression(Expression arg)
return exp;
}


public static Expression bitNegationExpression(Expression arg)
{
Expression exp = new Expression(EXPRESSION_BITNEGATION);
exp.args = new ArrayList<>();
exp.args.add(arg);
return exp;
}


public static Expression parenthesisExpression(Expression arg)
{
Expand Down
27 changes: 24 additions & 3 deletions src/parser/CPUOpParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ public CPUOpParser(List<CPUOpSpec> a_opSpecs, MDLConfig a_config) {

public boolean isOpName(String name)
{
return opSpecHash.containsKey(name);
return opSpecHash.containsKey(name.toLowerCase());
}


public List<CPUOpSpec> getOpSpecs(String name)
{
List<CPUOpSpec> l = opSpecHash.get(name);
List<CPUOpSpec> l = opSpecHash.get(name.toLowerCase());
if (l != null) return l;
return new ArrayList<>();
}
Expand All @@ -54,10 +54,31 @@ public List<CPUOpSpec> getOpSpecs(String name)
public CPUOp parseOp(String a_op, List<Expression> a_args, SourceStatement s, CodeBase code)
{
CPUOp op;
for(CPUOpSpec opSpec:getOpSpecs(a_op)) {
List<CPUOpSpec> candidates = getOpSpecs(a_op);
for(CPUOpSpec opSpec:candidates) {
op = parseOp(a_op, opSpec, a_args, s, code);
if (op != null) return op;
}
if (candidates != null && !candidates.isEmpty()) {
// try to see if any of the arguments was a constant with parenthesis that had been interpreted
// as an indirection:
boolean anyChange = false;
for(int i = 0;i<a_args.size();i++) {
Expression arg = a_args.get(i);
if (arg.evaluatesToNumericConstant() && arg.type == Expression.EXPRESSION_PARENTHESIS) {
a_args.set(i, arg.args.get(0));
anyChange = true;
}
}
if (anyChange) {
// try again!
for(CPUOpSpec opSpec:candidates) {
op = parseOp(a_op, opSpec, a_args, s, code);
if (op != null) return op;
}
}
}

return null;
}

Expand Down
Loading

0 comments on commit 31f2815

Please sign in to comment.