2019-05-02 22:45:40 +00:00
|
|
|
|
/*
|
|
|
|
|
* Copyright 2019 faddenSoft
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Web.Script.Serialization;
|
|
|
|
|
|
|
|
|
|
using Asm65;
|
|
|
|
|
using CommonUtil;
|
|
|
|
|
|
2019-07-20 20:28:10 +00:00
|
|
|
|
namespace SourceGen {
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Data pseudo-op formatter. Long operands, notably strings and dense hex blocks, may
|
|
|
|
|
/// be broken across multiple lines.
|
|
|
|
|
///
|
|
|
|
|
/// Assembler output will use Opcode and Operand, emitting multiple lines of ASC, HEX,
|
|
|
|
|
/// etc. The display list may treat it as a single item that is split across
|
|
|
|
|
/// multiple lines.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class PseudoOp {
|
|
|
|
|
/// <summary>
|
2019-08-17 23:59:08 +00:00
|
|
|
|
/// One piece of the pseudo-instruction.
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
public struct PseudoOut {
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Opcode. Same for all entries in the list.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string Opcode { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Formatted form of this piece of the operand.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string Operand { get; set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Copy constructor.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public PseudoOut(PseudoOut src) {
|
|
|
|
|
Opcode = src.Opcode;
|
|
|
|
|
Operand = src.Operand;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-17 23:59:08 +00:00
|
|
|
|
#region PseudoOpNames
|
|
|
|
|
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// <summary>
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
/// Pseudo-op name collection. Instances are immutable.
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
public class PseudoOpNames {
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
public string EquDirective { get; private set; }
|
2019-08-29 19:14:47 +00:00
|
|
|
|
public string VarDirective { get; private set; }
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
public string OrgDirective { get; private set; }
|
|
|
|
|
public string RegWidthDirective { get; private set; }
|
2020-07-09 22:17:47 +00:00
|
|
|
|
public string DataBankDirective { get; private set; }
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
|
|
|
|
|
public string DefineData1 { get; private set; }
|
|
|
|
|
public string DefineData2 { get; private set; }
|
|
|
|
|
public string DefineData3 { get; private set; }
|
|
|
|
|
public string DefineData4 { get; private set; }
|
|
|
|
|
public string DefineBigData2 { get; private set; }
|
|
|
|
|
public string DefineBigData3 { get; private set; }
|
|
|
|
|
public string DefineBigData4 { get; private set; }
|
|
|
|
|
public string Fill { get; private set; }
|
|
|
|
|
public string Dense { get; private set; }
|
2019-10-19 03:28:02 +00:00
|
|
|
|
public string Junk { get; private set; }
|
|
|
|
|
public string Align { get; private set; }
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
public string StrGeneric { get; private set; }
|
|
|
|
|
public string StrReverse { get; private set; }
|
|
|
|
|
public string StrLen8 { get; private set; }
|
|
|
|
|
public string StrLen16 { get; private set; }
|
|
|
|
|
public string StrNullTerm { get; private set; }
|
|
|
|
|
public string StrDci { get; private set; }
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-08-29 19:14:47 +00:00
|
|
|
|
/// Constructs an empty PseudoOp, for deserialization.
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
/// </summary>
|
2019-08-29 19:14:47 +00:00
|
|
|
|
public PseudoOpNames() : this(new Dictionary<string, string>()) { }
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Constructor. Pass in a dictionary with name/value pairs. Unknown names
|
|
|
|
|
/// will be ignored, missing names will be assigned the empty string.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="dict">Dictionary of values.</param>
|
|
|
|
|
public PseudoOpNames(Dictionary<string, string> dict) {
|
|
|
|
|
foreach (PropertyInfo prop in GetType().GetProperties()) {
|
|
|
|
|
dict.TryGetValue(prop.Name, out string value);
|
|
|
|
|
if (value == null) {
|
|
|
|
|
value = string.Empty;
|
|
|
|
|
}
|
|
|
|
|
prop.SetValue(this, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool operator ==(PseudoOpNames a, PseudoOpNames b) {
|
|
|
|
|
if (ReferenceEquals(a, b)) {
|
|
|
|
|
return true; // same object, or both null
|
|
|
|
|
}
|
|
|
|
|
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) {
|
|
|
|
|
return false; // one is null
|
|
|
|
|
}
|
|
|
|
|
return a.EquDirective == b.EquDirective &&
|
2019-08-29 19:14:47 +00:00
|
|
|
|
a.VarDirective == b.VarDirective &&
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
a.OrgDirective == b.OrgDirective &&
|
|
|
|
|
a.RegWidthDirective == b.RegWidthDirective &&
|
2020-07-09 22:17:47 +00:00
|
|
|
|
a.DataBankDirective == b.DataBankDirective &&
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
a.DefineData1 == b.DefineData1 &&
|
|
|
|
|
a.DefineData2 == b.DefineData2 &&
|
|
|
|
|
a.DefineData3 == b.DefineData3 &&
|
|
|
|
|
a.DefineData4 == b.DefineData4 &&
|
|
|
|
|
a.DefineBigData2 == b.DefineBigData2 &&
|
|
|
|
|
a.DefineBigData3 == b.DefineBigData3 &&
|
|
|
|
|
a.DefineBigData4 == b.DefineBigData4 &&
|
|
|
|
|
a.Fill == b.Fill &&
|
|
|
|
|
a.Dense == b.Dense &&
|
2019-10-19 03:28:02 +00:00
|
|
|
|
a.Junk == b.Junk &&
|
|
|
|
|
a.Align == b.Align &&
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
a.StrGeneric == b.StrGeneric &&
|
|
|
|
|
a.StrReverse == b.StrReverse &&
|
|
|
|
|
a.StrLen8 == b.StrLen8 &&
|
|
|
|
|
a.StrLen16 == b.StrLen16 &&
|
|
|
|
|
a.StrNullTerm == b.StrNullTerm &&
|
|
|
|
|
a.StrDci == b.StrDci;
|
|
|
|
|
}
|
|
|
|
|
public static bool operator !=(PseudoOpNames a, PseudoOpNames b) {
|
|
|
|
|
return !(a == b);
|
|
|
|
|
}
|
|
|
|
|
public override bool Equals(object obj) {
|
|
|
|
|
return obj is PseudoOpNames && this == (PseudoOpNames)obj;
|
|
|
|
|
}
|
|
|
|
|
public override int GetHashCode() {
|
|
|
|
|
// should be enough
|
|
|
|
|
return (EquDirective == null ? 0 : EquDirective.GetHashCode()) ^
|
|
|
|
|
(OrgDirective == null ? 0 : OrgDirective.GetHashCode()) ^
|
|
|
|
|
(DefineData1 == null ? 0 : DefineData1.GetHashCode()) ^
|
|
|
|
|
(Fill == null ? 0 : Fill.GetHashCode());
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
|
|
|
|
public string GetDefineData(int width) {
|
|
|
|
|
switch (width) {
|
|
|
|
|
case 1: return DefineData1;
|
|
|
|
|
case 2: return DefineData2;
|
|
|
|
|
case 3: return DefineData3;
|
|
|
|
|
case 4: return DefineData4;
|
|
|
|
|
default: Debug.Assert(false); return ".?!!";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public string GetDefineBigData(int width) {
|
|
|
|
|
switch (width) {
|
|
|
|
|
case 1: return DefineData1;
|
|
|
|
|
case 2: return DefineBigData2;
|
|
|
|
|
case 3: return DefineBigData3;
|
|
|
|
|
case 4: return DefineBigData4;
|
|
|
|
|
default: Debug.Assert(false); return ".!!?";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-08-18 00:22:14 +00:00
|
|
|
|
/// Merges the non-null, non-empty strings.
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// </summary>
|
2019-08-18 00:22:14 +00:00
|
|
|
|
public static PseudoOpNames Merge(PseudoOpNames basePon, PseudoOpNames newPon) {
|
|
|
|
|
Dictionary<string, string> baseDict = PropsToDict(basePon);
|
|
|
|
|
Dictionary<string, string> newDict = PropsToDict(newPon);
|
|
|
|
|
|
|
|
|
|
foreach (KeyValuePair<string, string> kvp in newDict) {
|
|
|
|
|
if (string.IsNullOrEmpty(kvp.Value)) {
|
2019-05-02 22:45:40 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2019-08-18 00:22:14 +00:00
|
|
|
|
baseDict[kvp.Key] = kvp.Value;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-18 00:22:14 +00:00
|
|
|
|
return new PseudoOpNames(baseDict);
|
|
|
|
|
}
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
|
2019-08-18 00:22:14 +00:00
|
|
|
|
private static Dictionary<string, string> PropsToDict(PseudoOpNames pon) {
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
Dictionary<string, string> dict = new Dictionary<string, string>();
|
2019-08-18 00:22:14 +00:00
|
|
|
|
foreach (PropertyInfo prop in pon.GetType().GetProperties()) {
|
|
|
|
|
string value = (string)prop.GetValue(pon);
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
if (!string.IsNullOrEmpty(value)) {
|
|
|
|
|
dict[prop.Name] = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-08-18 00:22:14 +00:00
|
|
|
|
return dict;
|
|
|
|
|
}
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
|
2019-08-18 00:22:14 +00:00
|
|
|
|
public string Serialize() {
|
|
|
|
|
// This results in a JSON-encoded string being stored in a JSON-encoded file,
|
|
|
|
|
// which means a lot of double-quote escaping. We could do something here
|
|
|
|
|
// that stored more nicely but it doesn't seem worth the effort.
|
|
|
|
|
JavaScriptSerializer ser = new JavaScriptSerializer();
|
|
|
|
|
Dictionary<string, string> dict = PropsToDict(this);
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
return ser.Serialize(dict);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static PseudoOpNames Deserialize(string cereal) {
|
|
|
|
|
JavaScriptSerializer ser = new JavaScriptSerializer();
|
|
|
|
|
try {
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
Dictionary<string, string> dict =
|
|
|
|
|
ser.Deserialize<Dictionary<string, string>>(cereal);
|
|
|
|
|
return new PseudoOpNames(dict);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
} catch (Exception ex) {
|
|
|
|
|
Debug.WriteLine("PseudoOpNames deserialization failed: " + ex.Message);
|
|
|
|
|
return new PseudoOpNames();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
/// Returns a PseudoOpNames instance with some reasonable defaults for on-screen display.
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// </summary>
|
2019-08-29 19:14:47 +00:00
|
|
|
|
public static PseudoOpNames DefaultPseudoOpNames { get; } =
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
new PseudoOpNames(new Dictionary<string, string> {
|
|
|
|
|
{ "EquDirective", ".eq" },
|
2019-08-29 19:14:47 +00:00
|
|
|
|
{ "VarDirective", ".var" },
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
{ "OrgDirective", ".org" },
|
|
|
|
|
{ "RegWidthDirective", ".rwid" },
|
2020-07-09 22:17:47 +00:00
|
|
|
|
{ "DataBankDirective", ".dbank" },
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
|
|
|
|
|
{ "DefineData1", ".dd1" },
|
|
|
|
|
{ "DefineData2", ".dd2" },
|
|
|
|
|
{ "DefineData3", ".dd3" },
|
|
|
|
|
{ "DefineData4", ".dd4" },
|
|
|
|
|
{ "DefineBigData2", ".dbd2" },
|
|
|
|
|
{ "DefineBigData3", ".dbd3" },
|
|
|
|
|
{ "DefineBigData4", ".dbd4" },
|
|
|
|
|
{ "Fill", ".fill" },
|
|
|
|
|
{ "Dense", ".bulk" },
|
2019-10-19 03:28:02 +00:00
|
|
|
|
{ "Junk", ".junk" },
|
|
|
|
|
{ "Align", ".align" },
|
Various improvements
The PseudoOpNames class is increasingly being used in situations
where mutability is undesirable. This change makes instances
immutable, eliminating the Copy() method and adding a constructor
that takes a Dictionary. The serialization code now operates on a
Dictionary instead of the class properties, but the JSON encoding is
identical, so this doesn't invalidate app settings file data.
Added an equality test to PseudoOpNames. In LineListGen, don't
reset the line list if the names haven't actually changed.
Use a table lookup for C64 character conversions. I figure that
should be faster than multiple conditionals on a modern x64 system.
Fixed a 64tass generator issue where we tried to query project
properties in a call that might not have a project available
(specifically, getting FormatConfig values out of the generator for
use in the "quick set" buttons for Display Format).
Fixed a regression test harness issue where, if the assembler reported
success but didn't actually generate output, an exception would be
thrown that halted the tests.
Increased the width of text entry fields on the Pseudo-Op tab of app
settings. The previous 8-character limit wasn't wide enough to hold
ACME's "!pseudopc". Also, use TrimEnd() to remove trailing spaces
(leading spaces are still allowed).
In the last couple of months, Win10 started stalling for a fraction
of a second when executing assemblers. It doesn't do this every
time; mostly it happens if it has been a while since the assembler
was run. My guess is this has to do with changes to the built-in
malware scanner. Whatever the case, we now change the mouse pointer
to a wait cursor while updating the assembler version cache.
2019-08-17 18:14:05 +00:00
|
|
|
|
|
|
|
|
|
{ "StrGeneric", ".str" },
|
|
|
|
|
{ "StrReverse", ".rstr" },
|
|
|
|
|
{ "StrLen8", ".l1str" },
|
|
|
|
|
{ "StrLen16", ".l2str" },
|
|
|
|
|
{ "StrNullTerm", ".zstr" },
|
|
|
|
|
{ "StrDci", ".dstr" }
|
|
|
|
|
});
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-08-17 23:59:08 +00:00
|
|
|
|
#endregion PseudoOpNames
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Computes the number of lines of output required to hold the formatted output.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="formatter">Format definition.</param>
|
|
|
|
|
/// <param name="dfd">Data format descriptor.</param>
|
|
|
|
|
/// <returns>Line count.</returns>
|
2019-08-14 00:22:21 +00:00
|
|
|
|
public static int ComputeRequiredLineCount(Formatter formatter, PseudoOpNames opNames,
|
|
|
|
|
FormatDescriptor dfd, byte[] data, int offset) {
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
if (dfd.IsString) {
|
2019-08-17 23:59:08 +00:00
|
|
|
|
Debug.Assert(false); // shouldn't be calling here anymore
|
2019-08-14 00:22:21 +00:00
|
|
|
|
List<string> lines = FormatStringOp(formatter, opNames, dfd, data,
|
|
|
|
|
offset, out string popcode);
|
|
|
|
|
return lines.Count;
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-02 22:45:40 +00:00
|
|
|
|
switch (dfd.FormatType) {
|
|
|
|
|
case FormatDescriptor.Type.Default:
|
|
|
|
|
case FormatDescriptor.Type.NumericLE:
|
|
|
|
|
case FormatDescriptor.Type.NumericBE:
|
|
|
|
|
case FormatDescriptor.Type.Fill:
|
2019-10-19 03:28:02 +00:00
|
|
|
|
case FormatDescriptor.Type.Junk:
|
2019-05-02 22:45:40 +00:00
|
|
|
|
return 1;
|
|
|
|
|
case FormatDescriptor.Type.Dense: {
|
|
|
|
|
// no delimiter, two output bytes per input byte
|
2020-07-20 01:39:27 +00:00
|
|
|
|
int maxLen = formatter.OperandWrapLen;
|
2019-12-11 01:41:00 +00:00
|
|
|
|
int textLen = dfd.Length * formatter.CharsPerDenseByte;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
return (textLen + maxLen - 1) / maxLen;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
Debug.Assert(false);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Generates a pseudo-op statement for the specified data operation.
|
|
|
|
|
///
|
|
|
|
|
/// For most operations, only one output line will be generated. For larger items,
|
2019-08-17 23:59:08 +00:00
|
|
|
|
/// like dense hex, the value may be split into multiple lines. The sub-index
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// indicates which line should be formatted.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="formatter">Format definition.</param>
|
|
|
|
|
/// <param name="opNames">Table of pseudo-op names.</param>
|
|
|
|
|
/// <param name="symbolTable">Project symbol table.</param>
|
|
|
|
|
/// <param name="labelMap">Symbol label map. May be null.</param>
|
|
|
|
|
/// <param name="dfd">Data format descriptor.</param>
|
|
|
|
|
/// <param name="data">File data array.</param>
|
|
|
|
|
/// <param name="offset">Start offset.</param>
|
|
|
|
|
/// <param name="subIndex">For multi-line items, which line.</param>
|
|
|
|
|
public static PseudoOut FormatDataOp(Formatter formatter, PseudoOpNames opNames,
|
|
|
|
|
SymbolTable symbolTable, Dictionary<string, string> labelMap,
|
|
|
|
|
FormatDescriptor dfd, byte[] data, int offset, int subIndex) {
|
|
|
|
|
if (dfd == null) {
|
|
|
|
|
// should never happen
|
|
|
|
|
//Debug.Assert(false, "Null dfd at offset+" + offset.ToString("x6"));
|
|
|
|
|
PseudoOut failed = new PseudoOut();
|
|
|
|
|
failed.Opcode = failed.Operand = "!FAILED!+" + offset.ToString("x6");
|
|
|
|
|
return failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int length = dfd.Length;
|
|
|
|
|
Debug.Assert(length > 0);
|
|
|
|
|
|
|
|
|
|
// All outputs for a given offset show the same offset and length, even for
|
|
|
|
|
// multi-line items.
|
|
|
|
|
PseudoOut po = new PseudoOut();
|
|
|
|
|
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
if (dfd.IsString) {
|
2019-08-17 23:59:08 +00:00
|
|
|
|
Debug.Assert(false); // shouldn't be calling here anymore
|
2019-08-14 00:22:21 +00:00
|
|
|
|
List<string> lines = FormatStringOp(formatter, opNames, dfd, data,
|
|
|
|
|
offset, out string popcode);
|
|
|
|
|
po.Opcode = popcode;
|
|
|
|
|
po.Operand = lines[subIndex];
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
} else {
|
|
|
|
|
switch (dfd.FormatType) {
|
|
|
|
|
case FormatDescriptor.Type.Default:
|
|
|
|
|
if (length != 1) {
|
|
|
|
|
// This shouldn't happen.
|
|
|
|
|
Debug.Assert(false);
|
|
|
|
|
length = 1;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
po.Opcode = opNames.GetDefineData(length);
|
|
|
|
|
int operand = RawData.GetWord(data, offset, length, false);
|
|
|
|
|
po.Operand = formatter.FormatHexValue(operand, length * 2);
|
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.Type.NumericLE:
|
|
|
|
|
po.Opcode = opNames.GetDefineData(length);
|
|
|
|
|
operand = RawData.GetWord(data, offset, length, false);
|
2019-08-31 01:33:05 +00:00
|
|
|
|
po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap,
|
|
|
|
|
dfd, operand, length, FormatNumericOpFlags.None);
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.Type.NumericBE:
|
|
|
|
|
po.Opcode = opNames.GetDefineBigData(length);
|
|
|
|
|
operand = RawData.GetWord(data, offset, length, true);
|
2019-08-31 01:33:05 +00:00
|
|
|
|
po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap,
|
|
|
|
|
dfd, operand, length, FormatNumericOpFlags.None);
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.Type.Fill:
|
|
|
|
|
po.Opcode = opNames.Fill;
|
|
|
|
|
po.Operand = length + "," + formatter.FormatHexValue(data[offset], 2);
|
|
|
|
|
break;
|
2019-10-19 03:28:02 +00:00
|
|
|
|
case FormatDescriptor.Type.Junk:
|
|
|
|
|
if (dfd.FormatSubType != FormatDescriptor.SubType.None) {
|
|
|
|
|
po.Opcode = opNames.Align;
|
|
|
|
|
int alignPow = FormatDescriptor.AlignmentToPower(dfd.FormatSubType);
|
|
|
|
|
po.Operand = formatter.FormatHexValue(1 << alignPow, 2) +
|
|
|
|
|
" (" + length.ToString() + " bytes)";
|
|
|
|
|
} else {
|
|
|
|
|
po.Opcode = opNames.Junk;
|
|
|
|
|
po.Operand = length.ToString();
|
|
|
|
|
}
|
|
|
|
|
break;
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
case FormatDescriptor.Type.Dense: {
|
2020-07-20 01:39:27 +00:00
|
|
|
|
int maxPerLine = formatter.OperandWrapLen / formatter.CharsPerDenseByte;
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
offset += subIndex * maxPerLine;
|
|
|
|
|
length -= subIndex * maxPerLine;
|
|
|
|
|
if (length > maxPerLine) {
|
|
|
|
|
length = maxPerLine;
|
|
|
|
|
}
|
|
|
|
|
po.Opcode = opNames.Dense;
|
|
|
|
|
po.Operand = formatter.FormatDenseHex(data, offset, length);
|
|
|
|
|
//List<PseudoOut> outList = new List<PseudoOut>();
|
|
|
|
|
//GenerateTextLines(text, "", "", po, outList);
|
|
|
|
|
//po = outList[subIndex];
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
Debug.Assert(false);
|
|
|
|
|
po.Opcode = ".???";
|
|
|
|
|
po.Operand = "$" + data[offset].ToString("x2");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return po;
|
|
|
|
|
}
|
|
|
|
|
|
Allow explicit widths in project/platform symbols, part 1
The ability to give explicit widths to local variables worked out
pretty well, so we're going to try adding the same thing to project
and platform symbols.
The first step is to allow widths to be specified in platform files,
and set with the project symbol editor. The DefSymbol editor is
also used for local variables, so a bit of dancing is required.
For platform/project symbols the width is optional, and is totally
ignored for constants. (For variables, constants are used for the
StackRel args, so the width is meaningful and required.)
We also now show the symbol's type (address or constant) and width
in the listing. This gets really distracting when overused, so we
only show it when the width is explicitly set. The default width
is 1, which most things will be, so users can make an aesthetic
choice there. (The place where widths make very little sense is when
the symbol represents a code entry point, rather than a data item.)
The maximum width of a local variable is now 256, but it's not
allowed to overlap with other variables or run of the end of the
direct page. The maximum width of a platform/project symbol is
65536, with bank-wrap behavior TBD.
The local variable table editor now refers to stack-relative
constants as such, rather than simply "constant", to make it clear
that it's not just defining an 8-bit constant.
Widths have been added to a handful of Apple II platform defs.
2019-10-01 21:58:24 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Adds an additional annotation to an EQU directive, indicating whether the symbol
|
|
|
|
|
/// is a constant or an address, and (if address) how many bytes it spans.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="formatter">Formatter object.</param>
|
|
|
|
|
/// <param name="operand">Formatted operand string.</param>
|
|
|
|
|
/// <param name="defSym">Project/platform/variable symbol.</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static string AnnotateEquDirective(Formatter formatter, string operand,
|
|
|
|
|
DefSymbol defSym) {
|
|
|
|
|
string typeStr;
|
2019-10-23 04:27:49 +00:00
|
|
|
|
if (defSym.IsConstant) {
|
Allow explicit widths in project/platform symbols, part 1
The ability to give explicit widths to local variables worked out
pretty well, so we're going to try adding the same thing to project
and platform symbols.
The first step is to allow widths to be specified in platform files,
and set with the project symbol editor. The DefSymbol editor is
also used for local variables, so a bit of dancing is required.
For platform/project symbols the width is optional, and is totally
ignored for constants. (For variables, constants are used for the
StackRel args, so the width is meaningful and required.)
We also now show the symbol's type (address or constant) and width
in the listing. This gets really distracting when overused, so we
only show it when the width is explicitly set. The default width
is 1, which most things will be, so users can make an aesthetic
choice there. (The place where widths make very little sense is when
the symbol represents a code entry point, rather than a data item.)
The maximum width of a local variable is now 256, but it's not
allowed to overlap with other variables or run of the end of the
direct page. The maximum width of a platform/project symbol is
65536, with bank-wrap behavior TBD.
The local variable table editor now refers to stack-relative
constants as such, rather than simply "constant", to make it clear
that it's not just defining an 8-bit constant.
Widths have been added to a handful of Apple II platform defs.
2019-10-01 21:58:24 +00:00
|
|
|
|
if (defSym.SymbolSource == Symbol.Source.Variable) {
|
|
|
|
|
typeStr = Res.Strings.EQU_STACK_RELATIVE;
|
|
|
|
|
} else {
|
|
|
|
|
typeStr = Res.Strings.EQU_CONSTANT;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
typeStr = Res.Strings.EQU_ADDRESS;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-16 00:15:31 +00:00
|
|
|
|
if (!defSym.HasWidth && !defSym.IsConstant) {
|
|
|
|
|
// It's an address without an explicit width, do not annotate.
|
|
|
|
|
return operand;
|
Allow explicit widths in project/platform symbols, part 1
The ability to give explicit widths to local variables worked out
pretty well, so we're going to try adding the same thing to project
and platform symbols.
The first step is to allow widths to be specified in platform files,
and set with the project symbol editor. The DefSymbol editor is
also used for local variables, so a bit of dancing is required.
For platform/project symbols the width is optional, and is totally
ignored for constants. (For variables, constants are used for the
StackRel args, so the width is meaningful and required.)
We also now show the symbol's type (address or constant) and width
in the listing. This gets really distracting when overused, so we
only show it when the width is explicitly set. The default width
is 1, which most things will be, so users can make an aesthetic
choice there. (The place where widths make very little sense is when
the symbol represents a code entry point, rather than a data item.)
The maximum width of a local variable is now 256, but it's not
allowed to overlap with other variables or run of the end of the
direct page. The maximum width of a platform/project symbol is
65536, with bank-wrap behavior TBD.
The local variable table editor now refers to stack-relative
constants as such, rather than simply "constant", to make it clear
that it's not just defining an 8-bit constant.
Widths have been added to a handful of Apple II platform defs.
2019-10-01 21:58:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-16 00:15:31 +00:00
|
|
|
|
StringBuilder sb = new StringBuilder(operand.Length + typeStr.Length + 16);
|
|
|
|
|
sb.Append(operand);
|
|
|
|
|
|
|
|
|
|
int spacesNeeded = 7 - operand.Length;
|
|
|
|
|
do { // always output at least one space
|
|
|
|
|
sb.Append(' ');
|
|
|
|
|
} while (--spacesNeeded > 0);
|
|
|
|
|
|
|
|
|
|
sb.Append("{");
|
|
|
|
|
sb.Append(typeStr);
|
|
|
|
|
if (defSym.HasWidth) {
|
|
|
|
|
sb.Append('/');
|
|
|
|
|
sb.Append(defSym.DataDescriptor.Length);
|
Allow explicit widths in project/platform symbols, part 1
The ability to give explicit widths to local variables worked out
pretty well, so we're going to try adding the same thing to project
and platform symbols.
The first step is to allow widths to be specified in platform files,
and set with the project symbol editor. The DefSymbol editor is
also used for local variables, so a bit of dancing is required.
For platform/project symbols the width is optional, and is totally
ignored for constants. (For variables, constants are used for the
StackRel args, so the width is meaningful and required.)
We also now show the symbol's type (address or constant) and width
in the listing. This gets really distracting when overused, so we
only show it when the width is explicitly set. The default width
is 1, which most things will be, so users can make an aesthetic
choice there. (The place where widths make very little sense is when
the symbol represents a code entry point, rather than a data item.)
The maximum width of a local variable is now 256, but it's not
allowed to overlap with other variables or run of the end of the
direct page. The maximum width of a platform/project symbol is
65536, with bank-wrap behavior TBD.
The local variable table editor now refers to stack-relative
constants as such, rather than simply "constant", to make it clear
that it's not just defining an 8-bit constant.
Widths have been added to a handful of Apple II platform defs.
2019-10-01 21:58:24 +00:00
|
|
|
|
}
|
2019-11-16 00:15:31 +00:00
|
|
|
|
sb.Append("}");
|
|
|
|
|
return sb.ToString();
|
Allow explicit widths in project/platform symbols, part 1
The ability to give explicit widths to local variables worked out
pretty well, so we're going to try adding the same thing to project
and platform symbols.
The first step is to allow widths to be specified in platform files,
and set with the project symbol editor. The DefSymbol editor is
also used for local variables, so a bit of dancing is required.
For platform/project symbols the width is optional, and is totally
ignored for constants. (For variables, constants are used for the
StackRel args, so the width is meaningful and required.)
We also now show the symbol's type (address or constant) and width
in the listing. This gets really distracting when overused, so we
only show it when the width is explicitly set. The default width
is 1, which most things will be, so users can make an aesthetic
choice there. (The place where widths make very little sense is when
the symbol represents a code entry point, rather than a data item.)
The maximum width of a local variable is now 256, but it's not
allowed to overlap with other variables or run of the end of the
direct page. The maximum width of a platform/project symbol is
65536, with bank-wrap behavior TBD.
The local variable table editor now refers to stack-relative
constants as such, rather than simply "constant", to make it clear
that it's not just defining an 8-bit constant.
Widths have been added to a handful of Apple II platform defs.
2019-10-01 21:58:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// <summary>
|
2019-08-14 00:22:21 +00:00
|
|
|
|
/// Converts a collection of bytes that represent a string into an array of formatted
|
|
|
|
|
/// string operands.
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="formatter">Formatter object.</param>
|
2019-08-14 00:22:21 +00:00
|
|
|
|
/// <param name="opNames">Pseudo-opcode name table.</param>
|
|
|
|
|
/// <param name="dfd">Format descriptor.</param>
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// <param name="data">File data.</param>
|
|
|
|
|
/// <param name="offset">Offset, within data, of start of string.</param>
|
|
|
|
|
/// <param name="popcode">Pseudo-opcode string.</param>
|
2019-08-17 23:59:08 +00:00
|
|
|
|
/// <returns>Array of operand strings.</returns>
|
|
|
|
|
public static List<string> FormatStringOp(Formatter formatter, PseudoOpNames opNames,
|
2019-08-14 00:22:21 +00:00
|
|
|
|
FormatDescriptor dfd, byte[] data, int offset, out string popcode) {
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-08-14 00:22:21 +00:00
|
|
|
|
int hiddenLeadingBytes = 0;
|
|
|
|
|
int trailingBytes = 0;
|
|
|
|
|
StringOpFormatter.ReverseMode revMode = StringOpFormatter.ReverseMode.Forward;
|
2019-08-14 22:25:09 +00:00
|
|
|
|
Formatter.DelimiterSet delSet = formatter.Config.mStringDelimiters;
|
|
|
|
|
Formatter.DelimiterDef delDef;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-08-14 00:22:21 +00:00
|
|
|
|
CharEncoding.Convert charConv;
|
|
|
|
|
switch (dfd.FormatSubType) {
|
|
|
|
|
case FormatDescriptor.SubType.Ascii:
|
2019-08-16 00:53:12 +00:00
|
|
|
|
if (dfd.FormatType == FormatDescriptor.Type.StringDci) {
|
|
|
|
|
charConv = CharEncoding.ConvertLowAndHighAscii;
|
|
|
|
|
} else {
|
|
|
|
|
charConv = CharEncoding.ConvertAscii;
|
|
|
|
|
}
|
2019-08-14 22:25:09 +00:00
|
|
|
|
delDef = delSet.Get(CharEncoding.Encoding.Ascii);
|
2019-08-14 00:22:21 +00:00
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.SubType.HighAscii:
|
2019-08-16 00:53:12 +00:00
|
|
|
|
if (dfd.FormatType == FormatDescriptor.Type.StringDci) {
|
|
|
|
|
charConv = CharEncoding.ConvertLowAndHighAscii;
|
|
|
|
|
} else {
|
|
|
|
|
charConv = CharEncoding.ConvertHighAscii;
|
|
|
|
|
}
|
2019-08-14 22:25:09 +00:00
|
|
|
|
delDef = delSet.Get(CharEncoding.Encoding.HighAscii);
|
2019-08-14 00:22:21 +00:00
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.SubType.C64Petscii:
|
2019-08-21 00:55:12 +00:00
|
|
|
|
if (dfd.FormatType == FormatDescriptor.Type.StringDci) {
|
|
|
|
|
charConv = CharEncoding.ConvertLowAndHighC64Petscii;
|
|
|
|
|
} else {
|
|
|
|
|
charConv = CharEncoding.ConvertC64Petscii;
|
|
|
|
|
}
|
2019-08-14 22:25:09 +00:00
|
|
|
|
delDef = delSet.Get(CharEncoding.Encoding.C64Petscii);
|
2019-08-14 00:22:21 +00:00
|
|
|
|
break;
|
|
|
|
|
case FormatDescriptor.SubType.C64Screen:
|
2019-08-16 00:53:12 +00:00
|
|
|
|
if (dfd.FormatType == FormatDescriptor.Type.StringDci) {
|
|
|
|
|
charConv = CharEncoding.ConvertLowAndHighC64ScreenCode;
|
|
|
|
|
} else {
|
|
|
|
|
charConv = CharEncoding.ConvertC64ScreenCode;
|
|
|
|
|
}
|
2019-08-14 22:25:09 +00:00
|
|
|
|
delDef = delSet.Get(CharEncoding.Encoding.C64ScreenCode);
|
2019-08-14 00:22:21 +00:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
Debug.Assert(false);
|
|
|
|
|
charConv = CharEncoding.ConvertAscii;
|
2019-08-14 22:25:09 +00:00
|
|
|
|
delDef = delSet.Get(CharEncoding.Encoding.Ascii);
|
2019-08-14 00:22:21 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-08-16 00:53:12 +00:00
|
|
|
|
if (delDef == null) {
|
|
|
|
|
delDef = Formatter.DOUBLE_QUOTE_DELIM;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-14 00:22:21 +00:00
|
|
|
|
switch (dfd.FormatType) {
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
case FormatDescriptor.Type.StringGeneric:
|
2019-08-14 00:22:21 +00:00
|
|
|
|
// Generic character data.
|
2019-08-11 23:22:16 +00:00
|
|
|
|
popcode = opNames.StrGeneric;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
break;
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
case FormatDescriptor.Type.StringReverse:
|
2019-08-16 00:53:12 +00:00
|
|
|
|
// Character data, full width specified by formatter. Show characters
|
2019-05-02 22:45:40 +00:00
|
|
|
|
// in reverse order.
|
2019-08-11 23:22:16 +00:00
|
|
|
|
popcode = opNames.StrReverse;
|
2019-08-14 00:22:21 +00:00
|
|
|
|
revMode = StringOpFormatter.ReverseMode.FullReverse;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
break;
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
case FormatDescriptor.Type.StringNullTerm:
|
2019-08-14 00:22:21 +00:00
|
|
|
|
// Character data with a terminating null. Don't show the null byte.
|
2019-08-11 23:22:16 +00:00
|
|
|
|
popcode = opNames.StrNullTerm;
|
2019-08-14 00:22:21 +00:00
|
|
|
|
trailingBytes = 1;
|
|
|
|
|
//if (strLen == 0) {
|
|
|
|
|
// showHexZeroes = 1;
|
|
|
|
|
//}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
break;
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
case FormatDescriptor.Type.StringL8:
|
2019-08-14 00:22:21 +00:00
|
|
|
|
// Character data with a leading length byte. Don't show the length.
|
|
|
|
|
hiddenLeadingBytes = 1;
|
|
|
|
|
//if (strLen == 0) {
|
|
|
|
|
// showHexZeroes = 1;
|
|
|
|
|
//}
|
2019-08-11 23:22:16 +00:00
|
|
|
|
popcode = opNames.StrLen8;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
break;
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
case FormatDescriptor.Type.StringL16:
|
2019-08-14 00:22:21 +00:00
|
|
|
|
// Character data with a leading length word. Don't show the length.
|
|
|
|
|
Debug.Assert(dfd.Length > 1);
|
|
|
|
|
hiddenLeadingBytes = 2;
|
|
|
|
|
//if (strLen == 0) {
|
|
|
|
|
// showHexZeroes = 2;
|
|
|
|
|
//}
|
2019-08-11 23:22:16 +00:00
|
|
|
|
popcode = opNames.StrLen16;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
break;
|
2019-08-14 00:22:21 +00:00
|
|
|
|
case FormatDescriptor.Type.StringDci:
|
2019-08-16 00:53:12 +00:00
|
|
|
|
// High bit on last byte is flipped.
|
2019-08-14 00:22:21 +00:00
|
|
|
|
popcode = opNames.StrDci;
|
|
|
|
|
break;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
default:
|
|
|
|
|
Debug.Assert(false);
|
|
|
|
|
popcode = ".!!!";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-14 22:25:09 +00:00
|
|
|
|
StringOpFormatter stropf = new StringOpFormatter(formatter, delDef,
|
2020-07-20 01:39:27 +00:00
|
|
|
|
StringOpFormatter.RawOutputStyle.CommaSep, charConv);
|
2019-08-14 00:22:21 +00:00
|
|
|
|
stropf.FeedBytes(data, offset + hiddenLeadingBytes,
|
|
|
|
|
dfd.Length - hiddenLeadingBytes - trailingBytes, 0, revMode);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-08-14 00:22:21 +00:00
|
|
|
|
return stropf.Lines;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Special formatting flags for the FormatNumericOperand() method.
|
|
|
|
|
/// </summary>
|
2019-11-09 04:44:45 +00:00
|
|
|
|
[Flags]
|
2019-05-02 22:45:40 +00:00
|
|
|
|
public enum FormatNumericOpFlags {
|
2019-11-13 01:24:41 +00:00
|
|
|
|
None = 0,
|
|
|
|
|
IsPcRel = 1, // opcode is PC relative, e.g. branch or PER
|
2020-07-02 00:59:12 +00:00
|
|
|
|
IsAbsolutePBR = 1 << 1, // operand implicitly uses 'K' on 65816 (JMP/JSR)
|
|
|
|
|
HasHashPrefix = 1 << 2, // operand has a leading '#', reducing ambiguity
|
|
|
|
|
OmitLabelPrefixSuffix = 1 << 3, // don't show annotation char or non-unique prefix
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-21 22:29:00 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Converts a FormatDescriptor SubType to a CharEncoding.Encoding value.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="subType">FormatDescriptor sub-type.</param>
|
|
|
|
|
/// <returns>The corresponding CharEncoding.Encoding value, or Encoding.Unknown
|
|
|
|
|
/// if the sub-type isn't a character encoding.</returns>
|
2019-08-12 00:59:20 +00:00
|
|
|
|
public static CharEncoding.Encoding SubTypeToEnc(FormatDescriptor.SubType subType) {
|
|
|
|
|
switch (subType) {
|
|
|
|
|
case FormatDescriptor.SubType.Ascii:
|
|
|
|
|
return CharEncoding.Encoding.Ascii;
|
|
|
|
|
case FormatDescriptor.SubType.HighAscii:
|
|
|
|
|
return CharEncoding.Encoding.HighAscii;
|
|
|
|
|
case FormatDescriptor.SubType.C64Petscii:
|
|
|
|
|
return CharEncoding.Encoding.C64Petscii;
|
|
|
|
|
case FormatDescriptor.SubType.C64Screen:
|
|
|
|
|
return CharEncoding.Encoding.C64ScreenCode;
|
|
|
|
|
default:
|
|
|
|
|
return CharEncoding.Encoding.Unknown;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-02 22:45:40 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Format a numeric operand value according to the specified sub-format.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="formatter">Text formatter.</param>
|
|
|
|
|
/// <param name="symbolTable">Full table of project symbols.</param>
|
|
|
|
|
/// <param name="labelMap">Symbol label remap, for local label conversion. May be
|
|
|
|
|
/// null.</param>
|
|
|
|
|
/// <param name="dfd">Operand format descriptor.</param>
|
|
|
|
|
/// <param name="operandValue">Operand's value. For most things this comes directly
|
|
|
|
|
/// out of the code, for relative branches it's a 24-bit absolute address.</param>
|
|
|
|
|
/// <param name="operandLen">Length of operand, in bytes. For an instruction, this
|
|
|
|
|
/// does not include the opcode byte. For a relative branch, this will be 2.</param>
|
|
|
|
|
/// <param name="flags">Special handling.</param>
|
|
|
|
|
public static string FormatNumericOperand(Formatter formatter, SymbolTable symbolTable,
|
2019-08-31 01:33:05 +00:00
|
|
|
|
Dictionary<string, string> labelMap, FormatDescriptor dfd, int operandValue,
|
|
|
|
|
int operandLen, FormatNumericOpFlags flags) {
|
|
|
|
|
return FormatNumericOperand(formatter, symbolTable, null, labelMap, dfd, -1,
|
|
|
|
|
operandValue, operandLen, flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2019-11-09 04:44:45 +00:00
|
|
|
|
/// Format a numeric operand value according to the specified sub-format. This
|
|
|
|
|
/// version takes additional arguments to support local variables.
|
2019-08-31 01:33:05 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="formatter">Text formatter.</param>
|
|
|
|
|
/// <param name="symbolTable">Full table of project symbols.</param>
|
|
|
|
|
/// <param name="lvLookup">Local variable lookup object. May be null if not
|
|
|
|
|
/// formatting an instruction.</param>
|
|
|
|
|
/// <param name="labelMap">Symbol label remap, for local label conversion. May be
|
|
|
|
|
/// null.</param>
|
|
|
|
|
/// <param name="dfd">Operand format descriptor.</param>
|
|
|
|
|
/// <param name="offset">Offset of start of instruction or data pseudo-op, for
|
|
|
|
|
/// variable name lookup. Okay to pass -1 when not formatting an instruction.</param>
|
|
|
|
|
/// <param name="operandValue">Operand's value. For most things this comes directly
|
|
|
|
|
/// out of the code, for relative branches it's a 24-bit absolute address.</param>
|
|
|
|
|
/// <param name="operandLen">Length of operand, in bytes. For an instruction, this
|
|
|
|
|
/// does not include the opcode byte. For a relative branch, this will be 2.</param>
|
|
|
|
|
/// <param name="flags">Special handling.</param>
|
|
|
|
|
public static string FormatNumericOperand(Formatter formatter, SymbolTable symbolTable,
|
|
|
|
|
LocalVariableLookup lvLookup, Dictionary<string, string> labelMap,
|
|
|
|
|
FormatDescriptor dfd, int offset, int operandValue, int operandLen,
|
|
|
|
|
FormatNumericOpFlags flags) {
|
2019-05-02 22:45:40 +00:00
|
|
|
|
Debug.Assert(operandLen > 0);
|
|
|
|
|
int hexMinLen = operandLen * 2;
|
|
|
|
|
|
|
|
|
|
switch (dfd.FormatSubType) {
|
|
|
|
|
case FormatDescriptor.SubType.None:
|
|
|
|
|
case FormatDescriptor.SubType.Hex:
|
|
|
|
|
case FormatDescriptor.SubType.Address:
|
2020-07-03 20:58:18 +00:00
|
|
|
|
if ((formatter.ExpressionMode == Formatter.FormatConfig.ExpressionMode.Cc65 ||
|
|
|
|
|
formatter.ExpressionMode == Formatter.FormatConfig.ExpressionMode.Merlin) &&
|
|
|
|
|
(flags & FormatNumericOpFlags.IsAbsolutePBR) != 0) {
|
|
|
|
|
// cc65 really doesn't like 24-bit values for JMP/JSR. If it sees a
|
|
|
|
|
// 24-bit hex constant it emits JML/JSL. Merlin works either way, and
|
|
|
|
|
// I think it looks better as a 16-bit value.
|
|
|
|
|
operandValue &= 0xffff;
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
return formatter.FormatHexValue(operandValue, hexMinLen);
|
|
|
|
|
case FormatDescriptor.SubType.Decimal:
|
|
|
|
|
return formatter.FormatDecimalValue(operandValue);
|
|
|
|
|
case FormatDescriptor.SubType.Binary:
|
|
|
|
|
return formatter.FormatBinaryValue(operandValue, hexMinLen * 4);
|
2019-08-12 00:59:20 +00:00
|
|
|
|
case FormatDescriptor.SubType.Ascii:
|
2019-08-10 21:24:19 +00:00
|
|
|
|
case FormatDescriptor.SubType.HighAscii:
|
Change the way string formats are defined
We used to use type="String", with the sub-type indicating whether
the string was null-terminated, prefixed with a length, or whatever.
This didn't leave much room for specifying a character encoding,
which is orthogonal to the sub-type.
What we actually want is to have the type specify the string type,
and then have the sub-type determine the character encoding. These
sub-types can also be used with the Numeric type to specify the
encoding of character operands.
This change updates the enum definitions and the various bits of
code that use them, but does not add any code for working with
non-ASCII character encodings.
The project file version number was incremented to 2, since the new
FormatDescriptor serialization is mildly incompatible with the old.
(Won't explode, but it'll post a complaint and ignore the stuff
it doesn't recognize.)
While I was at it, I finished removing DciReverse. It's still part
of the 2005-string-types regression test, which currently fails
because the generated source doesn't match.
2019-08-07 22:23:23 +00:00
|
|
|
|
case FormatDescriptor.SubType.C64Petscii:
|
|
|
|
|
case FormatDescriptor.SubType.C64Screen:
|
2019-08-12 00:59:20 +00:00
|
|
|
|
CharEncoding.Encoding enc = SubTypeToEnc(dfd.FormatSubType);
|
|
|
|
|
return formatter.FormatCharacterValue(operandValue, enc);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
case FormatDescriptor.SubType.Symbol:
|
2019-08-31 01:33:05 +00:00
|
|
|
|
if (lvLookup != null && dfd.SymbolRef.IsVariable) {
|
|
|
|
|
Debug.Assert(operandLen == 1); // only doing 8-bit stuff
|
|
|
|
|
DefSymbol defSym = lvLookup.GetSymbol(offset, dfd.SymbolRef);
|
|
|
|
|
if (defSym != null) {
|
2019-11-13 01:24:41 +00:00
|
|
|
|
// For local variables we're doing a trivial add and don't
|
2019-11-09 04:44:45 +00:00
|
|
|
|
// wrap, so the "common" format works for everybody.
|
2019-08-31 01:33:05 +00:00
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
FormatNumericSymbolCommon(formatter, defSym, null,
|
|
|
|
|
dfd, operandValue, operandLen, flags, sb);
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
} else {
|
|
|
|
|
Debug.WriteLine("Local variable format failed");
|
|
|
|
|
Debug.Assert(false);
|
|
|
|
|
return formatter.FormatHexValue(operandValue, hexMinLen);
|
|
|
|
|
}
|
2019-08-31 21:10:59 +00:00
|
|
|
|
} else if (symbolTable.TryGetNonVariableValue(dfd.SymbolRef.Label,
|
|
|
|
|
out Symbol sym)) {
|
2019-05-02 22:45:40 +00:00
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
switch (formatter.ExpressionMode) {
|
|
|
|
|
case Formatter.FormatConfig.ExpressionMode.Common:
|
|
|
|
|
FormatNumericSymbolCommon(formatter, sym, labelMap,
|
|
|
|
|
dfd, operandValue, operandLen, flags, sb);
|
|
|
|
|
break;
|
|
|
|
|
case Formatter.FormatConfig.ExpressionMode.Cc65:
|
|
|
|
|
FormatNumericSymbolCc65(formatter, sym, labelMap,
|
|
|
|
|
dfd, operandValue, operandLen, flags, sb);
|
|
|
|
|
break;
|
|
|
|
|
case Formatter.FormatConfig.ExpressionMode.Merlin:
|
|
|
|
|
FormatNumericSymbolMerlin(formatter, sym, labelMap,
|
|
|
|
|
dfd, operandValue, operandLen, flags, sb);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
Debug.Assert(false, "Unknown expression mode " +
|
|
|
|
|
formatter.ExpressionMode);
|
|
|
|
|
return "???";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sb.ToString();
|
|
|
|
|
} else {
|
|
|
|
|
return formatter.FormatHexValue(operandValue, hexMinLen);
|
|
|
|
|
}
|
|
|
|
|
default:
|
2019-08-10 21:24:19 +00:00
|
|
|
|
// should not see REMOVE or ASCII_GENERIC here
|
2019-05-02 22:45:40 +00:00
|
|
|
|
Debug.Assert(false);
|
|
|
|
|
return "???";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Format the symbol and adjustment using common expression syntax.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static void FormatNumericSymbolCommon(Formatter formatter, Symbol sym,
|
|
|
|
|
Dictionary<string, string> labelMap, FormatDescriptor dfd,
|
|
|
|
|
int operandValue, int operandLen, FormatNumericOpFlags flags, StringBuilder sb) {
|
|
|
|
|
// We could have some simple code that generated correct output, shifting and
|
|
|
|
|
// masking every time, but that's ugly and annoying. For single-byte ops we can
|
|
|
|
|
// just use the byte-select operators, for wider ops we get only as fancy as we
|
|
|
|
|
// need to be.
|
|
|
|
|
|
2019-11-13 01:24:41 +00:00
|
|
|
|
// Start by remapping the label, if necessary. The remapped label may have a
|
|
|
|
|
// local-variable prefix character.
|
2019-05-02 22:45:40 +00:00
|
|
|
|
string symLabel = sym.Label;
|
|
|
|
|
if (labelMap != null && labelMap.TryGetValue(symLabel, out string newLabel)) {
|
|
|
|
|
symLabel = newLabel;
|
|
|
|
|
}
|
2019-08-31 01:33:05 +00:00
|
|
|
|
if (sym.IsVariable) {
|
|
|
|
|
symLabel = formatter.FormatVariableLabel(symLabel);
|
|
|
|
|
}
|
2019-11-13 01:24:41 +00:00
|
|
|
|
|
|
|
|
|
// Now put the prefix/suffix back on if desired. We don't want to mess with it
|
|
|
|
|
// if it's from the assembler.
|
2019-11-16 00:15:31 +00:00
|
|
|
|
if ((flags & FormatNumericOpFlags.OmitLabelPrefixSuffix) == 0) {
|
2019-11-13 01:24:41 +00:00
|
|
|
|
symLabel = Symbol.ConvertLabelForDisplay(symLabel, sym.LabelAnno,
|
2019-11-17 00:34:42 +00:00
|
|
|
|
true, formatter);
|
2019-11-09 04:44:45 +00:00
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
|
|
|
|
if (operandLen == 1) {
|
|
|
|
|
// Use the byte-selection operator to get the right piece. In 64tass the
|
|
|
|
|
// selection operator has a very low precedence, similar to Merlin 32.
|
2020-07-02 20:00:02 +00:00
|
|
|
|
int symbolValue;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
string selOp;
|
|
|
|
|
if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank) {
|
|
|
|
|
symbolValue = (sym.Value >> 16) & 0xff;
|
|
|
|
|
if (formatter.Config.mBankSelectBackQuote) {
|
|
|
|
|
selOp = "`";
|
|
|
|
|
} else {
|
|
|
|
|
selOp = "^";
|
|
|
|
|
}
|
|
|
|
|
} else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High) {
|
|
|
|
|
symbolValue = (sym.Value >> 8) & 0xff;
|
|
|
|
|
selOp = ">";
|
|
|
|
|
} else {
|
|
|
|
|
symbolValue = sym.Value & 0xff;
|
|
|
|
|
if (symbolValue == sym.Value) {
|
|
|
|
|
selOp = string.Empty;
|
|
|
|
|
} else {
|
|
|
|
|
selOp = "<";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
operandValue &= 0xff;
|
|
|
|
|
|
|
|
|
|
if (operandValue != symbolValue &&
|
|
|
|
|
dfd.SymbolRef.ValuePart != WeakSymbolRef.Part.Low) {
|
|
|
|
|
// Adjustment is required to an upper-byte part.
|
|
|
|
|
sb.Append('(');
|
|
|
|
|
sb.Append(selOp);
|
|
|
|
|
sb.Append(symLabel);
|
|
|
|
|
sb.Append(')');
|
|
|
|
|
} else {
|
2020-07-02 20:00:02 +00:00
|
|
|
|
// no adjustment required, or no byte-selection
|
2019-05-02 22:45:40 +00:00
|
|
|
|
sb.Append(selOp);
|
|
|
|
|
sb.Append(symLabel);
|
|
|
|
|
}
|
2020-07-02 20:00:02 +00:00
|
|
|
|
int adjustment = operandValue - symbolValue;
|
|
|
|
|
sb.Append(formatter.FormatAdjustment(adjustment));
|
2019-05-02 22:45:40 +00:00
|
|
|
|
} else if (operandLen <= 4) {
|
|
|
|
|
// Operands and values should be 8/16/24 bit unsigned quantities. 32-bit
|
|
|
|
|
// support is really there so you can have a 24-bit pointer in a 32-bit hole.
|
|
|
|
|
// Might need to adjust this if 32-bit signed quantities become interesting.
|
|
|
|
|
uint mask = 0xffffffff >> ((4 - operandLen) * 8);
|
2020-07-02 20:00:02 +00:00
|
|
|
|
int rightShift, symbolValue;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank) {
|
|
|
|
|
symbolValue = (sym.Value >> 16);
|
|
|
|
|
rightShift = 16;
|
|
|
|
|
} else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High) {
|
|
|
|
|
symbolValue = (sym.Value >> 8);
|
|
|
|
|
rightShift = 8;
|
|
|
|
|
} else {
|
|
|
|
|
symbolValue = sym.Value;
|
|
|
|
|
rightShift = 0;
|
|
|
|
|
}
|
2020-07-02 20:00:02 +00:00
|
|
|
|
bool hasShift = (rightShift != 0);
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
2019-11-09 04:44:45 +00:00
|
|
|
|
if ((flags & FormatNumericOpFlags.IsPcRel) != 0) {
|
2019-05-02 22:45:40 +00:00
|
|
|
|
// PC-relative operands are funny, because an 8- or 16-bit value is always
|
|
|
|
|
// expanded to 24 bits. We output a 16-bit value that the assembler will
|
|
|
|
|
// convert back to 8-bit or 16-bit. In any event, the bank byte is never
|
|
|
|
|
// relevant to our computations.
|
|
|
|
|
operandValue &= 0xffff;
|
|
|
|
|
symbolValue &= 0xffff;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-02 00:59:12 +00:00
|
|
|
|
if ((flags & FormatNumericOpFlags.IsAbsolutePBR) != 0) {
|
|
|
|
|
if ((operandValue & 0x00ff0000) == (symbolValue & 0x00ff0000)) {
|
|
|
|
|
// JMP or JSR to something within the same bank. We don't need to
|
|
|
|
|
// mask the value.
|
|
|
|
|
symbolValue &= 0xffff;
|
|
|
|
|
} else {
|
|
|
|
|
// This is an absolute JMP/JSR to an out-of-bank location, which is
|
|
|
|
|
// bogus and should probably be prevented at a higher level. We handle
|
|
|
|
|
// it by altering the mask so an adjustment that covers the difference
|
|
|
|
|
// in bank values is generated.
|
|
|
|
|
mask = 0x00ffffff;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-02 22:45:40 +00:00
|
|
|
|
bool needMask = false;
|
|
|
|
|
if (symbolValue > mask) {
|
|
|
|
|
// Post-shift value won't fit in an operand-size box.
|
|
|
|
|
symbolValue = (int) (symbolValue & mask);
|
|
|
|
|
needMask = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
operandValue = (int)(operandValue & mask);
|
2020-07-02 20:00:02 +00:00
|
|
|
|
int adjustment = operandValue - symbolValue;
|
|
|
|
|
|
|
|
|
|
// Possibilities:
|
|
|
|
|
// label
|
|
|
|
|
// label + adj
|
|
|
|
|
// label >> rightShift
|
|
|
|
|
// (label >> rightShift) + adj
|
|
|
|
|
// label & mask
|
|
|
|
|
// (label & mask) + adj
|
|
|
|
|
// (label >> rightShift) & mask
|
|
|
|
|
// ((label >> rightShift) & mask) + adj
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
|
|
|
|
sb.Append(symLabel);
|
|
|
|
|
|
|
|
|
|
if (rightShift != 0) {
|
|
|
|
|
sb.Append(" >> ");
|
|
|
|
|
sb.Append(rightShift.ToString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (needMask) {
|
2020-07-02 20:00:02 +00:00
|
|
|
|
if (rightShift != 0) {
|
|
|
|
|
sb.Insert(0, '(');
|
|
|
|
|
sb.Append(')');
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-02 22:45:40 +00:00
|
|
|
|
sb.Append(" & ");
|
|
|
|
|
sb.Append(formatter.FormatHexValue((int)mask, 2));
|
2020-07-02 20:00:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (adjustment != 0) {
|
|
|
|
|
if (needMask || rightShift != 0) {
|
|
|
|
|
sb.Insert(0, '(');
|
|
|
|
|
sb.Append(')');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sb.Append(formatter.FormatAdjustment(adjustment));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Starting with a '(' makes it look like an indirect operand, so we need
|
|
|
|
|
// to prefix the expression with a no-op addition.
|
|
|
|
|
if (sb[0] == '(' && (flags & FormatNumericOpFlags.HasHashPrefix) == 0) {
|
|
|
|
|
sb.Insert(0, "0+");
|
2019-05-02 22:45:40 +00:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Debug.Assert(false, "bad numeric len");
|
|
|
|
|
sb.Append("?????");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Format the symbol and adjustment using cc65 expression syntax.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static void FormatNumericSymbolCc65(Formatter formatter, Symbol sym,
|
|
|
|
|
Dictionary<string, string> labelMap, FormatDescriptor dfd,
|
|
|
|
|
int operandValue, int operandLen, FormatNumericOpFlags flags, StringBuilder sb) {
|
|
|
|
|
// The key difference between cc65 and other assemblers with general expressions
|
|
|
|
|
// is that the bitwise shift and AND operators have higher precedence than the
|
|
|
|
|
// arithmetic ops like add and subtract. (The bitwise ops are equal to multiply
|
|
|
|
|
// and divide.) This means that, if we want to mask off the low 16 bits and add one
|
|
|
|
|
// to a label, we can write "start & $ffff + 1" rather than "(start & $ffff) + 1".
|
|
|
|
|
//
|
2020-07-02 20:00:02 +00:00
|
|
|
|
// This is particularly convenient for PEA, since "PEA (start & $ffff) + 1" looks like
|
2019-05-02 22:45:40 +00:00
|
|
|
|
// we're trying to use a (non-existent) indirect form of PEA. We can write things
|
|
|
|
|
// in a simpler way.
|
|
|
|
|
|
|
|
|
|
int adjustment, symbolValue;
|
|
|
|
|
|
|
|
|
|
string symLabel = sym.Label;
|
|
|
|
|
if (labelMap != null && labelMap.TryGetValue(symLabel, out string newLabel)) {
|
|
|
|
|
symLabel = newLabel;
|
|
|
|
|
}
|
2019-11-16 00:15:31 +00:00
|
|
|
|
if ((flags & FormatNumericOpFlags.OmitLabelPrefixSuffix) == 0) {
|
2019-11-13 01:24:41 +00:00
|
|
|
|
symLabel = Symbol.ConvertLabelForDisplay(symLabel, sym.LabelAnno,
|
2019-11-17 00:34:42 +00:00
|
|
|
|
true, formatter);
|
2019-11-09 04:44:45 +00:00
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
|
|
|
|
if (operandLen == 1) {
|
|
|
|
|
// Use the byte-selection operator to get the right piece.
|
|
|
|
|
string selOp;
|
|
|
|
|
if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank) {
|
|
|
|
|
symbolValue = (sym.Value >> 16) & 0xff;
|
|
|
|
|
selOp = "^";
|
|
|
|
|
} else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High) {
|
|
|
|
|
symbolValue = (sym.Value >> 8) & 0xff;
|
|
|
|
|
selOp = ">";
|
|
|
|
|
} else {
|
|
|
|
|
symbolValue = sym.Value & 0xff;
|
|
|
|
|
if (symbolValue == sym.Value) {
|
|
|
|
|
selOp = string.Empty;
|
|
|
|
|
} else {
|
|
|
|
|
selOp = "<";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sb.Append(selOp);
|
|
|
|
|
sb.Append(symLabel);
|
|
|
|
|
|
|
|
|
|
operandValue &= 0xff;
|
|
|
|
|
} else if (operandLen <= 4) {
|
|
|
|
|
uint mask = 0xffffffff >> ((4 - operandLen) * 8);
|
|
|
|
|
string shOp;
|
|
|
|
|
if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank) {
|
|
|
|
|
symbolValue = (sym.Value >> 16);
|
|
|
|
|
shOp = " >> 16";
|
|
|
|
|
} else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High) {
|
|
|
|
|
symbolValue = (sym.Value >> 8);
|
|
|
|
|
shOp = " >> 8";
|
|
|
|
|
} else {
|
|
|
|
|
symbolValue = sym.Value;
|
|
|
|
|
shOp = "";
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-09 04:44:45 +00:00
|
|
|
|
if ((flags & FormatNumericOpFlags.IsPcRel) != 0) {
|
2019-05-02 22:45:40 +00:00
|
|
|
|
// PC-relative operands are funny, because an 8- or 16-bit value is always
|
|
|
|
|
// expanded to 24 bits. We output a 16-bit value that the assembler will
|
|
|
|
|
// convert back to 8-bit or 16-bit. In any event, the bank byte is never
|
|
|
|
|
// relevant to our computations.
|
|
|
|
|
operandValue &= 0xffff;
|
|
|
|
|
symbolValue &= 0xffff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sb.Append(symLabel);
|
|
|
|
|
sb.Append(shOp);
|
|
|
|
|
if (symbolValue > mask) {
|
|
|
|
|
// Post-shift value won't fit in an operand-size box.
|
|
|
|
|
symbolValue = (int)(symbolValue & mask);
|
|
|
|
|
sb.Append(" & ");
|
|
|
|
|
sb.Append(formatter.FormatHexValue((int)mask, 2));
|
|
|
|
|
}
|
|
|
|
|
operandValue = (int)(operandValue & mask);
|
|
|
|
|
|
2019-09-07 20:39:22 +00:00
|
|
|
|
// If we've added stuff, and we're going to add an adjustment later, stick
|
|
|
|
|
// an extra space in between for readability.
|
|
|
|
|
if (sb.Length != symLabel.Length && operandValue != symbolValue) {
|
2019-05-02 22:45:40 +00:00
|
|
|
|
sb.Append(' ');
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Debug.Assert(false, "bad numeric len");
|
|
|
|
|
sb.Append("?????");
|
|
|
|
|
symbolValue = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
adjustment = operandValue - symbolValue;
|
|
|
|
|
|
|
|
|
|
sb.Append(formatter.FormatAdjustment(adjustment));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Format the symbol and adjustment using Merlin expression syntax.
|
|
|
|
|
/// </summary>
|
|
|
|
|
private static void FormatNumericSymbolMerlin(Formatter formatter, Symbol sym,
|
|
|
|
|
Dictionary<string, string> labelMap, FormatDescriptor dfd,
|
|
|
|
|
int operandValue, int operandLen, FormatNumericOpFlags flags, StringBuilder sb) {
|
|
|
|
|
// Merlin expressions are compatible with the original 8-bit Merlin. They're
|
|
|
|
|
// evaluated from left to right, with (almost) no regard for operator precedence.
|
|
|
|
|
//
|
|
|
|
|
// The part-selection operators differ from "simple" in two ways:
|
|
|
|
|
// (1) They always happen last. If FOO=$10f0, "#>FOO+$18" == $11. One of the
|
|
|
|
|
// few cases where left-to-right evaluation is overridden.
|
|
|
|
|
// (2) They select words, not bytes. If FOO=$123456, "#>FOO" is $1234. This is
|
|
|
|
|
// best thought of as a shift operator, rather than byte-selection. For
|
|
|
|
|
// 8-bit code this doesn't matter.
|
|
|
|
|
//
|
|
|
|
|
// This behavior leads to simpler expressions for simple symbol adjustments.
|
|
|
|
|
|
|
|
|
|
string symLabel = sym.Label;
|
|
|
|
|
if (labelMap != null && labelMap.TryGetValue(symLabel, out string newLabel)) {
|
|
|
|
|
symLabel = newLabel;
|
|
|
|
|
}
|
2019-11-16 00:15:31 +00:00
|
|
|
|
if ((flags & FormatNumericOpFlags.OmitLabelPrefixSuffix) == 0) {
|
2019-11-13 01:24:41 +00:00
|
|
|
|
symLabel = Symbol.ConvertLabelForDisplay(symLabel, sym.LabelAnno,
|
2019-11-17 00:34:42 +00:00
|
|
|
|
true, formatter);
|
2019-11-09 04:44:45 +00:00
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
|
|
|
|
|
int adjustment;
|
|
|
|
|
|
|
|
|
|
// If we add or subtract an adjustment, it will be done on the full value, which
|
|
|
|
|
// is then shifted to the appropriate part. So we need to left-shift the operand
|
|
|
|
|
// value to match. We fill in the low bytes with the contents of the symbol, so
|
|
|
|
|
// that the adjustment doesn't include unnecessary values. (For example, let
|
|
|
|
|
// FOO=$10f0, with operand "#>FOO" ($10). We shift the operand to get $1000, then
|
|
|
|
|
// OR in the low byte to get $10f0, so that when we subtract we get adjustment==0.)
|
|
|
|
|
int adjOperand, keepLen;
|
|
|
|
|
if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.Bank) {
|
|
|
|
|
adjOperand = operandValue << 16 | (int)(sym.Value & 0xff00ffff);
|
|
|
|
|
keepLen = 3;
|
|
|
|
|
} else if (dfd.SymbolRef.ValuePart == WeakSymbolRef.Part.High) {
|
|
|
|
|
adjOperand = (operandValue << 8) | (sym.Value & 0xff);
|
|
|
|
|
keepLen = 2;
|
|
|
|
|
} else {
|
|
|
|
|
adjOperand = operandValue;
|
|
|
|
|
keepLen = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
keepLen = Math.Max(keepLen, operandLen);
|
|
|
|
|
adjustment = adjOperand - sym.Value;
|
2019-10-03 17:32:54 +00:00
|
|
|
|
|
2019-05-02 22:45:40 +00:00
|
|
|
|
if (keepLen == 1) {
|
2019-10-03 17:32:54 +00:00
|
|
|
|
int origAdjust = adjustment;
|
2019-05-02 22:45:40 +00:00
|
|
|
|
adjustment %= 256;
|
|
|
|
|
// Adjust for aesthetics. The assembler implicitly applies a modulo operation,
|
|
|
|
|
// so we can use the value closest to zero.
|
|
|
|
|
if (adjustment > 127) {
|
|
|
|
|
adjustment = -(256 - adjustment) /*% 256*/;
|
|
|
|
|
} else if (adjustment < -128) {
|
|
|
|
|
adjustment = (256 + adjustment) /*% 256*/;
|
|
|
|
|
}
|
2019-10-03 17:32:54 +00:00
|
|
|
|
|
|
|
|
|
// We have a problem with ambiguous direct-page arguments if the adjusted
|
|
|
|
|
// value crosses a bank boundary. For example, "LDA $fff0+24" is computed
|
|
|
|
|
// as $010008, which is too big for a DP arg, so Merlin treats it as absolute
|
|
|
|
|
// (LDA $0008) instead of DP. If Merlin had done the implicit "& $ffff" before
|
|
|
|
|
// testing the value for DP range, this would behave correctly. Unfortunately
|
|
|
|
|
// there is no "force DP" modifier, so we either need to add an explicit mask
|
|
|
|
|
// or just punt and use the original adjustment.
|
2019-11-16 00:15:31 +00:00
|
|
|
|
//
|
|
|
|
|
// Note DP is only relevant for bank zero.
|
|
|
|
|
//
|
2019-10-03 17:32:54 +00:00
|
|
|
|
// TODO(someday): we only need to do this for ambiguous DP. If the instruction
|
|
|
|
|
// is imm or doesn't have an abs equivalent, or it's a fixed-width data item
|
|
|
|
|
// like .DD1, we can still use the nicer-looking adjustment. We don't currently
|
|
|
|
|
// pass the OpDef in here.
|
2019-11-16 00:15:31 +00:00
|
|
|
|
if ((sym.Value & 0xff0000) == 0 && ((sym.Value + adjustment) & 0xff0000) != 0) {
|
2019-10-03 17:32:54 +00:00
|
|
|
|
adjustment = origAdjust;
|
|
|
|
|
}
|
2019-05-02 22:45:40 +00:00
|
|
|
|
} else if (keepLen == 2) {
|
|
|
|
|
adjustment %= 65536;
|
|
|
|
|
if (adjustment > 32767) {
|
|
|
|
|
adjustment = -(65536 - adjustment) /*% 65536*/;
|
|
|
|
|
} else if (adjustment < -32768) {
|
|
|
|
|
adjustment = (65536 + adjustment) /*% 65536*/;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use the label from sym, not dfd's weak ref; might be different if label
|
|
|
|
|
// comparisons are case-insensitive.
|
|
|
|
|
switch (dfd.SymbolRef.ValuePart) {
|
|
|
|
|
case WeakSymbolRef.Part.Unknown:
|
|
|
|
|
case WeakSymbolRef.Part.Low:
|
|
|
|
|
// For Merlin, "<" is effectively a no-op. We can put it in for
|
|
|
|
|
// aesthetics when grabbing the low byte of a 16-bit value.
|
|
|
|
|
if ((operandLen == 1) && sym.Value > 0xff) {
|
|
|
|
|
sb.Append('<');
|
|
|
|
|
}
|
|
|
|
|
sb.Append(symLabel);
|
|
|
|
|
break;
|
|
|
|
|
case WeakSymbolRef.Part.High:
|
|
|
|
|
sb.Append('>');
|
|
|
|
|
sb.Append(symLabel);
|
|
|
|
|
break;
|
|
|
|
|
case WeakSymbolRef.Part.Bank:
|
|
|
|
|
sb.Append('^');
|
|
|
|
|
sb.Append(symLabel);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
Debug.Assert(false, "bad part");
|
|
|
|
|
sb.Append("???");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sb.Append(formatter.FormatAdjustment(adjustment));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|