68 files changed, 1503 insertions(+), 1420 deletions(-)

M README.md
M core/CMakeLists.txt
M core/core/io/file_helpers_linux.cpp
M core/core/io/file_id_linux.cpp
M core/core/strings/murmur_hash.h
M core/core/strings/utf8.cpp
M core/pch.h
M hasher/CMakeLists.txt
M hasher/pch.h
M jasm-6502/CMakeLists.txt
M jasm-z80/CMakeLists.txt
M jasm/assembling/assembler_impl/assembler_impl.cpp
M jasm/assembling/assembler_impl/assembler_impl.h
M jasm/assembling/assembler_impl/functions_impl.cpp
M jasm/assembling/assembler_impl/methods_impl.cpp
M jasm/assembling/assembler_impl/operators_impl.cpp
M jasm/assembling/assembler_impl/symbols_impl.cpp
M jasm/assembling/assembler_impl/syntax_impl.cpp
M jasm/assembling/functions.cpp
M jasm/assembling/functions.h
M jasm/assembling/instructions_6502.cpp
M jasm/assembling/instructions_6502.h
M jasm/assembling/instructions_z80.cpp
M jasm/assembling/instructions_z80.h
M jasm/assembling/methods.cpp
M jasm/assembling/methods.h
M jasm/assembling/value.cpp
M jasm/assembling/value.h
M jasm/docs/footer.html
M jasm/docs/header.html
M jasm/docs/jasm.md
M jasm/io/data_reader.cpp
M jasm/io/data_reader.h
M jasm/main.cpp
M jasm/parsing/keyword_finder.cpp
M jasm/parsing/keyword_finder.h
M jasm/parsing/keywords.cpp
M jasm/parsing/keywords.h
M jasm/parsing/operators.cpp
M jasm/parsing/operators.h
M jasm/parsing/processor_keywords_6502.cpp
M jasm/parsing/processor_keywords_6502.h
M jasm/parsing/processor_keywords_z80.cpp
M jasm/parsing/processor_keywords_z80.h
M jasm/parsing/syntax_tokens.cpp
M jasm/parsing/syntax_tokens.h
M jasm/parsing/token_chain.cpp
M jasm/parsing/tokenizer.cpp
M jasm/parsing/tokenizer.h
M jasm/parsing/types.cpp
M jasm/parsing/types.h
M jasm/pch.h
M jasm/strings/string_conversions.cpp
M jasm/strings/string_conversions.h
M jasm/strings/string_repository.cpp
M jasm/strings/string_repository.h
A => jasm/unit_tests/results/test_operator_high_byte_bank_mode.bin
A => jasm/unit_tests/results/test_string_conversion_with_high_bit_termination_property.bin
A => jasm/unit_tests/results/test_string_with_zero_character.bin
A => jasm/unit_tests/test_operator_high_byte_bank_mode.asm
A => jasm/unit_tests/test_string_conversion_with_high_bit_termination_property.asm
A => jasm/unit_tests/test_string_with_zero_character.asm
M jasm/version.h
M jasm/website/site/docs/index.html
M jasm/website/site/index.html
A => jasm/website/source/avatar.png
A => jasm/website/source/avatar.xcf
A => release.py
M README.md +4 -0
@@ 18,8 18,12 @@ Clone the repository into a directory ca
 	cmake -DCMAKE_BUILD_TYPE=Release ..
 	sudo make install
 
+More details about cross compiling for Windows is in the documentation.
+
 ## Visual Studio on Windows
 
+_This isn't actively supported and may not work out of the box._
+
 Download Visual Studio 2015 from www.microsoft.com and install it. Double click on the `jasm.sln` file to open the project. Select the `Release` configuration and build the solution. You will get a `jasm.exe` binary in `x64\Release`.
 
 # Running Unit Tests

          
M core/CMakeLists.txt +1 -1
@@ 22,7 22,7 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_F
 
 add_library(core STATIC ${core_src})
 
-set_property(TARGET core PROPERTY CXX_STANDARD 14)
+set_property(TARGET core PROPERTY CXX_STANDARD 17)
 set_property(TARGET core PROPERTY CXX_STANDARD_REQUIRED ON)
 
 target_include_directories(core PUBLIC "${CMAKE_CURRENT_LIST_DIR}")

          
M core/core/io/file_helpers_linux.cpp +1 -0
@@ 4,6 4,7 @@ 
 
 #include <core/io/file_helpers.h>
 #include <core/strings/utf8.h>
+#include <cstring>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>

          
M core/core/io/file_id_linux.cpp +1 -0
@@ 4,6 4,7 @@ 
 
 #include <core/io/file_id.h>
 #include <core/strings/utf8.h>
+#include <cstring>
 #include <sys/stat.h>
 #include <unistd.h>
 

          
M core/core/strings/murmur_hash.h +8 -0
@@ 1,5 1,7 @@ 
 #pragma once
 
+#include <string>
+
 namespace core
 {
 

          
@@ 37,6 39,12 @@ inline uint64_t murmur_hash3_string_x64_
 	return murmur_hash3_string_x64_64(str.c_str(), static_cast<int>(str.size()), seed);
 }
 
+/// Hash a string with optional seed.
+inline uint64_t murmur_hash3_string_x64_64(const std::wstring_view &str, const uint64_t seed = 0)
+{
+	return murmur_hash3_string_x64_64(str.data(), static_cast<int>(str.size()), seed);
+}
+
 /// Hash a static string without specifying its size.
 template<int N>
 uint64_t murmur_hash3_string_x64_64(const wchar_t (&str)[N], const uint64_t seed = 0)

          
M core/core/strings/utf8.cpp +1 -0
@@ 5,6 5,7 @@ 
 #if defined(_MSC_VER)
 	#include <codecvt>
 #endif
+#include <cstring>
 #include <locale>
 
 namespace core

          
M core/pch.h +0 -1
@@ 43,7 43,6 @@ 
 #endif
 #include <array>
 #include <cassert>
-#include <cstring>
 #include <string>
 #include <vector>
 #include <map>

          
M hasher/CMakeLists.txt +1 -1
@@ 20,7 20,7 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_F
 
 add_executable(hasher ${hasher_src})
 
-set_property(TARGET hasher PROPERTY CXX_STANDARD 14)
+set_property(TARGET hasher PROPERTY CXX_STANDARD 17)
 set_property(TARGET hasher PROPERTY CXX_STANDARD_REQUIRED ON)
 
 target_link_libraries(hasher core)

          
M hasher/pch.h +0 -1
@@ 37,7 37,6 @@ 
 	#include <windows.h>
 #endif
 #include <cassert>
-#include <cstring>
 #include <string>
 #include <vector>
 #include <unordered_map>

          
M jasm-6502/CMakeLists.txt +1 -1
@@ 25,7 25,7 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_
 
 add_executable(jasm-6502 ${jasm_src})
 
-set_property(TARGET jasm-6502 PROPERTY CXX_STANDARD 14)
+set_property(TARGET jasm-6502 PROPERTY CXX_STANDARD 17)
 set_property(TARGET jasm-6502 PROPERTY CXX_STANDARD_REQUIRED ON)
 
 target_link_libraries(jasm-6502 core)

          
M jasm-z80/CMakeLists.txt +1 -1
@@ 25,7 25,7 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_
 
 add_executable(jasm-z80 ${jasm_src})
 
-set_property(TARGET jasm-z80 PROPERTY CXX_STANDARD 14)
+set_property(TARGET jasm-z80 PROPERTY CXX_STANDARD 17)
 set_property(TARGET jasm-z80 PROPERTY CXX_STANDARD_REQUIRED ON)
 
 target_link_libraries(jasm-z80 core)

          
M jasm/assembling/assembler_impl/assembler_impl.cpp +17 -10
@@ 398,7 398,7 @@ void Assembler::setup_predefined_constan
 	// add functions
 	for(int i = 0; i < static_cast<int>(FunctionType::NumTypes); ++i) {
 		FunctionType f = static_cast<FunctionType>(i);
-		const wchar_t *function_name = to_string(f);
+		const std::wstring_view function_name = to_string(f);
 		symbol_hash = murmur_hash3_string_x64_64(function_name);
 		if (!_strings.has(symbol_hash))
 			_strings.add(symbol_hash, function_name);

          
@@ 868,8 868,11 @@ struct SimpleSymbolInformation
 	uint32_t addr; ///< Label address.
 };
 
