2018-09-28 17:05:11 +00:00
|
|
|
|
/*
|
|
|
|
|
* Copyright 2018 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.Diagnostics;
|
|
|
|
|
|
|
|
|
|
namespace Asm65 {
|
2021-09-22 21:39:39 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Memory address primitives.
|
|
|
|
|
/// </summary>
|
2018-09-28 17:05:11 +00:00
|
|
|
|
public static class Address {
|
ORG rework, part 6
Added support for non-addressable regions, which are useful for things
like file headers stripped out by the system loader, or chunks that
get loaded into non-addressable graphics RAM. Regions are specified
with the "NA" address value. The code list displays the address field
greyed out, starting from zero (which is kind of handy if you want to
know the relative offset within the region).
Putting labels in non-addressable regions doesn't make sense, but
symbol resolution is complicated enough that we really only have two
options: ignore the labels entirely, or allow them but warn of their
presence. The problem isn't so much the label, which you could
legitimately want to access from an extension script, but rather the
references to them from code or data. So we keep the label and add a
warning to the Messages list when we see a reference.
Moved NON_ADDR constants to Address class. AddressMap now has a copy.
This is awkward because Asm65 and CommonUtil don't share.
Updated the asm code generators to understand NON_ADDR, and reworked
the API so that Merlin and cc65 output is correct for nested regions.
Address region changes are now noted in the anattribs array, which
makes certain operations faster than checking the address map. It
also fixes a failure to recognize mid-instruction region changes in
the code analyzer.
Tweaked handling of synthetic regions, which are non-addressable areas
generated by the linear address map traversal to fill in any "holes".
The address region editor now treats attempts to edit them as
creation of a new region.
2021-10-01 01:07:21 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Address value to use for non-addressable regions of the file, such as file headers
|
|
|
|
|
/// stripped by the system loader or chunks loaded into non-addressable memory.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// If you change this value, also update the copy in CommonUtil.AddressMap.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public const int NON_ADDR = -1025;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Human-readable string that represents a non-addressable location.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This is a bit deep to bury a human-readable string, but it's useful to have the
|
|
|
|
|
/// value in one place.
|
|
|
|
|
/// </remarks>
|
|
|
|
|
public const string NON_ADDR_STR = "NA";
|
|
|
|
|
|
|
|
|
|
|
2018-09-28 17:05:11 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Converts a 16- or 24-bit address to a string.
|
|
|
|
|
/// </summary>
|
2021-09-22 21:39:39 +00:00
|
|
|
|
/// <param name="addr">Address</param>
|
|
|
|
|
/// <param name="always24">If true, force 24-bit output mode.</param>
|
|
|
|
|
/// <returns>Formatted string.</returns>
|
2018-09-28 17:05:11 +00:00
|
|
|
|
public static string AddressToString(int addr, bool always24) {
|
|
|
|
|
if (!always24 && addr < 65536) {
|
|
|
|
|
return addr.ToString("x4");
|
|
|
|
|
} else {
|
|
|
|
|
return (addr >> 16).ToString("x2") + "/" + (addr & 0xffff).ToString("x4");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Parses and validates a 16- or 24-bit address, expressed in hexadecimal. Bits
|
|
|
|
|
/// 16-23 may be specified with a slash.
|
|
|
|
|
///
|
|
|
|
|
/// The following all evaluate to the same thing: 1000, $1000, 0x1000, 00/1000.
|
|
|
|
|
/// </summary>
|
ORG rework, part 6
Added support for non-addressable regions, which are useful for things
like file headers stripped out by the system loader, or chunks that
get loaded into non-addressable graphics RAM. Regions are specified
with the "NA" address value. The code list displays the address field
greyed out, starting from zero (which is kind of handy if you want to
know the relative offset within the region).
Putting labels in non-addressable regions doesn't make sense, but
symbol resolution is complicated enough that we really only have two
options: ignore the labels entirely, or allow them but warn of their
presence. The problem isn't so much the label, which you could
legitimately want to access from an extension script, but rather the
references to them from code or data. So we keep the label and add a
warning to the Messages list when we see a reference.
Moved NON_ADDR constants to Address class. AddressMap now has a copy.
This is awkward because Asm65 and CommonUtil don't share.
Updated the asm code generators to understand NON_ADDR, and reworked
the API so that Merlin and cc65 output is correct for nested regions.
Address region changes are now noted in the anattribs array, which
makes certain operations faster than checking the address map. It
also fixes a failure to recognize mid-instruction region changes in
the code analyzer.
Tweaked handling of synthetic regions, which are non-addressable areas
generated by the linear address map traversal to fill in any "holes".
The address region editor now treats attempts to edit them as
creation of a new region.
2021-10-01 01:07:21 +00:00
|
|
|
|
/// <remarks>
|
|
|
|
|
/// This doesn't handle "NA", because most of the time we want to parse an actual address.
|
|
|
|
|
/// </remarks>
|
2018-09-28 17:05:11 +00:00
|
|
|
|
/// <param name="addrStr">String to validate.</param>
|
|
|
|
|
/// <param name="max">Maximum valid address value.</param>
|
|
|
|
|
/// <param name="addr">Integer form.</param>
|
|
|
|
|
/// <returns>True if the address is valid.</returns>
|
|
|
|
|
public static bool ParseAddress(string addrStr, int max, out int addr) {
|
|
|
|
|
string trimStr = addrStr.Trim(); // strip whitespace
|
|
|
|
|
if (trimStr.Length < 1) {
|
|
|
|
|
addr = -1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (trimStr[0] == '$') {
|
|
|
|
|
trimStr = trimStr.Remove(0, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int slashIndex = trimStr.IndexOf('/');
|
|
|
|
|
try {
|
|
|
|
|
if (slashIndex < 0) {
|
|
|
|
|
addr = Convert.ToInt32(trimStr, 16);
|
|
|
|
|
if (addr < 0 || addr > max) {
|
|
|
|
|
Debug.WriteLine("Simple value out of range");
|
|
|
|
|
addr = -1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
string[] splitStr = trimStr.Split('/');
|
|
|
|
|
if (splitStr.Length == 2) {
|
|
|
|
|
int addr1 = Convert.ToInt32(splitStr[0], 16);
|
|
|
|
|
int addr2 = Convert.ToInt32(splitStr[1], 16);
|
|
|
|
|
addr = (addr1 << 16) | addr2;
|
|
|
|
|
// Check components separately to catch overflow.
|
|
|
|
|
if (addr1 < 0 || addr1 > 255 || addr2 < 0 || addr2 > 65535 ||
|
|
|
|
|
addr > max) {
|
|
|
|
|
Debug.WriteLine("Slash value out of range");
|
|
|
|
|
addr = -1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
addr = -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception) {
|
|
|
|
|
// Thrown from Convert.ToInt32
|
|
|
|
|
//Debug.WriteLine("ValidateAddress: conversion of '" + addrStr + "' failed: " +
|
|
|
|
|
// ex.Message);
|
|
|
|
|
addr = -1;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//Debug.WriteLine("Conv " + addrStr + " --> " + addr.ToString("x6"));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|