-bool starts_with_breakpoint(const wchar_t *symbol, const std::wstring &beginning)
+bool starts_with_breakpoint(const std::wstring_view &symbol, const std::wstring &beginning)
 {
+	if (symbol.size() < beginning.size()) {
+		return false;
+	}
 	for(size_t i = 0; i < beginning.size(); ++i) {
 		if (symbol[i] != beginning[i])
 			return false;

          
@@ 911,9 914,9 @@ void Assembler::dump_vice_symbols(const 
 		if (UNLIKELY(!is_integer(value)))
 			continue;
 
-		const wchar_t *name_str = _strings.get(it->second);
+		const std::wstring_view name_str = _strings.get(it->second);
 		// skip automatic labels since they may override more interesting names
-		if (name_str[0] == L'@')
+		if (name_str.front() == L'@')
 			continue;
 
 		uint32_t integer_value = static_cast<uint32_t>(dereference_integer(value));

          
@@ 962,7 965,9 @@ void Assembler::dump_vice_symbols(const 
 		uint64_t name = symbol.name;
 		if (used_names.find(name) != used_names.end()) {
 			// symbol exists, try combining the index in the name
-			std::wstring name_with_index = _strings.get(name) + std::wstring(L"_") + std::to_wstring(symbol.index);
+			std::wstring name_with_index(_strings.get(name));
+			name_with_index.append(1, '_');
+			name_with_index.append(std::to_wstring(symbol.index));
 			name = murmur_hash3_string_x64_64(name_with_index);
 			// give up if still colliding
 			if (used_names.find(name) != used_names.end())

          
@@ 973,7 978,7 @@ void Assembler::dump_vice_symbols(const 
 		used_values.insert(symbol.addr);
 		used_names.insert(name);
 
-		const wchar_t *name_str = _strings.get(name);
+		const std::wstring_view name_str = _strings.get(name);
 		ss << L"al C:" << std::setw(4) << symbol.addr << std::setw(0) << L" ." << name_str << L'\n';
 	}
 

          
@@ 1013,9 1018,9 @@ void Assembler::dump_gba_symbols(const s
 		if (UNLIKELY(!is_integer(value)))
 			continue;
 
-		const wchar_t *name_str = _strings.get(it->second);
+		const std::wstring_view name_str = _strings.get(it->second);
 		// skip automatic labels since they may override more interesting names
-		if (name_str[0] == L'@')
+		if (name_str.front() == L'@')
 			continue;
 
 		uint32_t integer_value = static_cast<uint32_t>(dereference_integer(value));

          
@@ 1038,7 1043,9 @@ void Assembler::dump_gba_symbols(const s
 		uint64_t name = symbol.name;
 		if (used_names.find(name) != used_names.end()) {
 			// symbol exists, try combining the index in the name
-			std::wstring name_with_index = _strings.get(name) + std::wstring(L"_") + std::to_wstring(symbol.index);
+			std::wstring name_with_index(_strings.get(name));
+			name_with_index.append(1, L'_');
+			name_with_index.append(std::to_wstring(symbol.index));
 			name = murmur_hash3_string_x64_64(name_with_index);
 			// give up if still colliding
 			if (used_names.find(name) != used_names.end())

          
@@ 1049,7 1056,7 @@ void Assembler::dump_gba_symbols(const s
 		used_values.insert(symbol.addr);
 		used_names.insert(name);
 
-		const wchar_t *name_str = _strings.get(name);
+		const std::wstring_view name_str = _strings.get(name);
 		ss << std::setw(4) << (symbol.addr >> 16) << L":" << (symbol.addr & 0xffff) << std::setw(0) << L" " << name_str << L'\n';
 	}
 

          
M jasm/assembling/assembler_impl/assembler_impl.h +10 -14
@@ 331,12 331,7 @@ private:
 	/// Creating a type is a two step rocket.
 	/// 1) Allocate and clear the type space (this call)
 	/// 2) Generate a hash to use as a type reference (add_type_to_type_map)
-	inline TypeDescriptionWithPayload &reserve_type(uint32_t num_properties) {
-		uint32_t type_size = type_description_size(num_properties);
-		TypeDescriptionWithPayload &type = *static_cast<TypeDescriptionWithPayload *>(_current_pass.types.reserve(type_size));
-		memset(&type, 0, type_size);
-		return type;
-	}
+	TypeDescriptionWithPayload &reserve_type(uint32_t num_properties);
 
 	inline uint64_t add_type_to_type_map(const TypeDescription &type, TokenReadPosition position, uint64_t seed = 0) {
 		uint64_t hash = type.hash(seed);

          
@@ 374,7 369,7 @@ private:
 	double dereference_float(const Value &value) const;
 	/// Return the char pointer value of a string value. The value argument
 	/// must be a string value or reference to an string value.
-	const wchar_t *dereference_string(const Value &value) const;
+	std::wstring_view dereference_string(const Value &value) const;
 	/// Return the string hash of a string value. The value argument
 	/// must be a string value or reference to an string value.
 	uint64_t dereference_string_hash(const Value &value) const;

          
@@ 387,8 382,9 @@ private:
 	void set_integer(Value &result, int32_t value) const;
 	void set_float(Value &result, double value) const;
 	void set_string(Value &result, uint64_t value) const;
+	void set_string(Value &result, const std::wstring_view &value) const;
 	void set_string(Value &result, const std::wstring &value) const;
-	void set_string(Value &result, const std::wstring &&value) const;
+	void set_string(Value &result, std::wstring &&value) const;
 	void set_range(Value &result, int32_t value, int32_t size) const;
 	void set_function(Value &result, FunctionType f) const;
 	void set_method(Value &result, MethodType method, const Value &object) const;

          
@@ 403,7 399,7 @@ private:
 	using BooleanOperation = bool (*)(bool a, bool b);
 	using IntegerCompareOperation = bool (*)(int32_t a, int32_t b);
 	using FloatCompareOperation = bool (*)(double a, double b);
-	using StringCompareOperation = bool (*)(const wchar_t *a, const wchar_t *b);
+	using StringCompareOperation = bool (*)(const std::wstring_view &a, const std::wstring_view &b);
 
 	/// Get left value, result and the index of the applied property. Returns true if successful or false (with result set to unknown) if it to find the property.
 	bool get_operator_period_property_index(bool generate, const ExpressionComponent components[], Value &result, const Value &arg1, uint32_t next_index, uint32_t &key_index);

          
@@ 654,7 650,7 @@ private:
 	/// @param format_string A pointer to the character after the initial curly bracket. This will be modified to point after the closing bracket if parsing was successful.
 	/// @param format Will be updated with the parsing information if parsing was successful.
 	/// @return True if the parsing was successful.
-	bool parse_argument_format(bool generate, const SourceLocation &format_location, const wchar_t *&format_string, ArgFormat &format);
+	bool parse_argument_format(bool generate, const SourceLocation &format_location, std::wstring_view &format_string, ArgFormat &format);
 
 	/// Generate a string based on the specified format.
 	/// @param depth Recursion depth to stop recursion because objects can contain references to themselves.

          
@@ 718,10 714,10 @@ private:
 	/// @param hash Will be set to the symbol hash, if correctly formatted.
 	/// @param global Will be set according to symbol type, if correctly formatted.
 	/// @return True if the string is correctly formatted.
-	static bool parse_symbol_string(const std::wstring &symbol, uint64_t &hash, bool &global);
+	static bool parse_symbol_string(const std::wstring_view &symbol, uint64_t &hash, bool &global);
 
 	/// Store a symbol name in the string repository, taking away the initial '.' on a local symbol before.
-	void store_symbol_string_in_repository(uint64_t hash, const std::wstring &symbol);
+	void store_symbol_string_in_repository(uint64_t hash, const std::wstring_view &symbol);
 
 	/// Store a symbol in the current pass using a prehashed symbol name (with namespace or instance hash).
 	void store_symbol_value(uint64_t symbol_hash, const Value &value, bool global, bool is_address, StorageType type);

          
@@ 936,9 932,9 @@ private:
 	inline std::wstring variable_name(uint64_t symbol_hash, bool global)
 	{
 		if (global)
-			return _strings.get_string(symbol_hash);
+			return std::wstring(_strings.get(symbol_hash));
 		else
-			return L"." + _strings.get_string(symbol_hash);
+			return L"." + std::wstring(_strings.get(symbol_hash));
 	}
 
 	static inline std::wstring instance_path(const SourceLocation &source_location)

          
M jasm/assembling/assembler_impl/functions_impl.cpp +39 -34
@@ 515,8 515,9 @@ void Assembler::function_string(bool gen
 		StringConversions::Format format = StringConversions::Format::Invalid;
 		StringConversions::SubFormat subformat = StringConversions::SubFormat::Default;
 		StringConversions::Locale locale = StringConversions::Locale::Default;
+		StringConversions::Flags flags = StringConversions::None;
 
-		// loop through all the rest arguments and interpret as format/subformat/locale
+		// loop through all the rest arguments and interpret as format/subformat/locale/flag
 		uint32_t current_arg_index = arg2_index;
 		bool valid_formats = true;
 		bool has_format = false;

          
@@ 525,12 526,14 @@ void Assembler::function_string(bool gen
 		StringConversions::Format this_format = StringConversions::Format::Invalid;
 		StringConversions::SubFormat this_subformat = StringConversions::SubFormat::Default;
 		StringConversions::Locale this_locale = StringConversions::Locale::Default;
+		StringConversions::Flags this_flag = StringConversions::None;
+	
 		while(current_arg_index != 0) {
 
 			const Value &conversion_value = follow_reference_or_value(expression_values[current_arg_index]);
 			if (LIKELY(is_string(conversion_value))) {
 
-				const wchar_t *format_string = dereference_string(conversion_value);
+				const std::wstring_view format_string = dereference_string(conversion_value);
 				bool duplicate = false;
 				const wchar_t *duplicate_property = nullptr;
 				if (_string_conversions.is_format(format_string, this_format)) {

          
@@ 548,6 551,10 @@ void Assembler::function_string(bool gen
 					duplicate = has_locale;
 					duplicate_property = L"Locale";
 					has_locale = true;
+				} else if (_string_conversions.is_flag(format_string, this_flag)) {
+					duplicate = (flags & this_flag) != 0;
+					duplicate_property = L"Flag";
+					flags = static_cast<StringConversions::Flags>(flags | this_flag);
 				} else {
 					if (generate) {
 						const ExpressionComponent &ec = components[leftmost_node_in_expression(components, current_arg_index)];

          
@@ 586,7 593,7 @@ void Assembler::function_string(bool gen
 			if (_string_conversions.has_conversion(format, subformat, locale)) {
 				size_t error_pos = 0;
 				std::wstring converted_result;
-				if (_string_conversions.convert(unconverted_result, format, subformat, locale, converted_result, error_pos)) {
+				if (_string_conversions.convert(unconverted_result, format, subformat, locale, flags, converted_result, error_pos)) {
 					set_string(result, converted_result);
 					return;
 				} else {

          
@@ 826,7 833,7 @@ void Assembler::function_static_assert(b
 		return;
 
 	// assert failed
-	const wchar_t *string = dereference_string(value2);
+	const std::wstring_view string = dereference_string(value2);
 
 	const ExpressionComponent &ec = components[op];
 	std::wstringstream ss;

          
@@ 868,37 875,39 @@ void Assembler::function_print(bool gene
 };
 
 
-bool parse_format_number(const wchar_t *&format_string, int32_t &result)
+bool parse_format_number(std::wstring_view &format_string, int32_t &result)
 {
-	wchar_t c = *format_string;
+	if (format_string.empty()) {
+		return false;
+	}
+	wchar_t c = format_string.front();
 	if (c < L'0' || c > L'9')
 		return false;
 
 	result = c - L'0';
-	++format_string;
-	while (true) {
-		c = *format_string;
+	format_string.remove_prefix(1);
+	while (!format_string.empty()) {
+		c = format_string.front();
 		if (c < L'0' || c > L'9')
 			break;
 		result = result * 10 + (c - L'0');
-		++format_string;
+		format_string.remove_prefix(1);
 	}
 
 	return true;
 }
 
 
-bool Assembler::parse_argument_format(bool generate, const SourceLocation &format_location, const wchar_t *&format_string, ArgFormat &format)
+bool Assembler::parse_argument_format(bool generate, const SourceLocation &format_location, std::wstring_view &format_string, ArgFormat &format)
 {
-	while (*format_string != L'}') {
-		wchar_t c = *format_string;
+	while (!format_string.empty()) {
+		wchar_t c = format_string.front();
+		format_string.remove_prefix(1);
 
-		if (c == 0) {
-			std::wstringstream ss;
-			ss << L"Missing closing bracket in format specifier.";
-			report_error(format_location, AssemblyErrorCodes::UnexpectedEndOfFormatSpecifier, ss.str());
-			return false;
+		if (c == L'}') {
+			return true;
 		}
+
 		if (c == L'L' || c == L'R') {
 			// alignment format
 			if (format.align != ArgFormat::Align::OFF) {

          
@@ 912,7 921,6 @@ bool Assembler::parse_argument_format(bo
 			}
 			format.align = (c == L'L' ? ArgFormat::Align::LEFT : ArgFormat::Align::RIGHT);
 
-			++format_string;
 			int32_t width = 0;
 			if (!parse_format_number(format_string, width)) {
 				if (generate) {

          
@@ 937,7 945,6 @@ bool Assembler::parse_argument_format(bo
 			}
 			format.format = (c == L'D' ? ArgFormat::NumberFormat::DECIMAL : (c == L'X' ? ArgFormat::NumberFormat::HEX : ArgFormat::NumberFormat::FIXED_POINT));
 
-			++format_string;
 			int32_t width = 0;
 			if (!parse_format_number(format_string, width)) {
 				if (generate) {

          
@@ 960,10 967,10 @@ bool Assembler::parse_argument_format(bo
 		}
 	}
 
-	// skip curly bracket
-	++format_string;
-
-	return true;
+	std::wstringstream ss;
+	ss << L"Missing closing bracket in format specifier.";
+	report_error(format_location, AssemblyErrorCodes::UnexpectedEndOfFormatSpecifier, ss.str());
+	return false;
 }
 
 bool Assembler::argument_to_string(bool generate, const SourceLocation &format_location, const ArgFormat &format, const Value &arg_value, std::wstringstream &arg_string, uint32_t depth)

          
@@ 1128,19 1135,21 @@ bool Assembler::parse_format_arguments(b
 		return false;
 	}
 
-	const wchar_t *format_string = dereference_string(value1);
+	std::wstring_view format_string = dereference_string(value1);
 
 	// start generating a new string based on the format and arguments
 	std::wstringstream result_string;
 	uint32_t current_arg = arg1;
 	const ExpressionComponent &arg1_component = components[leftmost_node_in_expression(components, arg1)];
 
-	while(*format_string != 0) {
-		wchar_t c = *format_string;
-		if (c == L'\\' && format_string[1] == L'{') {
+	while(!format_string.empty()) {
+		wchar_t c = format_string.front();
+		format_string.remove_prefix(1);
+
+		if (c == L'\\' && !format_string.empty() && format_string.front() == L'{') {
 			// escaped curly bracket
 			result_string << L'{';
-			format_string += 2;
+			format_string.remove_prefix(1);
 			continue;
 		}
 

          
@@ 1159,9 1168,6 @@ bool Assembler::parse_format_arguments(b
 				return false;
 			}
 
-			// Skip initial bracket
-			++format_string;
-
 			// parse the format
 			ArgFormat format;
 			if (!parse_argument_format(generate, arg1_component.source_location, format_string, format))

          
@@ 1194,7 1200,6 @@ bool Assembler::parse_format_arguments(b
 		}
 
 		result_string << c;
-		++format_string;
 	}
 
 	current_arg = components[current_arg].next_sibling;

          
@@ 1231,7 1236,7 @@ void Assembler::function_symbol(bool gen
 	// validate string contents
 	uint64_t hash;
 	bool global;
-	std::wstring symbol_string = dereference_string(value1);
+	const std::wstring_view symbol_string = dereference_string(value1);
 	if (!parse_symbol_string(symbol_string, hash, global)) {
 		if (generate) {
 			const ExpressionComponent &ec = components[leftmost_node_in_expression(components, arg1)];

          
M jasm/assembling/assembler_impl/methods_impl.cpp +1 -1
@@ 40,7 40,7 @@ void Assembler::method_string_substring(
 		return;
 	}
 
-	std::wstring source_str = dereference_string(object);
+	const std::wstring_view source_str = dereference_string(object);
 
 	// detect start/end
 	core::Range<int32_t> range;

          
M jasm/assembling/assembler_impl/operators_impl.cpp +22 -19
@@ 2,7 2,6 @@ 
 
 #include <assembling/assembler_impl/assembler_impl.h>
 #include <core/ownership/destruct_call.h>
-#include <cstring>
 
 namespace jasm {
 

          
@@ 323,9 322,10 @@ void Assembler::operator_string_add(bool
 	if (!verify_string_argument(generate, components[operator_index], arg2, result))
 		return;
 
-	std::wstring a = dereference_string(arg1);
-	std::wstring b = dereference_string(arg2);
-	set_string(result, a+b);
+	std::wstring a = std::wstring(dereference_string(arg1));
+	const std::wstring_view b = dereference_string(arg2);
+	a.append(b);
+	set_string(result, a);
 }
 
 void Assembler::operator_float_subtract(bool generate, ValueVector &/*expression_values*/, const ExpressionComponent components[], uint32_t operator_index, Value &result, Value &arg1, const Value &arg2)

          
@@ 469,8 469,8 @@ void Assembler::compare_string_types(boo
 	if (!verify_comparable_argument_type(generate, components[operator_index], arg1, arg2, result))
 		return;
 
-	const wchar_t *a = dereference_string(arg1);
-	const wchar_t *b = dereference_string(arg2);
+	const std::wstring_view a = dereference_string(arg1);
+	const std::wstring_view b = dereference_string(arg2);
 	set_boolean(result, comp(a, b));
 }
 

          
@@ 490,7 490,7 @@ void Assembler::operator_float_equal_to(
 
 void Assembler::operator_string_equal_to(bool generate, ValueVector &/*expression_values*/, const ExpressionComponent components[], uint32_t operator_index, Value &result, Value &arg1, const Value &arg2)
 {
-	auto comp = [](const wchar_t *a, const wchar_t *b) { return wcscmp(a, b) == 0; };
+	auto comp = [](const std::wstring_view &a, const std::wstring_view &b) { return a == b; };
 	compare_string_types(generate, components, operator_index, result, arg1, arg2, comp);
 }
 

          
@@ 520,7 520,7 @@ void Assembler::operator_float_not_equal
 
 void Assembler::operator_string_not_equal_to(bool generate, ValueVector &/*expression_values*/, const ExpressionComponent components[], uint32_t operator_index, Value &result, Value &arg1, const Value &arg2)
 {
-	auto comp = [](const wchar_t *a, const wchar_t *b) { return wcscmp(a, b) != 0; };
+	auto comp = [](const std::wstring_view &a, const std::wstring_view &b) { return a != b; };
 	compare_string_types(generate, components, operator_index, result, arg1, arg2, comp);
 }
 

          
@@ 582,25 582,25 @@ void Assembler::operator_float_less_or_e
 
 void Assembler::operator_string_greater(bool generate, ValueVector &/*expression_values*/, const ExpressionComponent components[], uint32_t operator_index, Value &result, Value &arg1, const Value &arg2)
 {
-	auto comp = [](const wchar_t *a, const wchar_t *b) { return wcscmp(a, b) > 0; };
+	auto comp = [](const std::wstring_view &a, const std::wstring_view &b) { return a > b; };
 	compare_string_types(generate, components, operator_index, result, arg1, arg2, comp);
 }
 
 void Assembler::operator_string_less(bool generate, ValueVector &/*expression_values*/, const ExpressionComponent components[], uint32_t operator_index, Value &result, Value &arg1, const Value &arg2)
 {
-	auto comp = [](const wchar_t *a, const wchar_t *b) { return wcscmp(a, b) < 0; };
+	auto comp = [](const std::wstring_view &a, const std::wstring_view &b) { return a < b; };
 	compare_string_types(generate, components, operator_index, result, arg1, arg2, comp);
 }
 
 void Assembler::operator_string_greater_or_equal(bool generate, ValueVector &/*expression_values*/, const ExpressionComponent components[], uint32_t operator_index, Value &result, Value &arg1, const Value &arg2)
 {
-	auto comp = [](const wchar_t *a, const wchar_t *b) { return wcscmp(a, b) >= 0; };
+	auto comp = [](const std::wstring_view &a, const std::wstring_view &b) { return a >= b; };
 	compare_string_types(generate, components, operator_index, result, arg1, arg2, comp);
 }
 
 void Assembler::operator_string_less_or_equal(bool generate, ValueVector &/*expression_values*/, const ExpressionComponent components[], uint32_t operator_index, Value &result, Value &arg1, const Value &arg2)
 {
-	auto comp = [](const wchar_t *a, const wchar_t *b) { return wcscmp(a, b) <= 0; };
+	auto comp = [](const std::wstring_view &a, const std::wstring_view &b) { return a <= b; };
 	compare_string_types(generate, components, operator_index, result, arg1, arg2, comp);
 }
 

          
@@ 653,8 653,11 @@ void Assembler::operator_unary_low_byte(
 
 void Assembler::operator_unary_high_byte(bool /*generate*/, ValueVector &/*expression_values*/, const ExpressionComponent /*components*/[], uint32_t /*operator_index*/, Value &result, Value &arg1, uint32_t /*next_index*/)
 {
-	int32_t a = dereference_integer(arg1);
-	set_integer(result, a >> 8);
+	int32_t a = dereference_integer(arg1) >> 8;
+	if (_multi_bank_mode) {
+		a = a & 0xff;
+	}
+	set_integer(result, a);
 }
 
 void Assembler::operator_unary_prefix_increment(bool /*generate*/, ValueVector &/*expression_values*/, const ExpressionComponent /*components*/[], uint32_t /*operator_index*/, Value &result, Value &ref, Value &arg1, uint32_t /*next_index*/)

          
@@ 949,11 952,11 @@ void Assembler::operator_string_array_ac
 		return;
 	}
 
-	int32_t array_index = dereference_integer(arg2);
+	int64_t array_index = dereference_integer(arg2);
 
 	// check array index boundaries
-	const wchar_t *str = dereference_string(arg1);
-	int32_t length = static_cast<int32_t>(std::wcslen(str));
+	const std::wstring_view str = dereference_string(arg1);
+	int64_t length = static_cast<int64_t>(str.size());
 	if (array_index < 0 || array_index >= length) {
 		if (generate) {
 			const ExpressionComponent &operator_component = components[operator_index];

          
@@ 966,7 969,7 @@ void Assembler::operator_string_array_ac
 	}
 
 	// now return the character
-	set_integer(result, static_cast<int32_t>(str[array_index]));
+	set_integer(result, static_cast<int32_t>(str[static_cast<size_t>(array_index)]));
 }
 
 void Assembler::operator_list_array_access(bool generate, ValueVector &/*expression_values*/, const ExpressionComponent components[], uint32_t operator_index, Value &result, Value &arg1_ref, Value &arg1, const Value &arg2)

          
@@ 1488,7 1491,7 @@ void Assembler::operator_string_period(b
 	case StringProperties::Length:
 		size_t length;
 		if (arg1.type == ValueType::StringValue)
-			length = std::wcslen(dereference_string(arg1));
+			length = dereference_string(arg1).size();
 		else
 			length = arg1.object_reference.deref<StringDynamicObject>().value.size();
 		set_integer(result, static_cast<int32_t>(length));

          
M jasm/assembling/assembler_impl/symbols_impl.cpp +30 -9
@@ 1,6 1,7 @@ 
 #include "pch.h"
 
 #include <assembling/assembler_impl/assembler_impl.h>
+#include <cstring>
 #include <locale>
 
 namespace jasm {

          
@@ 77,6 78,17 @@ void Assembler::set_string(Value &result
 	result.object_reference.clear();
 }
 
+void Assembler::set_string(Value &result, const std::wstring_view &value) const
+{
+	result.set_found_in_current_pass(false);
+	result.union_space1 = 0;
+	result.union_space2 = 0;
+
+	result.type = ValueType::StringReference;
+	result.type_hash = _static_string_reference_type;
+	result.object_reference = new StringDynamicObject(value);
+}
+
 void Assembler::set_string(Value &result, const std::wstring &value) const
 {
 	result.set_found_in_current_pass(false);

          
@@ 88,7 100,7 @@ void Assembler::set_string(Value &result
 	result.object_reference = new StringDynamicObject(value);
 }
 
-void Assembler::set_string(Value &result, const std::wstring &&value) const
+void Assembler::set_string(Value &result, std::wstring &&value) const
 {
 	result.set_found_in_current_pass(false);
 	result.union_space1 = 0;

          
@@ 227,12 239,12 @@ double Assembler::dereference_float(cons
 	return 666.0;
 }
 
-const wchar_t *Assembler::dereference_string(const Value &value) const
+std::wstring_view Assembler::dereference_string(const Value &value) const
 {
 	if (value.type == ValueType::StringValue)
 		return _strings.get(value.string_hash);
 	if (value.type == ValueType::StringReference)
-		return value.object_reference.deref<StringDynamicObject>().value.c_str();
+		return value.object_reference.deref<StringDynamicObject>().value;
 	if (value.is_value_reference())
 		return dereference_string(follow_reference(value));
 	// the caller should check for this case!

          
@@ 268,7 280,7 @@ uint64_t Assembler::combine_namespace(ui
 	return seed;
 }
 
-bool Assembler::parse_symbol_string(const std::wstring &symbol, uint64_t &hash, bool &global)
+bool Assembler::parse_symbol_string(const std::wstring_view &symbol, uint64_t &hash, bool &global)
 {
 	size_t offset = 0;
 

          
@@ 307,15 319,16 @@ bool Assembler::parse_symbol_string(cons
 	return true;
 }
 
-void Assembler::store_symbol_string_in_repository(uint64_t hash, const std::wstring &symbol)
+void Assembler::store_symbol_string_in_repository(uint64_t hash, const std::wstring_view &symbol)
 {
 	if (_strings.has(hash))
 		return;
 
-	if (symbol[0] == L'.')
-		_strings.add(hash, &symbol[1], static_cast<int>(symbol.size())); // -1 for '.' but +1 for null termination
-	else
+	if (symbol.front() == L'.') {
+		_strings.add(hash, symbol.substr(1, std::wstring_view::npos));
+	} else {
 		_strings.add(hash, symbol);
+	}
 }
 
 void Assembler::store_symbol_value(uint64_t symbol_hash, const Value &value, bool global, bool is_address, StorageType type)

          
@@ 634,6 647,14 @@ void Assembler::evaluate_symbol(bool gen
 	}
 }
 
+TypeDescriptionWithPayload &Assembler::reserve_type(uint32_t num_properties)
+{
+	uint32_t type_size = type_description_size(num_properties);
+	TypeDescriptionWithPayload &type = *static_cast<TypeDescriptionWithPayload *>(_current_pass.types.reserve(type_size));
+	memset(&type, 0, type_size);
+	return type;
+}
+
 const TypeDescriptionWithPayload &Assembler::find_type(uint64_t type_hash) const
 {
 	auto it = _current_pass.type_lookup.find(type_hash);

          
@@ 694,7 715,7 @@ uint64_t Assembler::enter_namespace(uint
 	uint64_t combined_hash = murmur_hash3_x64_64(&namespace_hash, sizeof(namespace_hash), _symbol_environment.namespace_scope_stack.back());
 
 	if (UNLIKELY(_dump_symbols))
-		combine_and_store_hash_name(_symbol_environment.namespace_scope_stack.back(), combined_hash, _strings.get_string(namespace_hash));
+		combine_and_store_hash_name(_symbol_environment.namespace_scope_stack.back(), combined_hash, std::wstring(_strings.get(namespace_hash)));
 
 	_symbol_environment.namespace_scope_stack.push_back(combined_hash);
 	_symbol_environment.namespace_names_scope_stack.push_back(namespace_hash);

          
M jasm/assembling/assembler_impl/syntax_impl.cpp +2 -2
@@ 415,7 415,7 @@ const SyntaxToken *Assembler::parse_decl
 		}
 
 		// verify string format
-		std::wstring symbol_string = dereference_string(symbol_name);
+		const std::wstring_view symbol_string = dereference_string(symbol_name);
 		if (!parse_symbol_string(symbol_string, symbol_hash, global)) {
 			if (generate) {
 				std::wstringstream ss;

          
@@ 1163,7 1163,7 @@ const SyntaxToken *Assembler::parse_defi
 			// special case for arrays of fundamental types, which supports strings as input
 			if (is_string(expr_value)) {
 				// define a range of objects
-				std::wstring s = dereference_string(expr_value);
+				std::wstring_view s = dereference_string(expr_value);
 
 				for(wchar_t c : s) {
 					if (!define_single_fundamental_type(generate, element_type.type, static_cast<int32_t>(c), expr->source_location)) {

          
M jasm/assembling/functions.cpp +45 -45
@@ 60,55 60,55 @@ const FunctionDesc &function_info(Functi
 	return desc[static_cast<int>(type)];
 }
 
-const wchar_t *to_string(FunctionType type)
+const std::wstring_view to_string(FunctionType type)
 {
-	static const wchar_t *names[] = {
-		L"sizeof",
-		L"offsetof",
-		L"sin",
-		L"cos",
-		L"tan",
-		L"sinh",
-		L"cosh",
-		L"tanh",
-		L"asin",
-		L"acos",
-		L"atan",
-		L"atan2",
-		L"sqrt",
-		L"pow",
-		L"log",
-		L"log10",
-		L"exp",
-		L"abs",
-		L"floor",
-		L"ceil",
-		L"round",
-		L"modulo",
-		L"remainder",
-		L"degrees",
-		L"radians",
-		L"min",
-		L"max",
-		L"clamp",
-		L"lerp",
-		L"int",
-		L"float",
-		L"string",
-		L"hexstring",
-		L"unicode",
-		L"list",
-		L"map",
-		L"static_assert",
-		L"format",
-		L"print",
-		L"symbol",
-		L"select",
+	static const std::wstring_view names[] = {
+		std::wstring_view(L"sizeof"),
+		std::wstring_view(L"offsetof"),
+		std::wstring_view(L"sin"),
+		std::wstring_view(L"cos"),
+		std::wstring_view(L"tan"),
+		std::wstring_view(L"sinh"),
+		std::wstring_view(L"cosh"),
+		std::wstring_view(L"tanh"),
+		std::wstring_view(L"asin"),
+		std::wstring_view(L"acos"),
+		std::wstring_view(L"atan"),
+		std::wstring_view(L"atan2"),
+		std::wstring_view(L"sqrt"),
+		std::wstring_view(L"pow"),
+		std::wstring_view(L"log"),
+		std::wstring_view(L"log10"),
+		std::wstring_view(L"exp"),
+		std::wstring_view(L"abs"),
+		std::wstring_view(L"floor"),
+		std::wstring_view(L"ceil"),
+		std::wstring_view(L"round"),
+		std::wstring_view(L"modulo"),
+		std::wstring_view(L"remainder"),
+		std::wstring_view(L"degrees"),
+		std::wstring_view(L"radians"),
+		std::wstring_view(L"min"),
+		std::wstring_view(L"max"),
+		std::wstring_view(L"clamp"),
+		std::wstring_view(L"lerp"),
+		std::wstring_view(L"int"),
+		std::wstring_view(L"float"),
+		std::wstring_view(L"string"),
+		std::wstring_view(L"hexstring"),
+		std::wstring_view(L"unicode"),
+		std::wstring_view(L"list"),
+		std::wstring_view(L"map"),
+		std::wstring_view(L"static_assert"),
+		std::wstring_view(L"format"),
+		std::wstring_view(L"print"),
+		std::wstring_view(L"symbol"),
+		std::wstring_view(L"select"),
 	};
-	static_assert(sizeof(names) / sizeof(names[0]) == static_cast<int>(FunctionType::NumTypes), "Number of functions doesn't match number of strings");
+	static_assert(sizeof(names) / sizeof(names[0]) == static_cast<size_t>(FunctionType::NumTypes), "Number of functions doesn't match number of strings");
 
 	assert(type < FunctionType::NumTypes);
-	return names[static_cast<int>(type)];
+	return names[static_cast<size_t>(type)];
 }
 
 } // namespace jasm

          
M jasm/assembling/functions.h +1 -1
@@ 60,7 60,7 @@ struct FunctionDesc {
 /// Returns information about a function.
 const FunctionDesc &function_info(FunctionType type);
 
-const wchar_t *to_string(FunctionType type);
+const std::wstring_view to_string(FunctionType type);
 
 /// @}
 

          
M jasm/assembling/instructions_6502.cpp +76 -76
@@ 235,92 235,92 @@ bool is_ending_instruction(InstructionTy
 		|| type == InstructionType::Rts;
 }
 
-const wchar_t *to_string(AddressingModeType type)
+const std::wstring_view to_string(AddressingModeType type)
 {
-	const static wchar_t *names[] = {
-		L"zero page address",
-		L"absolute address",
-		L"zero page address with x offset",
-		L"absolute address with x offset",
-		L"zero page address with y offset",
-		L"absolute address with y offset",
-		L"implied",
-		L"immediate",
-		L"relative address",
-		L"indirect address",
-		L"indirect address with x offset",
-		L"indirect address with y offset",
+	const static std::wstring_view names[] = {
+		std::wstring_view(L"zero page address"),
+		std::wstring_view(L"absolute address"),
+		std::wstring_view(L"zero page address with x offset"),
+		std::wstring_view(L"absolute address with x offset"),
+		std::wstring_view(L"zero page address with y offset"),
+		std::wstring_view(L"absolute address with y offset"),
+		std::wstring_view(L"implied"),
+		std::wstring_view(L"immediate"),
+		std::wstring_view(L"relative address"),
+		std::wstring_view(L"indirect address"),
+		std::wstring_view(L"indirect address with x offset"),
+		std::wstring_view(L"indirect address with y offset"),
 	};
-	static_assert(sizeof(names) / sizeof(wchar_t *) == static_cast<int>(AddressingModeType::NumAddressingModes), "Number of addressing modes doesn't match number of strings");
+	static_assert(sizeof(names) / sizeof(names[0]) == static_cast<size_t>(AddressingModeType::NumAddressingModes), "Number of addressing modes doesn't match number of strings");
 
 	assert(type < AddressingModeType::NumAddressingModes);
-	return names[static_cast<int>(type)];
+	return names[static_cast<size_t>(type)];
 }
 
-const wchar_t *to_string(InstructionType type)
+const std::wstring_view to_string(InstructionType type)
 {
-	static const wchar_t *names[] = {
-		L"adc",
-		L"and",
-		L"asl",
-		L"bcc",
-		L"bcs",
-		L"beq",
-		L"bit",
-		L"bmi",
-		L"bne",
-		L"bpl",
-		L"brk",
-		L"bvc",
-		L"bvs",
-		L"clc",
-		L"cld",
-		L"cli",
-		L"clv",
-		L"cmp",
-		L"cpx",
-		L"cpy",
-		L"dec",
-		L"dex",
-		L"dey",
-		L"eor",
-		L"inc",
-		L"inx",
-		L"iny",
-		L"jmp",
-		L"jsr",
-		L"lda",
-		L"ldx",
-		L"ldy",
-		L"lsr",
-		L"nop",
-		L"ora",
-		L"pha",
-		L"php",
-		L"pla",
-		L"plp",
-		L"rol",
-		L"ror",
-		L"rti",
-		L"rts",
-		L"sbc",
-		L"sec",
-		L"sed",
-		L"sei",
-		L"sta",
-		L"stx",
-		L"sty",
-		L"tax",
-		L"tay",
-		L"tsx",
-		L"txa",
-		L"txs",
-		L"tya",
+	static const std::wstring_view names[] = {
+		std::wstring_view(L"adc"),
+		std::wstring_view(L"and"),
+		std::wstring_view(L"asl"),
+		std::wstring_view(L"bcc"),
+		std::wstring_view(L"bcs"),
+		std::wstring_view(L"beq"),
+		std::wstring_view(L"bit"),
+		std::wstring_view(L"bmi"),
+		std::wstring_view(L"bne"),
+		std::wstring_view(L"bpl"),
+		std::wstring_view(L"brk"),
+		std::wstring_view(L"bvc"),
+		std::wstring_view(L"bvs"),
+		std::wstring_view(L"clc"),
+		std::wstring_view(L"cld"),
+		std::wstring_view(L"cli"),
+		std::wstring_view(L"clv"),
+		std::wstring_view(L"cmp"),
+		std::wstring_view(L"cpx"),
+		std::wstring_view(L"cpy"),
+		std::wstring_view(L"dec"),
+		std::wstring_view(L"dex"),
+		std::wstring_view(L"dey"),
+		std::wstring_view(L"eor"),
+		std::wstring_view(L"inc"),
+		std::wstring_view(L"inx"),
+		std::wstring_view(L"iny"),
+		std::wstring_view(L"jmp"),
+		std::wstring_view(L"jsr"),
+		std::wstring_view(L"lda"),
+		std::wstring_view(L"ldx"),
+		std::wstring_view(L"ldy"),
+		std::wstring_view(L"lsr"),
+		std::wstring_view(L"nop"),
+		std::wstring_view(L"ora"),
+		std::wstring_view(L"pha"),
+		std::wstring_view(L"php"),
+		std::wstring_view(L"pla"),
+		std::wstring_view(L"plp"),
+		std::wstring_view(L"rol"),
+		std::wstring_view(L"ror"),
+		std::wstring_view(L"rti"),
+		std::wstring_view(L"rts"),
+		std::wstring_view(L"sbc"),
+		std::wstring_view(L"sec"),
+		std::wstring_view(L"sed"),
+		std::wstring_view(L"sei"),
+		std::wstring_view(L"sta"),
+		std::wstring_view(L"stx"),
+		std::wstring_view(L"sty"),
+		std::wstring_view(L"tax"),
+		std::wstring_view(L"tay"),
+		std::wstring_view(L"tsx"),
+		std::wstring_view(L"txa"),
+		std::wstring_view(L"txs"),
+		std::wstring_view(L"tya"),
 	};
-	static_assert(sizeof(names) / sizeof(wchar_t *) == static_cast<int>(InstructionType::NumTypes), "Number of instructions doesn't match number of strings");
+	static_assert(sizeof(names) / sizeof(names[0]) == static_cast<size_t>(InstructionType::NumTypes), "Number of instructions doesn't match number of strings");
 
 	assert(type < InstructionType::NumTypes);
-	return names[static_cast<int>(type)];
+	return names[static_cast<size_t>(type)];
 }
 
 }

          
M jasm/assembling/instructions_6502.h +2 -2
@@ 151,9 151,9 @@ InstructionType inverse_branch(Instructi
 bool is_ending_instruction(InstructionType type);
 
 /// Convert an addressing mode to a string for printing.
-const wchar_t *to_string(AddressingModeType type);
+const std::wstring_view to_string(AddressingModeType type);
 
-const wchar_t *to_string(InstructionType type);
+const std::wstring_view to_string(InstructionType type);
 
 /// @}
 

          
M jasm/assembling/instructions_z80.cpp +120 -120
@@ 9,141 9,141 @@ 
 namespace jasm
 {
 
-const wchar_t *to_string(InstructionType type)
+const std::wstring_view to_string(InstructionType type)
 {
-	static const wchar_t *names[] = {
-		L"adc",
-		L"add",
-		L"and",
-		L"bit",
-		L"call",
-		L"ccf",
-		L"cp",
-		L"cpd",
-		L"cpdr",
-		L"cpi",
-		L"cpir",
-		L"cpl",
-		L"daa",
-		L"dec",
-		L"di",
-		L"djnz",
-		L"ei",
-		L"ex",
-		L"exx",
-		L"halt",
-		L"im",
-		L"in",
-		L"inc",
-		L"ind",
-		L"indr",
-		L"ini",
-		L"inir",
-		L"jp",
-		L"jr",
-		L"ld",
-		L"ldd",
-		L"lddr",
-		L"ldi",
-		L"ldir",
-		L"neg",
-		L"nop",
-		L"or",
-		L"otdr",
-		L"otir",
-		L"out",
-		L"outd",
-		L"outi",
-		L"pop",
-		L"push",
-		L"res",
-		L"ret",
-		L"reti",
-		L"retn",
-		L"rl",
-		L"rla",
-		L"rlc",
-		L"rlca",
-		L"rld",
-		L"rr",
-		L"rra",
-		L"rrc",
-		L"rrca",
-		L"rrd",
-		L"rst",
-		L"sbc",
-		L"scf",
-		L"set",
-		L"sla",
-		L"sra",
-		L"srl",
-		L"sub",
-		L"xor",
+	static const std::wstring_view names[] = {
+		std::wstring_view(L"adc"),
+		std::wstring_view(L"add"),
+		std::wstring_view(L"and"),
+		std::wstring_view(L"bit"),
+		std::wstring_view(L"call"),
+		std::wstring_view(L"ccf"),
+		std::wstring_view(L"cp"),
+		std::wstring_view(L"cpd"),
+		std::wstring_view(L"cpdr"),
+		std::wstring_view(L"cpi"),
+		std::wstring_view(L"cpir"),
+		std::wstring_view(L"cpl"),
+		std::wstring_view(L"daa"),
+		std::wstring_view(L"dec"),
+		std::wstring_view(L"di"),
+		std::wstring_view(L"djnz"),
+		std::wstring_view(L"ei"),
+		std::wstring_view(L"ex"),
+		std::wstring_view(L"exx"),
+		std::wstring_view(L"halt"),
+		std::wstring_view(L"im"),
+		std::wstring_view(L"in"),
+		std::wstring_view(L"inc"),
+		std::wstring_view(L"ind"),
+		std::wstring_view(L"indr"),
+		std::wstring_view(L"ini"),
+		std::wstring_view(L"inir"),
+		std::wstring_view(L"jp"),
+		std::wstring_view(L"jr"),
+		std::wstring_view(L"ld"),
+		std::wstring_view(L"ldd"),
+		std::wstring_view(L"lddr"),
+		std::wstring_view(L"ldi"),
+		std::wstring_view(L"ldir"),
+		std::wstring_view(L"neg"),
+		std::wstring_view(L"nop"),
+		std::wstring_view(L"or"),
+		std::wstring_view(L"otdr"),
+		std::wstring_view(L"otir"),
+		std::wstring_view(L"out"),
+		std::wstring_view(L"outd"),
+		std::wstring_view(L"outi"),
+		std::wstring_view(L"pop"),
+		std::wstring_view(L"push"),
+		std::wstring_view(L"res"),
+		std::wstring_view(L"ret"),
+		std::wstring_view(L"reti"),
+		std::wstring_view(L"retn"),
+		std::wstring_view(L"rl"),
+		std::wstring_view(L"rla"),
+		std::wstring_view(L"rlc"),
+		std::wstring_view(L"rlca"),
+		std::wstring_view(L"rld"),
+		std::wstring_view(L"rr"),
+		std::wstring_view(L"rra"),
+		std::wstring_view(L"rrc"),
+		std::wstring_view(L"rrca"),
+		std::wstring_view(L"rrd"),
+		std::wstring_view(L"rst"),
+		std::wstring_view(L"sbc"),
+		std::wstring_view(L"scf"),
+		std::wstring_view(L"set"),
+		std::wstring_view(L"sla"),
+		std::wstring_view(L"sra"),
+		std::wstring_view(L"srl"),
+		std::wstring_view(L"sub"),
+		std::wstring_view(L"xor"),
 	};
-	static_assert(sizeof(names) / sizeof(wchar_t *) == static_cast<int>(InstructionType::NumTypes), "Number of instructions doesn't match number of strings");
+	static_assert(sizeof(names) / sizeof(names[0]) == static_cast<size_t>(InstructionType::NumTypes), "Number of instructions doesn't match number of strings");
 
 	assert(type < InstructionType::NumTypes);
-	return names[static_cast<int>(type)];
+	return names[static_cast<size_t>(type)];
 }
 
-const wchar_t *to_string(InstructionArgumentType type)
+const std::wstring_view to_string(InstructionArgumentType type)
 {
-	static const wchar_t *names[] = {
-		L"<none>",
-		L"<number>",
-		L"(<address>)",
-		L"(c)",
-		L"(bc)",
-		L"(de)",
-		L"(hl)",
-		L"(ix)",
-		L"(iy)",
-		L"(sp)",
-		L"(ix+<offset>)",
-		L"(iy+<offset>)",
-		L"a",
-		L"b",
-		L"c",
-		L"d",
-		L"e",
-		L"h",
-		L"l",
-		L"i",
-		L"r",
-		L"af",
-		L"bc",
-		L"de",
-		L"hl",
-		L"ix",
-		L"iy",
-		L"sp",
-		L"af'",
-		L"c",
-		L"m",
-		L"nc",
-		L"nz",
-		L"p",
-		L"pe",
-		L"po",
-		L"z",
+	static const std::wstring_view names[] = {
+		std::wstring_view(L"<none>"),
+		std::wstring_view(L"<number>"),
+		std::wstring_view(L"(<address>)"),
+		std::wstring_view(L"(c)"),
+		std::wstring_view(L"(bc)"),
+		std::wstring_view(L"(de)"),
+		std::wstring_view(L"(hl)"),
+		std::wstring_view(L"(ix)"),
+		std::wstring_view(L"(iy)"),
+		std::wstring_view(L"(sp)"),
+		std::wstring_view(L"(ix+<offset>)"),
+		std::wstring_view(L"(iy+<offset>)"),
+		std::wstring_view(L"a"),
+		std::wstring_view(L"b"),
+		std::wstring_view(L"c"),
+		std::wstring_view(L"d"),
+		std::wstring_view(L"e"),
+		std::wstring_view(L"h"),
+		std::wstring_view(L"l"),
+		std::wstring_view(L"i"),
+		std::wstring_view(L"r"),
+		std::wstring_view(L"af"),
+		std::wstring_view(L"bc"),
+		std::wstring_view(L"de"),
+		std::wstring_view(L"hl"),
+		std::wstring_view(L"ix"),
+		std::wstring_view(L"iy"),
+		std::wstring_view(L"sp"),
+		std::wstring_view(L"af'"),
+		std::wstring_view(L"c"),
+		std::wstring_view(L"m"),
+		std::wstring_view(L"nc"),
+		std::wstring_view(L"nz"),
+		std::wstring_view(L"p"),
+		std::wstring_view(L"pe"),
+		std::wstring_view(L"po"),
+		std::wstring_view(L"z"),
 
 		// The modes up to here is used when parsing and the actual meaning of numbers is unknown.
 		// Past this point are the actual specific types of numbers listed since the instructions
 		// have this information. It makes it possible to print out the required addressing mode.
 
-		L"<byte value>",
-		L"<word value>",
-		L"(<byte value>)",
-		L"(<word value>)",
-		L"<relative address>",
-		L"<bit>",
-		L"<zero page address>",
-		L"<interrupt number>",
+		std::wstring_view(L"<byte value>"),
+		std::wstring_view(L"<word value>"),
+		std::wstring_view(L"(<byte value>)"),
+		std::wstring_view(L"(<word value>)"),
+		std::wstring_view(L"<relative address>"),
+		std::wstring_view(L"<bit>"),
+		std::wstring_view(L"<zero page address>"),
+		std::wstring_view(L"<interrupt number>"),
 	};
-	static_assert(sizeof(names) / sizeof(wchar_t *) == static_cast<int>(InstructionArgumentType::NumTypes), "Number of instruction arguments doesn't match number of strings");
+	static_assert(sizeof(names) / sizeof(names[0]) == static_cast<size_t>(InstructionArgumentType::NumTypes), "Number of instruction arguments doesn't match number of strings");
 
 	assert(type < InstructionArgumentType::NumTypes);
-	return names[static_cast<int>(type)];
+	return names[static_cast<size_t>(type)];
 }
 
 namespace

          
M jasm/assembling/instructions_z80.h +2 -2
@@ 240,8 240,8 @@ const core::StaticArray<AddressingMode> 
 /// where the addressing mode was found.
 const InstructionOpCode &opcode(InstructionType instruction, uint8_t addressing_mode_index);
 
-const wchar_t *to_string(InstructionType type);
-const wchar_t *to_string(InstructionArgumentType type);
+const std::wstring_view to_string(InstructionType type);
+const std::wstring_view to_string(InstructionArgumentType type);
 
 /// @}
 

          
M jasm/assembling/methods.cpp +16 -16
@@ 34,26 34,26 @@ const MethodDesc &method_info(MethodType
 	return desc[static_cast<int>(type)];
 }
 
-const wchar_t *to_string(MethodType type)
+const std::wstring_view to_string(MethodType type)
 {
-	static const wchar_t *names[] = {
-		L"substring",
-		L"push",
-		L"pop",
-		L"insert",
-		L"erase",
-		L"keep",
-		L"clear",
-		L"get",
-		L"set",
-		L"erase",
-		L"clear",
-		L"has",
+	static const std::wstring_view names[] = {
+		std::wstring_view(L"substring"),
+		std::wstring_view(L"push"),
+		std::wstring_view(L"pop"),
+		std::wstring_view(L"insert"),
+		std::wstring_view(L"erase"),
+		std::wstring_view(L"keep"),
+		std::wstring_view(L"clear"),
+		std::wstring_view(L"get"),
+		std::wstring_view(L"set"),
+		std::wstring_view(L"erase"),
+		std::wstring_view(L"clear"),
+		std::wstring_view(L"has"),
 	};
-	static_assert(sizeof(names) / sizeof(names[0]) == static_cast<int>(MethodType::NumTypes), "Number of methods doesn't match number of strings");
+	static_assert(sizeof(names) / sizeof(names[0]) == static_cast<size_t>(MethodType::NumTypes), "Number of methods doesn't match number of strings");
 
 	assert(type < MethodType::NumTypes);
-	return names[static_cast<int>(type)];
+	return names[static_cast<size_t>(type)];
 }
 
 } // namespace jasm

          
M jasm/assembling/methods.h +1 -1
@@ 77,7 77,7 @@ struct MethodDesc {
 /// Returns information about a function.
 const MethodDesc &method_info(MethodType type);
 
-const wchar_t *to_string(MethodType type);
+const std::wstring_view to_string(MethodType type);
 
 /// @}
 

          
M jasm/assembling/value.cpp +35 -29
@@ 2,73 2,79 @@ 
 
 #include <assembling/value.h>
 #include <core/exceptions/exception.h>
+#include <cstring>
 #include <strings/string_repository.h>
 
 namespace jasm {
 
 using namespace core;
 
-const wchar_t *to_string(ValueType type)
+const std::wstring_view to_string(ValueType type)
 {
 	assert(type < ValueType::NumTypes);
 	switch(type)
 	{
 	case ValueType::None:
-		return L"void";
+		return std::wstring_view(L"void");
 	case ValueType::Unknown:
-		return L"unknown";
+		return std::wstring_view(L"unknown");
 	case ValueType::BoolValue:
-		return L"boolean";
+		return std::wstring_view(L"boolean");
 	case ValueType::IntegerValue:
-		return L"integer";
+		return std::wstring_view(L"integer");
 	case ValueType::FloatValue:
-		return L"float";
+		return std::wstring_view(L"float");
 	case ValueType::StringValue:
-		return L"string";
+		return std::wstring_view(L"string");
 	case ValueType::RangeValue:
-		return L"range";
+		return std::wstring_view(L"range");
 	case ValueType::ByteOffset:
-		return L"byte offset";
+		return std::wstring_view(L"byte offset");
 	case ValueType::WordOffset:
-		return L"word offset";
+		return std::wstring_view(L"word offset");
 	case ValueType::LongOffset:
-		return L"long offset";
+		return std::wstring_view(L"long offset");
 	case ValueType::StructOffset:
-		return L"struct offset";
+		return std::wstring_view(L"struct offset");
 	case ValueType::ArrayOffset:
-		return L"array offset";
+		return std::wstring_view(L"array offset");
 	case ValueType::ByteTypename:
-		return L"byte typename";
+		return std::wstring_view(L"byte typename");
 	case ValueType::WordTypename:
-		return L"word typename";
+		return std::wstring_view(L"word typename");
 	case ValueType::LongTypename:
-		return L"long typename";
+		return std::wstring_view(L"long typename");
 	case ValueType::StructTypename:
-		return L"struct typename";
+		return std::wstring_view(L"struct typename");
 	case ValueType::FunctionReference:
-		return L"function";
+		return std::wstring_view(L"function");
 	case ValueType::MacroReference:
-		return L"macro";
+		return std::wstring_view(L"macro");
 	case ValueType::ValueReference:
-		return L"value reference";
+		return std::wstring_view(L"value reference");
 	case ValueType::StringReference:
-		return L"string";
+		return std::wstring_view(L"string");
 	case ValueType::EnumReference:
-		return L"enum";
+		return std::wstring_view(L"enum");
 	case ValueType::ObjectReference:
-		return L"object";
+		return std::wstring_view(L"object");
 	case ValueType::ListReference:
-		return L"list";
+		return std::wstring_view(L"list");
 	case ValueType::ListElementReference:
-		return L"list element reference";
+		return std::wstring_view(L"list element reference");
 	case ValueType::MapReference:
-		return L"map";
+		return std::wstring_view(L"map");
 	case ValueType::MethodClosure:
-		return L"method closure";
+		return std::wstring_view(L"method closure");
 	case ValueType::NumTypes:
 		return nullptr;
 	}
-	UNREACHABLE_CODE(return L"");
+	UNREACHABLE_CODE(return std::wstring_view());
+}
+
+StaticValue::StaticValue() {
+	// must clear to get deterministic hash for padding
+	memset(this, 0, sizeof(StaticValue));
 }
 
 std::wstring make_printable(const std::wstring &str)

          
@@ 111,7 117,7 @@ std::wstring to_string(const StringRepos
 		return std::to_wstring(value.float_value);
 
 	case ValueType::StringValue:
-		return make_printable(std::wstring(string_repo.get_string(value.string_hash)));
+		return make_printable(std::wstring(string_repo.get(value.string_hash)));
 
 	case ValueType::ByteOffset:
 	case ValueType::WordOffset:

          
M jasm/assembling/value.h +6 -7
@@ 69,16 69,13 @@ enum class ValueType : uint8_t
 };
 
 
-const wchar_t *to_string(ValueType type);
+const std::wstring_view to_string(ValueType type);
 
 /// This is the POD part of a value.
 struct StaticValue
 {
-	StaticValue() {
-		// must clear to get deterministic hash for padding
-		memset(this, 0, sizeof(StaticValue));
-	}
-
+	StaticValue();
+	
 	friend bool operator==(const StaticValue &a, const StaticValue &b)
 	{
 		return

          
@@ 410,11 407,13 @@ class StringDynamicObject : public Dynam
 {
 public:
 	StringDynamicObject() : DynamicObject() {}
+	StringDynamicObject(const std::wstring_view &str) : DynamicObject(), value(str) {}
 	StringDynamicObject(const std::wstring &str) : DynamicObject(), value(str) {}
-	StringDynamicObject(std::wstring &&str) : DynamicObject(), value(str) {}
+	StringDynamicObject(std::wstring &&str) : DynamicObject(), value(std::move(str)) {}
 
 	virtual bool equal_to(const DynamicObject &other) const override;
 	virtual uint64_t hash(uint64_t seed) const override;
+
 	std::wstring value;
 };
 

          
M jasm/docs/footer.html +2 -1
@@ 1,1 1,2 @@ 
-</body>
+	</body>
+</html>

          
M jasm/docs/header.html +4 -5
@@ 1,11 1,10 @@ 
 <!DOCTYPE html>
-<html>
+<html lang="en">
 	<head>
-		<title>jAsm</title>
+		<title>jAsm Documentation</title>
 		<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
 		<meta name="description" content="This is the documentation for the jAsm assembler.">
 		<link rel="shortcut icon" href="images/favicon.ico">
-		<link href="jasm.css" rel="stylesheet"></link>
+		<link href="jasm.css" rel="stylesheet">
 	</head>
-</html>
-<body>
+	<body>

          
M jasm/docs/jasm.md +141 -190
@@ 107,13 107,11 @@ This documentation covers the language a
    * [Macros](#macros)
    * [Alignment](#alignment)
 
-<a name="processor-support">
+<div id="processor-support"></div>
 # Processor Support
-</a>
-
-<a name="m6502">
+
+<div id="m6502"></div>
 ## 6502
-</a>
 
 jAsm supports all regular instructions of the 6502. Instructions are written in lower case.
 

          
@@ 121,9 119,8 @@ jAsm supports all regular instructions o
 	lda #0
 	sta $d020
 
-<a name="z80">
+<div id="z80"></div>
 ## Z80
-</a>
 
 jAsm supports all regular instructions of the Z80. Instructions are written in lower case.
 

          
@@ 136,9 133,8 @@ Due to the large amount of source code w
 	[text]
 	python3 jasm-z80/convert_z80_keyword_case.py <my_source_directory>
 
-<a name="starter-guide">
+<div id="starter-guide"></div>
 # Starter Guide
-</a>
 
 - - -
 

          
@@ 154,12 150,12 @@ We'll start by creating a small program 
 Save this to a file named main.jasm. Use utf-8 format, because this is what jAsm expects. 7-bit ASCII is also ok since that is compatible with the utf-8 format. Now we'll assemble it into a binary. Open a command line window and change the current directory to where the main.jasm file is. Type this on the command line.
 
 	[text]
-	jasm-6502 main.jasm main.prg
+	jasm-6502 -hla main.jasm main.prg
 
 Now you have a program that changes the border color on a Commodore 64. Load it into an emulator or onto a real machine.
 
 	[text]
-	LOAD"TEST.PRG",8,1
+	LOAD"MAIN.PRG",8,1
 
 Now start it.
 

          
@@ 168,9 164,8 @@ Now start it.
 
 The border color changes.
 
-<a name="starter-guide-basic-start">
+<div id="starter-guide-basic-start"></div>
 ## Basic Start
-</a>
 
 If you want to start it on a Commodore 64 with a BASIC line, you need to add the necessary data to produce a SYS line at the BASIC start. This is specific to the Commodore BASIC v2. This example shows how to do that in jAsm.
 

          
@@ 196,9 191,8 @@ Stuff written after `[6502]|//` are comm
 
 `[6502]|string(.start)` means "call the built in function `[6502]|string` with the argument `[6502]|.start`". The function will return a string representation of `[6502]|.start`.
 
-<a name="starter-guide-basic-macro">
+<div id="starter-guide-basic-macro"></div>
 ## Basic Macro
-</a>
 
 This BASIC line thing will be used a lot in programs since almost all programs loaded from disk will need it. Let's break out this code into a handy macro that we can reuse. The macro will need two arguments, one is the line number and one is the address to start the program from.
 

          
@@ 225,9 219,8 @@ This BASIC line thing will be used a lot
 
 The start of the main section invokes the macro and this inserts the code in the macro at the place of invocation.
 
-<a name="starter-guide-using-files">
+<div id="starter-guide-using-files"></div>
 ## Using Files
-</a>
 
 The main section of our example looks a lot cleaner now. We can now move the macro to its own file. We can build a small library of handy macros to help us be productive and avoid solving the same problem several times.
 

          
@@ 245,9 238,8 @@ Move the macro code into a file called m
 		rts
 	}
 
-<a name="starter-guide-defining-constants">
+<div id="starter-guide-defining-constants"></div>
 ## Defining Constants
-</a>
 
 The border color changing address isn't exactly self explanatory. The BASIC start address is also a naked constant that isn't exactly self explained. Let's make this a bit better.
 

          
@@ 281,9 273,8 @@ I use uppercase characters for fixed add
 		rts
 	}
 
-<a name="starter-guide-conditional-assembly">
+<div id="starter-guide-conditional-assembly"></div>
 ## Conditional Assembly
-</a>
 
 Now, what if we wanted to port this to VIC20? We would only need to create a vic20.jasm file with different `[6502]|BASIC_START` and `[6502]|BORDER_COLOR` addresses and then include that instead of the c64.jasm file. We can also support both at the same time. Let's put this in the vic20.jasm file.
 

          
@@ 310,9 301,8 @@ Now, what we need is a way to include ei
 		rts
 	}
 
-<a name="starter-guide-command-line-constants">
+<div id="starter-guide-command-line-constants"></div>
 ## Command Line Constants
-</a>
 
 The `[6502]|if` statement wants a boolean expression within the parentheses and if true the first block of code is used, otherwise the second block is used. We can feed constants from the command line to solve this. The command line option is `[text]|-d` and it needs to be followed by an assignment. In this case we want to assign `[6502]|C64_BUILD` to `[6502]|true` or `[6502]|false`.
 

          
@@ 320,9 310,8 @@ The `[6502]|if` statement wants a boolea
 	jasm-6502 -d C64_BUILD=true main.jasm main.prg
 	jasm-6502 -d C64_BUILD=false main.jasm main.prg
 
-<a name="starter-guide-definining-data">
+<div id="starter-guide-definining-data"></div>
 ## Defining Data
-</a>
 
 Let's try a hello world example. We'll drop the VIC20 support to make the code shorter. We will define the string "hello world!" and print it, character by character. We have already seen how to define a string in memory in the BASIC line. Printing is done with a jump to $ffd2, which prints a single character. Let's add the following naked constant to the c64.jasm file.
 

          
@@ 354,9 343,8 @@ Now we'll add the loop to print the text
 
 The define now has a name before the equal sign. This becomes a special kind of label. It can be used as a normal label but it also contains information about the defined data. `[6502]|sizeof` is a function that returns the size in bytes of such a labeled object or array.
 
-<a name="starter-guide-coding-for-readabiity">
+<div id="starter-guide-coding-for-readabiity"></div>
 ## Coding For Readability
-</a>
 
 This works but is hard to read. It isn't obvious where the loop starts and ends unless we read the instructions. Let's improve it using indentation.
 

          
@@ 381,9 369,8 @@ This works but is hard to read. It isn't
 		define byte[] hello_world_text = { "HELLO WORLD!" }
 	}
 
-<a name="starter-guide-automatic-labels">
+<div id="starter-guide-automatic-labels"></div>
 ## Automatic Labels
-</a>
 
 This is better but can be improved further. jAsm supports an automatic `[6502]|@loop` label at the beginning of a scope defined by curly braces.
 

          
@@ 411,9 398,8 @@ This is better but can be improved furth
 
 It's now much easier to read the loop and we got rid of the explicitly defined label `[6502]|.loop`.
 
-<a name="starter-guide-subroutines">
+<div id="starter-guide-subroutines"></div>
 ## Subroutines
-</a>
 
 If we want to print more text we need to move the loop into a subroutine which can be called with a jsr instruction and some parameters in registers.
 

          
@@ 466,9 452,8 @@ If we want to print more text we need to
 	main.jasm(24,13) : Error 3000 : Operator + is not defined for left hand side unknown type.
 	main.jasm(25,7) : Error 3004 : Reference to undefined symbol .size
 
-<a name="starter-guide-declaring-symbols">
+<div id="starter-guide-declaring-symbols"></div>
 ## Declaring Symbols
-</a>
 
 There is something wrong with `[6502]|.addr` and `[6502]|.size`. The reason is that local constants are not accessible outside the scope they are defined in. Local constants are always accessible inside the scope they are defined in, even in inner scopes. The scope is defined by the closest enclosing curly braces. So `[6502]|.addr` and `[6502]|.size` is accessible inside the loop but not outside.
 

          
@@ 550,9 535,8 @@ This subroutine can be reused so let's m
 		include "screen_io.jasm"
 	}
 
-<a name="starter-guide-bss-sections">
+<div id="starter-guide-bss-sections"></div>
 ## Bss Sections
-</a>
 
 Self modifying code is handy and can improve efficiency but it doesn't work if the code is in a cartridge ROM, because it can't be modified. Let's try modifying the code to use the zero page instead. To do this we need to reserve some space for variables in the zero page area. This is done with a bss section. BSS stands for "Block Started by Symbol" and means a static memory block that is part of the program, but without its content stored in the executable file. The bss section doesn't generate any code or data, it just reserves uninitialized space. I reserved the last 5 bytes in the zero page area from $fb to, but not including, $100.
 

          
@@ 658,9 642,8 @@ Now the zero is added. Let's update the 
 
 Now that looks better. The `[6502]|@continue` is another automatic label that is defined by the closest surrounding closing curly braces.
 
-<a name="starter-guide-section-parts">
+<div id="starter-guide-section-parts"></div>
 ## Section Parts
-</a>
 
 One thing that isn't really great is that it isn't obvious what addr is used for. It would be nice if it was connected to the print subroutine somehow. We can make that connection by creating partial sections in the screen\_io.jasm file that adds to the sections in the main file. We do that by moving the reserve into the screen\_io.jasm file. We also move the include outside the main section, because we can't define a partial section within a section.
 

          
@@ 720,9 703,8 @@ The screen\_io.jasm file now needs to de
 
 We now have a kind of module with the print subroutine and its zero page variable. It can sit beside other potential modules in a larger program without overlapping. We don't have to specify a single address and it will be optimally packed together.
 
-<a name="starter-guide-namespaces">
+<div id="starter-guide-namespaces"></div>
 ## Namespaces
-</a>
 
 What if some other module also wants to have a temporary address called `[6502]|addr`? That could be a problem. One solution for this is to put the print related names in a namespace.
 

          
@@ 792,9 774,8 @@ If `[6502]|print_text` is used a lot in 
 
 	include "screen_io.jasm"
 
-<a name="starter-guide-modules">
+<div id="starter-guide-modules"></div>
 ## Modules
-</a>
 
 A namespace expose everything to the outside world. Sometimes that's what you want but it could also be nice to control the module's interface. This can be done using /modules/ instead of namespaces. In a module, all global variables are local to the module unless they are marked for export.
 

          
@@ 832,9 813,8 @@ In our example, `[6502]|addr` doesn't ne
 
 Accessing the print\_text subroutine in the module is done exactly the same way it was accessed in the namespace so the rest of the program can be left unchanged.
 
-<a name="starter-guide-debugging-in-vice">
+<div id="starter-guide-debugging-in-vice"></div>
 ## Debugging in VICE
-</a>
 
 jAsm can assist debugging in the VICE emulator by exporting the names of addresses for use in the emulator. Add [--dump-vice-symbols](#symbol-dumps) and a filename to the command line arguments to export this information.
 

          
@@ 905,9 885,8 @@ Change the c64.jasm file to this, assemb
 
 Problem solved!
 
-<a name="starter-guide-vice-breakpoints">
+<div id="starter-guide-vice-breakpoints"></div>
 ## VICE Breakpoints
-</a>
 
 To aid debugging you can set breakpoints in your program. This makes it easy to stop the program in a specific subroutine and single step through it. You do this by creating a label with a name that begins with `[6502]|breakpoint`. Let's try this. Add a label somewhere in the print\_text subroutine, like this.
 

          
@@ 944,15 923,13 @@ There are two more types of breakpoints.
 
 Now you know the basics of jAsm and should be able to start experimenting yourself. The language has more to offer and the complete syntax is described in the reference section. Good luck!
 
-<a name="compiling-jasm">
+<div id="compiling-jasm"></div>
 # Compiling jAsm
-</a>
 
 - - -
 
-<a name="fetching-source-code">
+<div id="fetching-source-code"></div>
 ## Fetching Source Code
-</a>
 
 You need to fetch the source code from BitBucket to get started. If you have a command line Mercurial client you can clone the repository like this.
 

          
@@ 961,9 938,8 @@ You need to fetch the source code from B
 
 jAsm compiles using CMake and Clang or using Code::Blocks or Visual Studio.
 
-<a name="compiling-using-cmake">
+<div id="compiling-using-cmake"></div>
 ## Compiling Using CMake
-</a>
 
 To build with CMake you need CMake 3.5, Clang, Mercurial and python3 installed. On Debian, Ubuntu or Mint systems you can use apt-get to fetch the dependencies like this.
 

          
@@ 981,15 957,36 @@ Clone the repository into a directory ca
 	cmake -DCMAKE_BUILD_TYPE=Release ..
 	sudo make install
 
-<a name="compiling-using-vs">
+If you want to cross compile binaries for Windows you need to install MingW.
+
+	[text]
+	sudo apt-get install mingw-w64
+
+Cross compile like this.
+
+	[text]
+	hg clone ssh://hg@bitbucket.org/bjonte/jasm
+	cd jasm
+	mkdir build
+	cd build
+	cmake -DCMAKE_TOOLCHAIN_FILE=../win64_cross_compile_toolchain.txt -DCMAKE_BUILD_TYPE=Release ..
+	make
+
+You will find the binaries in build/jasm-6502 and build/jasm-z80. You will also need the MingW dynamic link libraries found here in Linux Mint.
+
+	[text]
+	/usr/lib/gcc/x86_64-w64-mingw32/7.3-win32/libgcc_s_seh-1.dll
+	/usr/lib/gcc/x86_64-w64-mingw32/7.3-win32/libstdc++-6.dll
+
+<div id="compiling-using-vs"></div>
 ## Compiling Using Visual Studio
-</a>
+
+<i>The Visual Studio solution is no longer actively maintained and may not work. Cross compiling on Linux is the supported method to build Windows binaries.</i>
 
 Download Visual Studio 2015 from www.microsoft.com and install it. Double click on the `[text]|jasm.sln` file to open the project. Select the `[text]|Release` configuration and build the solution. You will get a `[text]|jasm.exe` binary in `[text]|x64\Release`.
 
-<a name="starting-jasm">
+<div id="starting-jasm"></div>
 # Starting jAsm
-</a>
 
 - - -
 

          
@@ 1005,9 1002,8 @@ If you are assembling for Z80, use that 
 
 There are some flags to tweak how the assembler behaves.
 
-<a name="bank-mode">
+<div id="bank-mode"></div>
 ## Bank Mode
-</a>
 
 When working with several memory banks it is handy to place them after each other in memory. That way it is possible to check which bank code or data belongs to just looking at the address. For example, cartridge bank 0 could be located at $08000-$0a000 and bank 1 at $18000-$1a000. However, jAsm will generate an error when trying to reference bank 1 in data definitions or instructions because the addresses exceeds 16 bits. This can be overridden with the `[text]|--bank-mode` flag, which automatically truncates long addresses.
 

          
@@ 1016,9 1012,10 @@ When working with several memory banks i
 
 _A shortcut alternative is `[text]|-bm`._
 
-<a name="predefined-constants">
+This also have implications on the high byte unary operator (`[6502]|>`). Without bank mode '`[6502]|>addr`' will mean the same as '`[6502]|addr>>8`' but with the bank mode enabled this will be '`[6502]|(addr>>8)&$ff`' to make sure the result is an eight bit value suitable for instructions taking a byte argument.
+
+<div id="predefined-constants"></div>
 ## Predefined Constants
-</a>
 
 You can instruct the assembler to create some initial constants that can be accessed in the source code with the `[text]|--define` flag.
 

          
@@ 1030,9 1027,8 @@ You can feed it with integers, booleans 
 
 _A shortcut alternative is `[text]|-d`._
 
-<a name="symbol-dumps">
+<div id="symbol-dumps"></div>
 ## Symbol Dumps
-</a>
 
 The constants and variables in the assembled program can be written to text files in these formats.
 

          
@@ 1066,9 1062,8 @@ Dump No$GBA symbols like this.
 
 _A shortcut alternative is `[text]|-dg`._
 
-<a name="binary-header">
+<div id="binary-header"></div>
 ## Binary Header
-</a>
 
 By default, jAsm outputs only the binary data without any header. To generate a program file for Commodore 64 that can be loaded from BASIC, a two byte header must be added containing the load address in little endian format. You can add this header using `[text]|--header-little-endian-address`.
 

          
@@ 1077,9 1072,8 @@ By default, jAsm outputs only the binary
 
 _A shortcut alternative is `[text]|-hla`._
 
-<a name="include-paths">
+<div id="include-paths"></div>
 ## Include Paths
-</a>
 
 You can add include paths using the `[text]|--include-dir` flag. jAsm will look in these for included files.
 

          
@@ 1088,9 1082,8 @@ You can add include paths using the `[te
 
 _A shortcut alternative is `[text]|-i`._
 
-<a name="max-errors">
+<div id="max-errors"></div>
 ## Max Errors
-</a>
 
 With the `[text]|--max-errors` flag, you can specify the number of errors that will be printed before jAsm stops assembling.
 

          
@@ 1099,9 1092,8 @@ With the `[text]|--max-errors` flag, you
 
 _A shortcut alternative is `[text]|-me`._
 
-<a name="output-files-and-sections">
+<div id="output-files-and-sections"></div>
 ## Output Files and Sections
-</a>
 
 The default output mode will merge all code sections into one big binary and pad the inbetween space with zeros. With the flag `[text]|--output-multiple-files`, this can be changed to store one file per section instead.
 

          
@@ 1110,9 1102,8 @@ The default output mode will merge all c
 
 _A shortcut alternative is `[text]|-om`._
 
-<a name="verboseness">
+<div id="verboseness"></div>
 ## Verboseness
-</a>
 
 jAsm supports several levels of output during assembly. This is controlled by the `[text]|-v0`, `[text]|-v1`, `[text]|-v2` and `[text]|-v3` flags.
 

          
@@ 1127,29 1118,25 @@ jAsm supports several levels of output d
 	<tr><td><code>[text]|-v3</code></td><td>Show errors, warnings, general information and debugging information</td></tr>
 </table>
 
-<a name="return-codes">
+<div id="return-codes"></div>
 ## Return Codes
-</a>
 
 jAsm returns with return code 0 for success and non-zero if an error occurred.
 
-<a name="language-reference">
+<div id="language-reference"></div>
 # Language Reference
-</a>
 
 - - -
 
 This section documents the entire syntax. Have a look at the starter guide first to get a grasp of the basics before digging into this.
 
-<a name="input-format">
+<div id="input-format"></div>
 ## Input Format
-</a>
 
 jAsm uses Unicode utf-8 encoded text files only. If you provide something that can't be interpreted as utf-8, an error will be returned.
 
-<a name="comments">
+<div id="comments"></div>
 ## Comments
-</a>
 
 jAsm supports C style single line comments. They span the rest of the line.
 

          
@@ 1171,9 1158,8 @@ A notable difference from C is that jAsm
 	  /* also */
     a comment **/
 
-<a name="assembler-instruction-syntax">
+<div id="assembler-instruction-syntax"></div>
 ## Assembler Instruction Syntax
-</a>
 
 This documentation doesn't cover the actual instructions, their meaning and so on. You will have to find that elsewhere.
 

          
@@ 1201,9 1187,8 @@ Instructions end with a new line or a se
 	[6502]
 	inx; inx; inx;
 
-<a name="constants">
+<div id="constants"></div>
 ## Constants
-</a>
 
 Named constants can be defined in the source code to replace naked constants. This is encouraged as much as possible since it makes the source code much more readable. Two types exist. 1) Labels set to the current program counter and 2) constant declarations. Labels are names followed by a colon.
 

          
@@ 1245,9 1230,8 @@ Address constants can be exported to the
 
 Constants and labels can be defined and used in any order, unlike C where everything needs to be declared before use.
 
-<a name="variables">
+<div id="variables"></div>
 ## Variables
-</a>
 
 A constant cannot change its value throughout the source.
 

          
@@ 1267,9 1251,8 @@ Since the value is dependent on the pars
 	lda #A // Error 3004 : Reference to undefined symbol A
 	var A = 3
 
-<a name="local-vs-global">
+<div id="local-vs-global"></div>
 ## Local vs Global
-</a>
 
 There are local and global symbols (constants, labels and variables). Local symbols always start with a period character.
 

          
@@ 1293,9 1276,8 @@ Symbols cannot be used outside their sco
 	}
 	lda #.A // Error 3004 : Reference to undefined symbol .A
 
-<a name="program-counter">
+<div id="program-counter"></div>
 ## Program Counter
-</a>
 
 The program counter is always represented by an asterisk (*). This can be used to refer to things relative the current address.
 

          
@@ 1303,9 1285,8 @@ The program counter is always represente
 	lda #0
 	inc *-1 // increment the value 0 in the previous instruction
 
-<a name="automatic-scope-variables">
+<div id="automatic-scope-variables"></div>
 ## Automatic Scope Variables
-</a>
 
 Every scope has automatic local variables generated to simplify loop constructs in the code without the need to make up names for the loop labels. At the start of the scope `[6502]|@loop` is created and at the end `[6502]|@continue` is created. This makes it possible to loop and exit the loop without explicitly creating labels.
 

          
@@ 1319,9 1300,8 @@ Every scope has automatic local variable
 	}
 	rts
 
-<a name="dynamically-created-symbol-names">
+<div id="dynamically-created-symbol-names"></div>
 ## Dynamically Created Symbol Names
-</a>
 
 Constant and variable names can be constructed by an expression if the `[6502]|dynamic` keyword is used when defining the symbols.
 

          
@@ 1345,9 1325,8 @@ This can be used together with the `[650
 	define byte[] data1 = { "next", 0 }
 	define byte[] data2 = { "last", 0 }
 
-<a name="namespaces">
+<div id="namespaces"></div>
 ## Namespaces
-</a>
 
 Global symbols have a tendency to collide name-wise with each other in large programs, especially if there are several people working on the same code. jAsm supports namespaces to reduce these problems. For example, `[6502]|setup` is a common name and different systems may provide their own `[6502]|setup`. If the symbols are placed in their own namespaces they can coexist.
 

          
@@ 1418,9 1397,8 @@ You can declare that you will be using a
 
 Note that this doesn't resolve ambiguity so, in some cases, you may still need to specify the absolute namespace for symbols.
 
-<a name="modules">
+<div id="modules"></div>
 ## Modules
-</a>
 
 Namespaces encapsulates global symbols in its own space but expose all of them outside the namespace. Modules exist to solve the problem of exposing too much.
 

          
@@ 1479,9 1457,8 @@ Import several variables like this.
 	{
 	}
 
-<a name="declaring-symbols">
+<div id="declaring-symbols"></div>
 ## Declaring Symbols
-</a>
 
 Local symbols cannot be accessed outside their scope and sometimes they need to be defined in an inner scope. This is common when using self modifying code. In the following example, it isn't possible to access `[6502]|.char` outside the loop.
 

          
@@ 1518,9 1495,8 @@ This problem can be solved by declaring 
 
 This technique can be used with any type of local symbol to move its scope.
 
-<a name="expressions">
+<div id="expressions"></div>
 ## Expressions
-</a>
 
 Expressions in jAsm are similar to expressions in C. They can contain assignments and assignments return the value assigned. This has the side effect that you can do multiple assignments.
 

          
@@ 1531,9 1507,8 @@ Expressions in jAsm are similar to expre
 
 Normal parentheses are always used in expressions, not brackets.
 
-<a name="operators">
+<div id="operators"></div>
 ## Operators
-</a>
 
 jAsm supports a number of operators, similar to C but not exactly the same. The operators are, in the order of precedence:
 

          
@@ 1583,9 1558,8 @@ jAsm supports a number of operators, sim
 	<tr><td><code>[6502]|&gt;&gt;=</code></td><td>right shift, and assign</td><td>integer</td><td><code>[6502]|aa &gt;&gt;= 1</code></td></tr>
 </table>
 
-<a name="statements">
+<div id="statements"></div>
 ## Statements
-</a>
 
 Statements are blocks of code that control the generation of instructions or change the assembler state. Statements can optionally be separated by a semicolon, just like in C. Newline characters only matter in instructions where it is impossible for the assembler to know if some instructions (like rol) have an address following or not. In all other cases newlines are completely ignored. The following is valid in jAsm.
 

          
@@ 1612,9 1586,8 @@ The parser tries to include as much as p
 	var bb = 1 + aa;
 	++aa // ok!
 
-<a name="data-types">
+<div id="data-types"></div>
 ## Data Types
-</a>
 
 jAsm has a couple of built in data types.
 

          
@@ 1625,18 1598,16 @@ jAsm has a couple of built in data types
 * Lists
 * Maps
 
-<a name="boolean-type">
+<div id="boolean-type"></div>
 ### Boolean Type
-</a>
 
 Booleans can only be either `[6502]|true` or `[6502]|false`. Comparison operators return boolean values. They are well suited for conditional assembly.
 
 	[6502]
 	const USE_DEBUG_OUTPUT = true
 
-<a name="integer-type">
+<div id="integer-type"></div>
 ### Integer Type
-</a>
 
 Integer numbers are 32 bit signed numbers in the range [-2147483648, 2147483647].
 

          
@@ 1650,9 1621,8 @@ Integer numbers can be expressed in seve
 	const HEX_NUMBER = $1f // hexadecimal number with base 16
 	const BIN_NUMBER = %11111 // binary number with base 2
 
-<a name="float-type">
+<div id="float-type"></div>
 ### Floating Point Type
-</a>
 
 Floating point numbers are 64 bit signed numbers with decimal points. They can represent large numbers and precision increases closer to zero. The largest number is roughly 10<sup>308</sup>. The smallest number is roughly 10<sup>-308</sup>.
 

          
@@ 1661,9 1631,8 @@ Floating point numbers are 64 bit signed
 	const NUMBER2 = 0.0
 	const NUMBER3 = -1e-50
 
-<a name="string-type">
+<div id="string-type"></div>
 ### String Type
-</a>
 
 Strings are quoted text. The characters are stored as wide characters (32 bits in Linux and 16 bits in Windows), leaving you a large selection of characters to choose from. This is why utf-8 is used as the file format for source code.
 

          
@@ 1719,7 1688,7 @@ The following operators and methods are 
 		<th>Function</th>
 		<th>Argument types</th>
 		<th>Description</th>
-		<th width="35%">Examples</th>
+		<th style="width: 35%;">Examples</th>
 	</tr>
 	<tr>
 		<td><code>[6502]|+</code></td>

          
@@ 1747,16 1716,15 @@ The following operators and methods are 
 	</tr>
 </table>
 
-<a name="string-conversions">
+<div id="string-conversions"></div>
 #### String Conversions
-</a>
-
-To support different platform's character sets there is a [6502]|string() function that is used to convert unicode strings, which is the default string type, to other character sets.
+
+To support different platform's character sets there is a `[6502]|string()` function that is used to convert unicode strings, which is the default string type, to other character sets.
 
 	[6502]
 	const PET_HELLO = string("Hello", "petscii", "lowercase")
 
-The function takes a number of arguments. First the string to convert, then a number of conversion properties which specifies the format, subformat and locale for the conversion, in any order.
+The function takes a number of arguments. First the string to convert, then a number of conversion properties which specifies the format, subformat, locale and flags for the conversion, in any order.
 
 The following format properties are supported:
 

          
@@ 1824,9 1792,23 @@ The following optional locale properties
 	</tr>
 </table>
 
-<a name="list-type">
+The following optional flag properties are supported.
+
+<table>
+	<tr>
+		<th>Flag</th>
+		<th>Supported formats</th>
+		<th>Comment</th>
+	</tr>
+	<tr>
+		<td>high_bit_term</td>
+		<td>All</td>
+		<td>The last character in the string is modified to have bit 7 set. This is sometimes used as a cheap terminator for the string.</td>
+	</tr>
+</table>
+
+<div id="list-type"></div>
 ### List Type
-</a>
 
 The list type can hold a collection of values with different types. A list is created using the `[6502]|list` function. This constructs a list containing the arguments.
 

          
@@ 1838,7 1820,7 @@ The list type can hold a collection of v
 		<th>Function</th>
 		<th>Argument types</th>
 		<th>Description</th>
-		<th width="35%">Examples</th>
+		<th style="width: 35%;">Examples</th>
 	</tr>
 	<tr>
 		<td><code>[6502]|+</code></td>

          
@@ 1908,9 1890,8 @@ The list type can hold a collection of v
 	</tr>
 </table>
 
-<a name="map-type">
+<div id="map-type"></div>
 ### Map Type
-</a>
 
 The map type can hold a collection of values with different types. A map is created using the `[6502]|map` function. This constructs a map containing key and value pairs as arguments.
 

          
@@ 1964,9 1945,8 @@ Values can be of any type, but keys must
 	</tr>
 </table>
 
-<a name="passing-values">
+<div id="passing-values"></div>
 ## Passing Values
-</a>
 
 Values are *always passed by value*, never by reference. Everytime you assign a value to some other variable, a copy is made. This makes it possible to assign constants to variables and variables to constants without problems. Values passed as macro arguments will be copied before executing the macro body as well.
 

          
@@ 1978,9 1958,8 @@ Values are *always passed by value*, nev
 
 There is no ghosting or pointers that can mess up the data unexpectedly.
 
-<a name="type-conversions">
+<div id="type-conversions"></div>
 ## Type Conversions
-</a>
 
 There are a number of functions in the root namespace dedicated to converting between the built-in types.
 

          
@@ 1989,7 1968,7 @@ There are a number of functions in the r
 		<th>Function</th>
 		<th>Accepted input types</th>
 		<th>Description</th>
-		<th width="35%">Examples</th>
+		<th style="width: 35%;">Examples</th>
 	</tr>
 	<tr>
 		<td><code>[6502]|int(value)</code></td>

          
@@ 2023,9 2002,8 @@ There are a number of functions in the r
 	</tr>
 </table>
 
-<a name="memory-storage-types">
+<div id="memory-storage-types"></div>
 ## Memory Storage Types
-</a>
 
 There are specific `[6502]|byte`, `[6502]|word` and `[6502]|long` types for memory storage. They are used when reserving or defining data to include in the assembler program.
 

          
@@ 2038,9 2016,8 @@ The memory storage data types store nega
 * `[6502]|word` is a 16 bit data type
 * `[6502]|long` is a 32 bit data type
 
-<a name="storage-conversions">
+<div id="storage-conversions"></div>
 ## Storage Conversions
-</a>
 
 There are a number of functions in the root namespace dedicated to converting between the memory storage type ranges.
 

          
@@ 2049,7 2026,7 @@ There are a number of functions in the r
 		<th>Function</th>
 		<th>Accepted input types</th>
 		<th>Description</th>
-		<th width="35%">Examples</th>
+		<th style="width: 35%;">Examples</th>
 	</tr>
 	<tr>
 		<td><code>[6502]|byte(value)</code></td>

          
@@ 2071,9 2048,8 @@ There are a number of functions in the r
 	</tr>
 </table>
 
-<a name="math-functions">
+<div id="math-functions"></div>
 ## Math Functions
-</a>
 
 jAsm provides a number of mathematical functions in the root namespace.
 

          
@@ 2082,7 2058,7 @@ jAsm provides a number of mathematical f
 		<th>Function</th>
 		<th>Accepted input types</th>
 		<th>Description</th>
-		<th width="35%">Examples</th>
+		<th style="width: 35%;">Examples</th>
 	</tr>
 	<tr>
 		<td><code>[6502]|abs(x)</code></td>

          
@@ 2248,9 2224,8 @@ jAsm provides a number of mathematical f
 	</tr>
 </table>
 
-<a name="math-constants">
+<div id="math-constants"></div>
 ## Math Constants
-</a>
 
 jAsm has a couple of predefined constants in the root namespace.
 

          
@@ 2269,9 2244,8 @@ jAsm has a couple of predefined constant
 	</tr>
 </table>
 
-<a name="print-and-formatting">
+<div id="print-and-formatting"></div>
 ## Print and Formatting
-</a>
 
 There are two functions dedicated to formatting and printing, `[6502]|format` and `[6502]|print`. Both use the same arguments but `[6502]|format` returns the result and `[6502]|print` outputs it. Let's take `[6502]|format` as the example.
 

          
@@ 2349,9 2323,8 @@ To print an opening curly bracket, prefi
 	</tr>
 </table>
 
-<a name="symbol-functions">
+<div id="symbol-functions"></div>
 ## Symbol Functions
-</a>
 
 <table>
 	<tr>

          
@@ 2368,9 2341,8 @@ To print an opening curly bracket, prefi
 	</tr>
 </table>
 
-<a name="asserts">
+<div id="asserts"></div>
 ## Asserts
-</a>
 
 jAsm supports static asserts to help improve the robustness of your programs. Use those to verify limitations in your program. The following example shows a common use case.
 

          
@@ 2388,13 2360,11 @@ jAsm supports static asserts to help imp
 
 The first argument is a boolean expression. If this evaluates to `[6502]|false`, the assembler will generate an error and print the string in the second argument.
 
-<a name="sections">
+<div id="sections"></div>
 ## Sections
-</a>
-
-<a name="code-sections">
+
+<div id="code-sections"></div>
 ### Code Sections
-</a>
 
 To output anything, a jAsm source file needs to contain a code section. Here is a simple example program that changes the border color on a Commodore 64 and returns.
 

          
@@ 2416,9 2386,8 @@ An end address can be specified after th
 		rts
 	}
 
-<a name="sections-in-sections">
+<div id="sections-in-sections"></div>
 ### Sections in Sections
-</a>
 
 Sections can be placed within sections. This is useful in two cases, 1) store relocated code and 2) output the size and placement of code.
 

          
@@ 2486,9 2455,8 @@ The asterisk represents the current prog
 	$8000 - $804a ($004a) code: main
 	  $800e - $804a ($003c) code: string
 
-<a name="bss-sections">
+<div id="bss-sections"></div>
 ### BSS Sections
-</a>
 
 Bss sections is used to reserve memory for variables in your assembler program. This section type doesn't output anything, it just keeps track of a program counter to measure the size of reserved space. It isn't possible to place instructions or other data generating statements in a bss section. Reservation of space is done with the reserve statement.
 

          
@@ 2499,9 2467,8 @@ Bss sections is used to reserve memory f
 		reserve byte num_boosts
 	}
 
-<a name="section-parts">
+<div id="section-parts"></div>
 ### Section Parts
-</a>
 
 It is possible to add to an existing section later in the source code using section parts.
 

          
@@ 2518,9 2485,8 @@ It is possible to add to an existing sec
 
 A section part refers to the name of a previously defined section to add its contents to it. This can be used to create single file modules with code and variable reservations for specific systems. Empty sections can be created in a main file for zero page variables, code and variables and includes a number of modules. The modules adds to these sections to form a complete program.
 
-<a name="section-part-mapping">
+<div id="section-part-mapping"></div>
 ### Section Part Mapping
-</a>
 
 It is possible to name a module's sections using generic names like "code", "variables" and "zero page" and still have the power to map these to more specific section names in a main program.
 

          
@@ 2563,9 2529,8 @@ Several mappings can be specified like t
 		include "module.jasm"
 	}
 
-<a name="building-rom-images">
+<div id="building-rom-images"></div>
 ### Building ROM Images
-</a>
 
 Sections can be used to build large cartridge images with banks. Do that by creating an outer section for all the banks and one inner section per bank.
 

          
@@ 2605,9 2570,8 @@ An alternative is to use the [bank mode]
 
 That way, you can create macros to generate special code when jumping between banks based on the addresses of program counter and jump target address.
 
-<a name="building-overlayed-code-sections">
+<div id="building-overlayed-code-sections"></div>
 ### Building Overlayed Code Sectors
-</a>
 
 Sections can also be used when building a game that streams code from disk at runtime. Each streaming code sector gets its own section and the command line option [--output-multiple-files](#output-files-and-sections) is used to output one file per section. If the same code files are used in several streaming code sectors, you use namespaces to keep them apart.
 

          
@@ 2644,9 2608,8 @@ Sections can also be used when building 
 		}
 	}
 
-<a name="conditional-assembly">
+<div id="conditional-assembly"></div>
 ## Conditional Assembly
-</a>
 
 Code blocks can be selected or rejected with the `[6502]|if` statement.
 

          
@@ 2697,9 2660,8 @@ Note that the function lazy evaluates it
 	const .valid = 5
 	const .selected = select(true, .valid, .invalid)
 
-<a name="include-source">
+<div id="include-source"></div>
 ## Include Source
-</a>
 
 Large programs may need to be separated into several files. You can include other source files in a source file using the include statement.
 

          
@@ 2708,9 2670,8 @@ Large programs may need to be separated 
 
 This will act as if all the text in `[text]|some_file.jasm` was pasted over the include statement. Files will be searched for in the current directory first, and then all additional include directories specified by command line options.
 
-<a name="include-data">
+<div id="include-data"></div>
 ## Include Data
-</a>
 
 Data like pictures, sprites and character sets can be included in a code section, to be accessible from code. Use the `[6502]|incbin` statement for that.
 

          
@@ 2731,9 2692,8 @@ This will skip the first two bytes of th
 
 This will read at most 4 bytes from offset 2 in the specified file.
 
-<a name="defining-data">
+<div id="defining-data"></div>
 ## Defining Data
-</a>
 
 You can define data to be included in a code section using the define statement.
 

          
@@ 2823,9 2783,8 @@ This can also fill using a more complex 
 	[6502]
 	define byte[100] = { "HELLO WORLD!", ... }
 
-<a name="reserving-space">
+<div id="reserving-space"></div>
 ## Reserving Space
-</a>
 
 In bss sections you can allocate space for variables in your program. You use the reserve statement to do that.
 

          
@@ 2847,9 2806,8 @@ It is also possible to reserve space wit
 
 The `[6502]|sizeof` and `[6502]|offsetof` functions can also be used on reserved memory labels, just like for defined data.
 
-<a name="subroutines">
+<div id="subroutines"></div>
 ## Subroutines
-</a>
 
 To create a subroutine you really only need to place a label somewhere and jump to it. jAsm allows you to express it a bit more explicitly using the subroutine keyword.
 

          
@@ 2868,9 2826,8 @@ To create a subroutine you really only n
 
 The label `[6502]|multiply_by_8` is automatically created.
 
-<a name="enumerations">
+<div id="enumerations"></div>
 ## Enumerations
-</a>
 
 jAsm supports enumerated constants. It is a simplified way of assigning a series of numbers without specifying each number. It makes it easier to insert a value in the middle without re-enumerating all following numbers.
 

          
@@ 2911,15 2868,13 @@ Enum values can be specified relative to
 		paddle4 = device.joy2
 	}
 
-<a name="loops">
+<div id="loops"></div>
 ## Loops
-</a>
 
 Sometimes it is necessary to write a lot of repetitive code. jAsm supports loops for this purpose.
 
-<a name="for-loops">
+<div id="for-loops"></div>
 ### For Loop
-</a>
 
 The `[6502]|for` loop is a general form of loop that can be used in a large variety of situations. It is very similar to the C for-loop with a tighter set of options.
 

          
@@ 2931,9 2886,8 @@ The `[6502]|for` loop is a general form 
 
 This creates five nop instructions. The `[6502]|for` loop starts with an optional variable declaration, followed by a required ending condition expression and ends with an optional variable modification expression. The loop takes another pass as long as the ending condition expression evaluates to `[6502]|true`.
 
-<a name="range-based-for-loops">
+<div id="range-based-for-loops"></div>
 ### Range-based For Loop
-</a>
 
 A more specific form of for loop exists which conveniently iterates over lists.
 

          
@@ 2972,9 2926,8 @@ This type of loop can be used on maps as
 
 Note that the iteration over the map keys and values will not be done in any particular order.
 
-<a name="repeat-loop">
+<div id="repeat-loop"></div>
 ### Repeat Loop
-</a>
 
 The `[6502]|repeat` loop is a simplified version of the for loop. It can only repeat itself a fixed number of times and doesn't use a complex exit condition expression. It generates an automatic local label `[6502]|@i` as a zero based loop iteration counter.
 

          
@@ 2986,9 2939,8 @@ The `[6502]|repeat` loop is a simplified
 
 This defines numbers 0, 1, 2, 3 and 4 in memory.
 
-<a name="macros">
+<div id="macros"></div>
 ## Macros
-</a>
 
 Macros are a way to generate adaptable and reusable code blocks. A macro is a function type object which generates its contents where it is invoked. This is much like an inline function or a template function in C++.
 

          
@@ 3126,9 3078,8 @@ A macro can be ended early without retur
 
 Note that the return statement must end with a semicolon if no value is to be returned, otherwise an expression is expected.
 
-<a name="alignment">
+<div id="alignment"></div>
 ## Alignment
-</a>
 
 Sometimes code or data needs to be aligned to avoid extra cycles spent on traversing memory block boundaries. This is done with the align statement.
 

          
M jasm/io/data_reader.cpp +59 -28
@@ 20,13 20,15 @@ DataReader::DataReader(std::vector<std::
 
 DataReader::~DataReader()
 {
-	// wait for threaded operations
-	for(auto &file : _files)
-		file.file_request.wait();
+	#if defined(USE_THREADS)
+		// wait for threaded operations
+		for(auto &file : _files)
+			file.file_request.wait();
+	#endif
 }
 
 
-uint64_t DataReader::queue_load(const wchar_t *filename)
+uint64_t DataReader::queue_load(const std::wstring_view &filename)
 {
 	uint64_t handle = murmur_hash3_string_x64_64(filename);
 

          
@@ 42,13 44,16 @@ uint64_t DataReader::queue_load(const wc
 	file_info.filename = filename;
 
 	// start loading
-	auto load_func = [this](FileInfo *file) {
-		load(file);
-	};
-	file_info.file_request = std::async(std::launch::async, load_func, &file_info);
-	file_info.size_future = file_info.size_promise.get_future();
-	file_info.data_future = file_info.data_promise.get_future();
-
+	#if defined(USE_THREADS)
+		auto load_func = [this](FileInfo *file) {
+			load(file);
+		};
+		file_info.file_request = std::async(std::launch::async, load_func, &file_info);
+		file_info.size_future = file_info.size_promise.get_future();
+		file_info.data_future = file_info.data_promise.get_future();
+	#else
+		file_info.loaded = false;
+	#endif
 	return handle;
 }
 

          
@@ 59,8 64,15 @@ size_t DataReader::size(uint64_t handle)
 	assert(it != _handle_map.end());
 
 	FileInfo &info = *it->second;
-	if (info.size_future.valid())
-		info.size = info.size_future.get();
+	#if defined(USE_THREADS)
+		if (info.size_future.valid())
+			info.size = info.size_future.get();
+	#else
+		if (!info.loaded) {
+			load(&info);
+			info.loaded = true;
+		}
+	#endif
 
 	return info.size;
 }

          
@@ 72,9 84,16 @@ const std::vector<uint8_t> &DataReader::
 	assert(it != _handle_map.end());
 
 	FileInfo &info = *it->second;
-	if (info.data_future.valid())
-		return info.data_future.get();
-
+	#if defined(USE_THREADS)
+		if (info.data_future.valid())
+			return info.data_future.get();
+	#else
+		if (!info.loaded) {
+			load(&info);
+			info.loaded = true;
+		}
+	#endif
+	
 	return info.data;
 }
 

          
@@ 84,7 103,9 @@ void DataReader::load(FileInfo *file_inf
 	std::ifstream file;
 	size_t size;
 	std::wstring file_path;
+#if defined(USE_THREADS)
 	try {
+#endif
 		// find file amongst include directories
 		if (!match_include_dir_and_file(file_info->filename, _include_dirs, file_path)) {
 			std::wstringstream ss;

          
@@ 113,34 134,44 @@ void DataReader::load(FileInfo *file_inf
 		// get file size
 		size = static_cast<size_t>(file.tellg());
 
-		// nofify the caller of the size
-		file_info->size_promise.set_value(size);
+		#if defined(USE_THREADS)
+			// nofify the caller of the size
+			file_info->size_promise.set_value(size);
+		#else
+			file_info->size = size;
+		#endif
 
+#if defined(USE_THREADS)
 	} catch (Exception &) {
 		file_info->size_promise.set_exception(std::current_exception());
 		file_info->data_promise.set_exception(std::current_exception());
 		return;
 	}
+#endif
 
+#if defined(USE_THREADS)
 	try {
+#endif
 		file_info->data.resize(size);
 
-		if (size > 0) {
-			// read contents
-			file.seekg(0, std::ios::beg);
-			file.read(reinterpret_cast<char *>(&file_info->data[0]), static_cast<int64_t>(size));
+		// read contents
+		file.seekg(0, std::ios::beg);
+		file.read(reinterpret_cast<char *>(file_info->data.data()), static_cast<int64_t>(size));
+
+		if (!file.good())
+			throw FileException(L"Failed to read " + file_path);
 
-			if (!file.good())
-				throw FileException(L"Failed to read " + file_path);
-		}
+		#if defined(USE_THREADS)
+			// notify the caller of the data
+			file_info->data_promise.set_value(file_info->data);
+		#endif
 
-		// nofify the caller of the data
-		file_info->data_promise.set_value(file_info->data);
-
+#if defined(USE_THREADS)
 	} catch (Exception &) {
 		file_info->data_promise.set_exception(std::current_exception());
 		return;
 	}
+#endif
 }
 
 } // namespace jasm

          
M jasm/io/data_reader.h +13 -7
@@ 2,7 2,9 @@ 
 
 #include <core/collections/hash_map.h>
 #include <core/collections/null_hash_compare.h>
-#include <future>
+#if defined(USE_THREADS)
+	#include <future>
+#endif
 #include <list>
 
 namespace jasm {

          
@@ 24,7 26,7 @@ public:
 
 	/// Queue a load request.
 	/// @return Handle to the load request.
-	uint64_t queue_load(const wchar_t *filename);
+	uint64_t queue_load(const std::wstring_view &filename);
 
 	/// This will block until the size has been determined and then return it or a fail state.
 	/// @throw An exception is thrown if a file error occurred.

          
@@ 49,11 51,15 @@ private:
 	struct FileInfo {
 		std::wstring filename;
 
-		std::promise<size_t> size_promise;
-		std::future<size_t> size_future;
-		std::promise<std::vector<uint8_t> &> data_promise;
-		std::future<std::vector<uint8_t> &> data_future;
-		std::future<void> file_request;
+		#if defined(USE_THREADS)
+			std::promise<size_t> size_promise;
+			std::future<size_t> size_future;
+			std::promise<std::vector<uint8_t> &> data_promise;
+			std::future<std::vector<uint8_t> &> data_future;
+			std::future<void> file_request;
+		#else
+			bool loaded;
+		#endif
 
 		size_t size; ///< Cached size for when future has been consumed.
 		std::vector<uint8_t> data;

          
M jasm/main.cpp +3 -2
@@ 96,8 96,9 @@ std::wstring combine_filenames(const std
 
 void write_sections(CommandLineArgs &args, const std::vector<Section> &sections, const StringRepository &strings, const std::vector<std::wstring> &used_files)
 {
-	for(const Section &section : sections)
-		write_file(section.start_address, args.load_addr_header, args.multi_bank_mode, section, combine_filenames(args.output_file, std::wstring(L"_") + strings.get(section.name)), section.generated_data(), strings, used_files);
+	for(const Section &section : sections) {
+		write_file(section.start_address, args.load_addr_header, args.multi_bank_mode, section, combine_filenames(args.output_file, std::wstring(L"_") + std::wstring(strings.get(section.name))), section.generated_data(), strings, used_files);
+	}
 }
 
 void assemble(CommandLineArgs &args)

          
M jasm/parsing/keyword_finder.cpp +18 -21
@@ 9,7 9,6 @@ namespace jasm
 
 using namespace core;
 
-/// Set the keywords to match.
 void KeywordFinder::set_keywords(const std::vector<std::wstring> &keywords)
 {
 	if (keywords.empty())

          
@@ 34,21 33,23 @@ void KeywordFinder::set_keywords(const s
 		// add the keyword hash to the stored vector
 		_keyword_hashes.push_back(murmur_hash3_string_x64_64(keyword));
 
-		place_keyword(keyword.c_str());
+		place_keyword(keyword);
 	}
 }
 
-void KeywordFinder::place_keyword(const wchar_t *str)
+void KeywordFinder::place_keyword(std::wstring_view str)
 {
+	assert(!str.empty());
+
 	uint32_t node_index = 1; // zero is reserved as end marker
 
 	// find a matching character amongst the children
 	while (true) {
 		Node &node = _keyword_character_tree[node_index];
-		if (*str == node.character) {
-			++str;
+		if (str.front() == node.character) {
+			str.remove_prefix(1);
 
-			if (*str == 0) {
+			if (str.empty()) {
 				// found leaf, so set the leaf state
 				node.keyword_index = static_cast<uint16_t>(_keyword_hashes.size() - 1); // the hash has already been pushed
 				return;

          
@@ 59,7 60,7 @@ void KeywordFinder::place_keyword(const 
 				// create a child node
 				node.child_index = static_cast<uint32_t>(_keyword_character_tree.size());
 				node_index = node.child_index;
-				_keyword_character_tree.emplace_back(*str);
+				_keyword_character_tree.emplace_back(str.front());
 			} else
 				node_index = node.child_index;
 

          
@@ 74,27 75,23 @@ void KeywordFinder::place_keyword(const 
 			// point the previous sibling to the node after the last, where a new will be created
 			node.sibling_index = static_cast<uint32_t>(_keyword_character_tree.size());
 			node_index = node.sibling_index; // move to the new sibling
-			_keyword_character_tree.emplace_back(*str);
+			_keyword_character_tree.emplace_back(str.front());
 		}
 	}
 }
 
-/// Check for a keyword match in a null terminated string array.
-/// @param str Null terminated string to match keyword in.
-/// @param hash Murmur hash of the found keyword.
-/// @return true if a keyword matched.
-bool KeywordFinder::match(const wchar_t *str, uint64_t &hash)
+bool KeywordFinder::match(std::wstring_view str, uint64_t &hash)
 {
 	uint32_t node_index = 1; // zero is reserved as end marker
-	while (*str != 0 && node_index != 0) {
+	while (!str.empty() && node_index != 0) {
 		const Node &node = _keyword_character_tree[node_index];
 		// iterate through the tree to find the longest match
-		if (node.character == *str) {
+		if (node.character == str.front()) {
 			// correct sibling is found, proceed to next character and child
-			++str;
+			str.remove_prefix(1);
 			if (node.keyword_index != 0) {
 				// a matching keyword was found, but the string may be longer than the match
-				if (*str == 0) {
+				if (str.empty()) {
 					// the string is also ending here, so it is a match
 					hash = _keyword_hashes[node.keyword_index];
 					return true;

          
@@ 109,14 106,14 @@ bool KeywordFinder::match(const wchar_t 
 	return false;
 }
 
-bool KeywordFinder::match_beginning(const wchar_t *str, uint64_t &hash)
+bool KeywordFinder::match_beginning(std::wstring_view str, uint64_t &hash)
 {
 	uint32_t node_index = 1; // zero is reserved as end marker
 	bool matched = false;
-	while (*str != 0 && node_index != 0) {
+	while (!str.empty() && node_index != 0) {
 		const Node &node = _keyword_character_tree[node_index];
 		// iterate through the tree to find the longest match
-		if (node.character == *str) {
+		if (node.character == str.front()) {
 			// correct sibling is found, proceed to next character and child
 			if (node.keyword_index != 0) {
 				// a matching keyword was found, but we must continue in case a longer keyword matches

          
@@ 124,7 121,7 @@ bool KeywordFinder::match_beginning(cons
 				matched = true;
 				hash = _keyword_hashes[node.keyword_index];
 			}
-			++str;
+			str.remove_prefix(1);
 			node_index = node.child_index;
 		} else {
 			// not found yet, so move to next sibling

          
M jasm/parsing/keyword_finder.h +6 -6
@@ 13,18 13,18 @@ public:
 	/// Set the keywords to match.
 	void set_keywords(const std::vector<std::wstring> &keywords);
 
-	/// Check for a keyword match in a null terminated string array.
+	/// Check for a keyword match in a string view.
 	/// "++hello" will not match "++" since it isn't exact.
-	/// @param str Null terminated string to match keyword in.
+	/// @param str String to match keyword in.
 	/// @param hash Murmur hash of the found keyword.
 	/// @return true if a keyword matched.
-	bool match(const wchar_t *str, uint64_t &hash);
+	bool match(std::wstring_view str, uint64_t &hash);
 
-	/// Check for a keyword match in a null terminated string array.
+	/// Check for a keyword match in a string view.
 	/// This match is greedy and will ignore that there may be more characters
 	/// unmatched after the match. "++hello" will match "++".
 	/// @param str beginning of string to match.
-	bool match_beginning(const wchar_t *str, uint64_t &hash);
+	bool match_beginning(std::wstring_view str, uint64_t &hash);
 
 private:
 	struct Node

          
@@ 47,7 47,7 @@ private:
 	};
 
 	/// Place the keyword in the character tree.
-	void place_keyword(const wchar_t *str);
+	void place_keyword(std::wstring_view str);
 
 	std::vector<uint64_t> _keyword_hashes; ///< Hashes of each of the keywords in unhashed alphabetical order.
 	std::vector<Node> _keyword_character_tree; ///< Tree with characters to find keywords with.

          
M jasm/parsing/keywords.cpp +40 -40
@@ 4,58 4,58 @@ 
 
 namespace jasm {
 
-const wchar_t *to_string(KeywordType type)
+const std::wstring_view to_string(KeywordType type)
 {
-	static const wchar_t *names[] = {
-		L"include",
-		L"incbin",
-		L"section",
-		L"optimize",
-		L"namespace",
-		L"module",
-		L"import",
-		L"export",
-		L"using",
-		L"if",
-		L"else",
-		L"elif",
-		L"for",
-		L"macro",
-		L"return",
-		L"align",
-		L"subroutine",
-		L"function",
-		L"struct",
-		L"enum",
-		L"declare",
-		L"const",
-		L"var",
-		L"define",
-		L"reserve",
-		L"basic",
-		L"dynamic",
-		L"address",
-		L"repeat",
+	static const std::wstring_view names[] = {
+		std::wstring_view(L"include"),
+		std::wstring_view(L"incbin"),
+		std::wstring_view(L"section"),
+		std::wstring_view(L"optimize"),
+		std::wstring_view(L"namespace"),
+		std::wstring_view(L"module"),
+		std::wstring_view(L"import"),
+		std::wstring_view(L"export"),
+		std::wstring_view(L"using"),
+		std::wstring_view(L"if"),
+		std::wstring_view(L"else"),
+		std::wstring_view(L"elif"),
+		std::wstring_view(L"for"),
+		std::wstring_view(L"macro"),
+		std::wstring_view(L"return"),
+		std::wstring_view(L"align"),
+		std::wstring_view(L"subroutine"),
+		std::wstring_view(L"function"),
+		std::wstring_view(L"struct"),
+		std::wstring_view(L"enum"),
+		std::wstring_view(L"declare"),
+		std::wstring_view(L"const"),
+		std::wstring_view(L"var"),
+		std::wstring_view(L"define"),
+		std::wstring_view(L"reserve"),
+		std::wstring_view(L"basic"),
+		std::wstring_view(L"dynamic"),
+		std::wstring_view(L"address"),
+		std::wstring_view(L"repeat"),
 		#if defined(_DEBUG)
-			L"_debug_",
+			std::wstring_view(L"_debug_"),
 		#endif
 	};
-	static_assert(sizeof(names) / sizeof(wchar_t *) == static_cast<int>(KeywordType::NumTypes), "Number of keywords doesn't match number of strings");
+	static_assert(sizeof(names) / sizeof(names[0]) == static_cast<size_t>(KeywordType::NumTypes), "Number of keywords doesn't match number of strings");
 
 	assert(type < KeywordType::NumTypes);
-	return names[static_cast<int>(type)];
+	return names[static_cast<size_t>(type)];
 }
 
-const wchar_t *to_string(BooleanType type)
+const std::wstring_view to_string(BooleanType type)
 {
-	static const wchar_t *names[] = {
-		L"false",
-		L"true",
+	static const std::wstring_view names[] = {
+		std::wstring_view(L"false"),
+		std::wstring_view(L"true"),
 	};
-	static_assert(sizeof(names) / sizeof(wchar_t *) == static_cast<int>(BooleanType::NumTypes), "Number of booleans doesn't match number of strings");
+	static_assert(sizeof(names) / sizeof(names[0]) == static_cast<size_t>(BooleanType::NumTypes), "Number of booleans doesn't match number of strings");
 
 	assert(type < BooleanType::NumTypes);
-	return names[static_cast<int>(type)];
+	return names[static_cast<size_t>(type)];
 }
 
 } // namespace jasm

          
M jasm/parsing/keywords.h +2 -2
@@ 49,8 49,8 @@ enum class BooleanType : uint8_t
 	NumTypes,
 };
 
-const wchar_t *to_string(KeywordType type);
-const wchar_t *to_string(BooleanType type);
+const std::wstring_view to_string(KeywordType type);
+const std::wstring_view to_string(BooleanType type);
 
 /// @}
 

          
M jasm/parsing/operators.cpp +62 -62
@@ 51,102 51,102 @@ static OperatorDesc operators[] = {
 };
 
 
-const wchar_t *to_string(OperatorType type)
+const std::wstring_view to_string(OperatorType type)
 {
-	static const wchar_t *names[] = {
-		L"()", // token not used in tokenizer but used to order functions in syntax parser
-		L"[]", // token not used in tokenizer but used to order functions in syntax parser
-		L".",
+	static const std::wstring_view names[] = {
+		std::wstring_view(L"()"), // token not used in tokenizer but used to order functions in syntax parser
+		std::wstring_view(L"[]"), // token not used in tokenizer but used to order functions in syntax parser
+		std::wstring_view(L"."),
 
 		// operator precedence 3
-		L"!",
-		L"~",
+		std::wstring_view(L"!"),
+		std::wstring_view(L"~"),
 
-		L"+", // token not used in tokenizer but used to order functions in syntax parser
-		L"-", // token not used in tokenizer but used to order functions in syntax parser
-		L"<", // token not used in tokenizer but used to order functions in syntax parser
-		L">", // token not used in tokenizer but used to order functions in syntax parser
+		std::wstring_view(L"+"), // token not used in tokenizer but used to order functions in syntax parser
+		std::wstring_view(L"-"), // token not used in tokenizer but used to order functions in syntax parser
+		std::wstring_view(L"<"), // token not used in tokenizer but used to order functions in syntax parser
+		std::wstring_view(L">"), // token not used in tokenizer but used to order functions in syntax parser
 
 		// operator precedence 4
-		L"<<",
-		L">>",
+		std::wstring_view(L"<<"),
+		std::wstring_view(L">>"),
 
 		// operator precedence 5
-		L"*",
-		L"/",
+		std::wstring_view(L"*"),
+		std::wstring_view(L"/"),
 
 		// operator precedence 6
-		L"+",
-		L"-",
+		std::wstring_view(L"+"),
+		std::wstring_view(L"-"),
 
 		// operator precedence 7
-		L"&",
+		std::wstring_view(L"&"),
 
 		// operator precedence 8
-		L"^",
+		std::wstring_view(L"^"),
 
 		// operator precedence 9
-		L"|",
+		std::wstring_view(L"|"),
 
 		// operator precedence 10
-		L"<",
-		L">",
-		L"<=",
-		L">=",
+		std::wstring_view(L"<"),
+		std::wstring_view(L">"),
+		std::wstring_view(L"<="),
+		std::wstring_view(L">="),
 
 		// operator precedence 11
-		L"==",
-		L"!=",
+		std::wstring_view(L"=="),
+		std::wstring_view(L"!="),
 
 		// operator precedence 12
-		L"&&",
+		std::wstring_view(L"&&"),
 
 		// operator precedence 13
-		L"||",
+		std::wstring_view(L"||"),
 
 		// reference operators
-		L"=",
-		L"+=",
-		L"-=",
-		L"*=",
-		L"/=",
-		L"&&=",
-		L"||=",
-		L"&=",
-		L"|=",
-		L"^=",
-		L"<<=",
-		L">>=",
+		std::wstring_view(L"="),
+		std::wstring_view(L"+="),
+		std::wstring_view(L"-="),
+		std::wstring_view(L"*="),
+		std::wstring_view(L"/="),
+		std::wstring_view(L"&&="),
+		std::wstring_view(L"||="),
+		std::wstring_view(L"&="),
+		std::wstring_view(L"|="),
+		std::wstring_view(L"^="),
+		std::wstring_view(L"<<="),
+		std::wstring_view(L">>="),
 
 		// operator precedence 2
-		L"++", // token not used in tokenizer but used to order functions in syntax parser
-		L"--", // token not used in tokenizer but used to order functions in syntax parser
+		std::wstring_view(L"++"), // token not used in tokenizer but used to order functions in syntax parser
+		std::wstring_view(L"--"), // token not used in tokenizer but used to order functions in syntax parser
 		// operator precedence 3
-		L"++", // token not used in tokenizer but used to order functions in syntax parser
-		L"--", // token not used in tokenizer but used to order functions in syntax parser
+		std::wstring_view(L"++"), // token not used in tokenizer but used to order functions in syntax parser
+		std::wstring_view(L"--"), // token not used in tokenizer but used to order functions in syntax parser
 
 		// other operators
-		L":",
-		L"::",
-		L";",
-		L",",
-		L"#",
-		L"%%",
-		L"(",
-		L")",
-		L"[",
-		L"]",
-		L"{",
-		L"}",
-		L"++",
-		L"--",
-		L"@",
-		L"...",
+		std::wstring_view(L":"),
+		std::wstring_view(L"::"),
+		std::wstring_view(L";"),
+		std::wstring_view(L","),
+		std::wstring_view(L"#"),
+		std::wstring_view(L"%%"),
+		std::wstring_view(L"("),
+		std::wstring_view(L")"),
+		std::wstring_view(L"["),
+		std::wstring_view(L"]"),
+		std::wstring_view(L"{"),
+		std::wstring_view(L"}"),
+		std::wstring_view(L"++"),
+		std::wstring_view(L"--"),
+		std::wstring_view(L"@"),
+		std::wstring_view(L"..."),
 	};
-	static_assert(sizeof(names) / sizeof(wchar_t *) == static_cast<int>(OperatorType::NumTypes), "Number of operators doesn't match number of strings");
+	static_assert(sizeof(names) / sizeof(names[0]) == static_cast<size_t>(OperatorType::NumTypes), "Number of operators doesn't match number of strings");
 
 	assert(type < OperatorType::NumTypes);
-	return names[static_cast<int>(type)];
+	return names[static_cast<size_t>(type)];
 }
 
 

          
M jasm/parsing/operators.h +1 -1
@@ 119,7 119,7 @@ inline bool is_assignment(OperatorType t
 
 const OperatorDesc &operator_desc(OperatorType type);
 
-const wchar_t *to_string(OperatorType type);
+const std::wstring_view to_string(OperatorType type);
 
 /// @}
 

          
M jasm/parsing/processor_keywords_6502.cpp +6 -6
@@ 7,17 7,17 @@ 
 namespace jasm
 {
 
-const wchar_t *to_string(ProcessorKeywordType type)
+const std::wstring_view to_string(ProcessorKeywordType type)
 {
-	static const wchar_t *names[] = {
+	static const std::wstring_view names[] = {
 		// instruction registers
-		L"x",
-		L"y",
+		std::wstring_view(L"x"),
+		std::wstring_view(L"y"),
 	};
-	static_assert(sizeof(names) / sizeof(names[0]) == static_cast<int>(ProcessorKeywordType::NumTypes), "Number of registers/conditions doesn't match number of strings");
+	static_assert(sizeof(names) / sizeof(names[0]) == static_cast<size_t>(ProcessorKeywordType::NumTypes), "Number of registers/conditions doesn't match number of strings");
 
 	assert(type < ProcessorKeywordType::NumTypes);
-	return names[static_cast<int>(type)];