M .hgignore +1 -0
@@ 20,3 20,4 @@ hasher/bin/**/*
 jasm/bin/**/*
 jasm/output.bin
 jasm/test.asm
+build/**/*

          
A => .kdev4/jasm.kdev4 +69 -0
@@ 0,0 1,69 @@ 
+[Buildset]
+BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\x08\x00j\x00a\x00s\x00m)
+
+[CMake]
+Build Directory Count=1
+Current Build Directory Index-Host System=0
+
+[CMake][CMake Build Directory 0]
+Build Directory Path=/home/jonas/dev/c64/jasm/build
+Build Type=Debug
+CMake Binary=/usr/bin/cmake
+CMake Executable=/usr/bin/cmake
+Environment Profile=
+Extra Arguments=
+Install Directory=/usr/local
+Runtime=Host System
+
+[Launch]
+Launch Configurations=Launch Configuration 0,Launch Configuration 1,Launch Configuration 2
+
+[Launch][Launch Configuration 0]
+Configured Launch Modes=execute
+Configured Launchers=nativeAppLauncher
+Name=jasm-6502
+Type=Native Application
+
+[Launch][Launch Configuration 0][Data]
+Arguments=test.jasm -v3 test.prg
+Dependencies=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x03\x00\x00\x00\x08\x00j\x00a\x00s\x00m\x00\x00\x00\x12\x00j\x00a\x00s\x00m\x00-\x006\x005\x000\x002\x00\x00\x00\x12\x00j\x00a\x00s\x00m\x00-\x006\x005\x000\x002)
+Dependency Action=Build
+EnvironmentGroup=
+Executable=file:///home/jonas/dev/c64/jasm
+External Terminal=konsole --noclose --workdir %workdir -e %exe
+Project Target=jasm,jasm-6502,jasm-6502
+Use External Terminal=false
+Working Directory=file:///home/jonas/dev/c64/jasm/jasm/test
+isExecutable=false
+
+[Launch][Launch Configuration 1]
+Configured Launch Modes=execute
+Configured Launchers=nativeAppLauncher
+Name=jasm-z80
+Type=Native Application
+
+[Launch][Launch Configuration 1][Data]
+Arguments=test.jasm -v3 test.prg
+Dependencies=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x03\x00\x00\x00\x08\x00j\x00a\x00s\x00m\x00\x00\x00\x10\x00j\x00a\x00s\x00m\x00-\x00z\x008\x000\x00\x00\x00\x10\x00j\x00a\x00s\x00m\x00-\x00z\x008\x000)
+Dependency Action=Build
+EnvironmentGroup=
+Executable=file:///home/jonas/dev/c64/jasm
+External Terminal=konsole --noclose --workdir %workdir -e %exe
+Project Target=jasm,jasm-z80,jasm-z80
+Use External Terminal=false
+Working Directory=file:///home/jonas/dev/c64/jasm/jasm/test
+isExecutable=false
+
+[Launch][Launch Configuration 2]
+Configured Launch Modes=execute
+Configured Launchers=nativeAppLauncher
+Name=hasher
+Type=Native Application
+
+[Launch][Launch Configuration 2][Data]
+Dependencies=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x03\x00\x00\x00\x08\x00j\x00a\x00s\x00m\x00\x00\x00\x0c\x00h\x00a\x00s\x00h\x00e\x00r\x00\x00\x00\x0c\x00h\x00a\x00s\x00h\x00e\x00r)
+Dependency Action=Build
+Project Target=jasm,hasher,hasher
+
+[Project]
+VersionControlSupport=kdevgit

          
M README.md +3 -3
@@ 4,13 4,13 @@ jAsm currently compiles with CMake for L
 
 # Command line build with CMake in Linux
 
-To build with CMake you need CMake 3.5, Clang, git and python3 installed. On Debian, Ubuntu or Mint systems you can use apt-get to fetch the dependencies like this.
+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.
 
-	sudo apt-get install cmake clang git python3
+	sudo apt-get install cmake clang mercurial python3
 
 Clone the repository into a directory called 'jasm' and build it like this.
 
-	git clone https://bitbucket.org/bjonte/jasm.git jasm
+	hg clone ssh://hg@bitbucket.org/bjonte/jasm
 	cd jasm
 	export CXX=/usr/bin/clang++
 	mkdir build

          
A => jasm.kdev4 +4 -0
@@ 0,0 1,4 @@ 
+[Project]
+CreatedFrom=CMakeLists.txt
+Manager=KDevCMakeManager
+Name=jasm

          
M jasm.sublime-project +1 -1
@@ 26,7 26,7 @@ 
 	[
 		{
 			"name": "Make",
-			"cmd": [ "make" ],
+			"cmd": [ "make", "-j4" ],
 			"working_dir": "${project_path}/build",
 			"file_regex": "^(.+):(\\d+):(\\d+):\\s+\\w+:\\s+(.*)$"
 		},

          
R jasm.workspace =>  +0 -12
@@ 1,12 0,0 @@ 
-<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
-<CodeBlocks_workspace_file>
-	<Workspace title="Workspace">
-		<Project filename="core/core.cbp" />
-		<Project filename="jasm/jasm.cbp">
-			<Depends filename="core/core.cbp" />
-		</Project>
-		<Project filename="hasher/hasher.cbp">
-			<Depends filename="core/core.cbp" />
-		</Project>
-	</Workspace>
-</CodeBlocks_workspace_file>
  No newline at end of file

          
M jasm/assembling/assembler_impl/assembler_impl.cpp +188 -78
@@ 40,8 40,9 @@ Assembler::Assembler(bool multiple_outpu
 	, _vice_dump_file(vice_dump_file)
 	, _gba_dump_file(gba_dump_file)
 	, _dump_symbols(!symbol_dump_file.empty() || !vice_dump_file.empty() || !gba_dump_file.empty())
-	, _current_pass_types(token_chain_type_buffer_size)
-	, _previous_pass_types(token_chain_type_buffer_size)
+	, _current_pass(token_chain_type_buffer_size)
+	, _previous_pass(token_chain_type_buffer_size)
+	, _oscillating_state(false)
 	, _last_pass_hash(0)
 	, _static_none_type(0)
 	, _static_unknown_type(0)

          
@@ 85,8 86,7 @@ Assembler::Assembler(bool multiple_outpu
 	// add empty string for zero hash because the root namespace has hash 0
 	_strings.add(0, L"");
 
-	if (_dump_symbols)
-		_symbol_names[0] = L"";
+	_symbol_names[0] = L"";
 }
 
 void Assembler::fill_type_integer_operators(TypeDescription &type)

          
@@ 134,21 134,21 @@ void Assembler::fill_type_array_operator
 void Assembler::setup_fundamental_types()
 {
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::None;
 		_static_none_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::Unknown;
 		_static_unknown_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::BoolValue;

          
@@ 162,7 162,7 @@ void Assembler::setup_fundamental_types(
 		_static_bool_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::IntegerValue;

          
@@ 170,7 170,7 @@ void Assembler::setup_fundamental_types(
 		_static_integer_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::FloatValue;

          
@@ 193,7 193,7 @@ void Assembler::setup_fundamental_types(
 		_static_float_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = static_cast<uint32_t>(StringProperties::NumProperties);
 		TypeDescriptionWithPayload &type = reserve_type(num_properties);
 		type.type = ValueType::StringValue;

          
@@ 212,7 212,7 @@ void Assembler::setup_fundamental_types(
 		_static_string_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = static_cast<uint32_t>(StringProperties::NumProperties);
 		TypeDescriptionWithPayload &type = reserve_type(num_properties);
 		type.type = ValueType::StringReference;

          
@@ 231,7 231,7 @@ void Assembler::setup_fundamental_types(
 		_static_string_reference_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::RangeValue;

          
@@ 239,7 239,7 @@ void Assembler::setup_fundamental_types(
 		_static_range_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::ByteOffset;

          
@@ 248,7 248,7 @@ void Assembler::setup_fundamental_types(
 		_static_byte_offset_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::WordOffset;

          
@@ 257,7 257,7 @@ void Assembler::setup_fundamental_types(
 		_static_word_offset_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::LongOffset;

          
@@ 266,7 266,7 @@ void Assembler::setup_fundamental_types(
 		_static_long_offset_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::ValueReference;

          
@@ 274,7 274,7 @@ void Assembler::setup_fundamental_types(
 		_static_value_reference_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::FunctionReference;

          
@@ 282,7 282,7 @@ void Assembler::setup_fundamental_types(
 		_static_function_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::ByteTypename;

          
@@ 291,7 291,7 @@ void Assembler::setup_fundamental_types(
 		_static_byte_type_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::WordTypename;

          
@@ 300,7 300,7 @@ void Assembler::setup_fundamental_types(
 		_static_word_type_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::LongTypename;

          
@@ 309,7 309,7 @@ void Assembler::setup_fundamental_types(
 		_static_long_type_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::MacroReference;

          
@@ 317,7 317,7 @@ void Assembler::setup_fundamental_types(
 		_static_macro_reference = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::MethodClosure;

          
@@ 325,7 325,7 @@ void Assembler::setup_fundamental_types(
 		_static_method_closure_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = static_cast<uint32_t>(ListProperties::NumProperties);
 		TypeDescriptionWithPayload &type = reserve_type(num_properties);
 		type.type = ValueType::ListReference;

          
@@ 345,7 345,7 @@ void Assembler::setup_fundamental_types(
 		_static_list_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = 0;
 		TypeDescription &type = reserve_type(num_properties);
 		type.type = ValueType::ListElementReference;

          
@@ 353,7 353,7 @@ void Assembler::setup_fundamental_types(
 		_static_list_element_type = add_type_to_type_map(type, p);
 	}
 	{
-		TokenReadPosition p = _current_pass_types.position();
+		TokenReadPosition p = _current_pass.types.position();
 		uint32_t num_properties = static_cast<uint32_t>(MapProperties::NumProperties);
 		TypeDescriptionWithPayload &type = reserve_type(num_properties);
 		type.type = ValueType::MapReference;

          
@@ 375,7 375,7 @@ uint64_t Assembler::add_array_type_descr
 {
 	const TypeDescription &underlying_type = find_type(underlying_type_hash);
 
-	TokenReadPosition p = _current_pass_types.position();
+	TokenReadPosition p = _current_pass.types.position();
 
 	uint32_t num_properties = 0;
 	TypeDescription &type = reserve_type(num_properties);

          
@@ 436,7 436,7 @@ void Assembler::setup_predefined_constan
 		if (!_strings.has(symbol_hash))
 			_strings.add(symbol_hash, pair.first);
 		if (create_label(generate, symbol_hash, is_global, StorageType::Constant, beginning)) {
-			Value &new_value = _current_pass_values.back();
+			Value &new_value = _current_pass.values.back();
 			set_boolean(new_value, pair.second);
 		}
 	}

          
@@ 445,7 445,7 @@ void Assembler::setup_predefined_constan
 		if (!_strings.has(symbol_hash))
 			_strings.add(symbol_hash, pair.first);
 		if (create_label(generate, symbol_hash, is_global, StorageType::Constant, beginning)) {
-			Value &new_value = _current_pass_values.back();
+			Value &new_value = _current_pass.values.back();
 			set_integer(new_value, pair.second);
 		}
 	}

          
@@ 458,7 458,7 @@ void Assembler::setup_predefined_constan
 		_strings.add(string_hash, pair.second);
 
 		if (create_label(generate, symbol_hash, is_global, StorageType::Constant, beginning)) {
-			Value &new_value = _current_pass_values.back();
+			Value &new_value = _current_pass.values.back();
 			set_string(new_value, string_hash);
 		}
 	}

          
@@ 519,6 519,28 @@ void Assembler::propagate_children_data_
 		propagate_data(section);
 }
 
+void Assembler::prepare_next_assembly_pass()
+{
+	// store this pass' section part sizes for next pass
+	collect_section_sizes(_sections);
+
+	// move current pass symbol information to previous pass
+	std::swap(_current_pass.values, _previous_pass.values);
+	_current_pass.values.clear();
+
+	_previous_pass.types = std::move(_current_pass.types);
+	_current_pass.types = TokenChain(token_chain_type_buffer_size);
+
+	std::swap(_current_pass.value_lookup, _previous_pass.value_lookup);
+	_current_pass.value_lookup.clear();
+
+	std::swap(_current_pass.type_lookup, _previous_pass.type_lookup);
+	_current_pass.type_lookup.clear();
+
+	std::swap(_current_pass.namespaces, _previous_pass.namespaces);
+	_current_pass.namespaces.clear();
+}
+
 void Assembler::run_assembly_pass(bool generate, int pass)
 {
 	debug() << L"Assemble pass " << pass << L'\n';

          
@@ 527,8 549,8 @@ void Assembler::run_assembly_pass(bool g
 	assert(_data_generation_depth == 0);
 
 	_input_reader = TokenReader(_input[0]);
-	_current_pass_type_reader = TokenReader(_current_pass_types);
-	_previous_pass_type_reader = TokenReader(_previous_pass_types);
+	_current_pass.type_reader = TokenReader(_current_pass.types);
+	_previous_pass.type_reader = TokenReader(_previous_pass.types);
 	_sections.clear();
 	_section = nullptr;
 

          
@@ 541,7 563,7 @@ void Assembler::run_assembly_pass(bool g
 	// allocated first 8 bytes to be sure that 0 is unused. It may come in handy
 	// as a null pointer.
 	static_assert(sizeof(void *) == 8, "Pointer is expected to be 8 bytes");
-	void *&null_padding = _current_pass_types.reserve<void *>();
+	void *&null_padding = _current_pass.types.reserve<void *>();
 	null_padding = nullptr;
 
 	// constants uses fundamental types so the order matters

          
@@ 579,25 601,6 @@ void Assembler::run_assembly_pass(bool g
 				dump_symbols(base_name(_symbol_dump_file) + std::to_wstring(pass) + file_extension(_symbol_dump_file));
 			}
 		#endif
-
-		// store this pass' section part sizes for next pass
-		collect_section_sizes(_sections);
-
-		// move current pass symbol information to previous pass
-		std::swap(_current_pass_values, _previous_pass_values);
-		_current_pass_values.clear();
-
-		_previous_pass_types = std::move(_current_pass_types);
-		_current_pass_types = TokenChain(token_chain_type_buffer_size);
-
-		std::swap(_current_pass_value_lookup, _previous_pass_value_lookup);
-		_current_pass_value_lookup.clear();
-
-		std::swap(_current_pass_type_lookup, _previous_pass_type_lookup);
-		_current_pass_type_lookup.clear();
-
-		std::swap(_current_pass_namespaces, _previous_pass_namespaces);
-		_current_pass_namespaces.clear();
 	}
 
 }

          
@@ 643,9 646,9 @@ Section *Assembler::find_section(uint64_
 bool Assembler::progress_was_made()
 {
 	// hash types
-	uint64_t storage_hash = _previous_pass_types.hash();
+	uint64_t storage_hash = _current_pass.types.hash();
 	// hash values
-	for(const auto &value : _previous_pass_values) {
+	for(const auto &value : _current_pass.values) {
 		storage_hash = value.hash(storage_hash);
 	}
 	// TODO: hash variable map

          
@@ 669,19 672,18 @@ bool Assembler::progress_was_made()
 		// The variables are oscillating and doesn't stabilize!
 		// This will go on forever so we must exit and warn the
 		// user that the result will not assemble.
-		SourceLocation location;
-		location.file_index = 0;
-		location.column = 1;
-		location.row = 1;
-		const wchar_t * const message =
-			L"The variable state doesn't stabilize!\n"
-			L"This is an indication that the source code contains conflicting conditional\n"
-			L"code. Try disabling the latest changes in conditional blocks or code referring\n"
-			L"to conditional blocks to see what triggers this condition. Unfortunately it is\n"
-			L"not possible right now to get detailed information about exactly where the\n"
-			L"problem is.\n";
-		const bool fatal = true;
-		report_error(location, AssemblyErrorCodes::UnstableVariableState, message, fatal);
+		_oscillating_state = true;
+
+		if (_dump_symbols) {
+			// We have already done a pass with dump symbols turned on (to collect a reverse
+			// lookup for symbol names so report no more progress.
+			return false;
+		} else {
+			// Turn on dump symbols flag to start storing reverse lookups for symbols in the next pass.
+			_dump_symbols = true;
+			// Fake progress to get another pass
+			return true;
+		}
 	}
 
 	return false;

          
@@ 695,6 697,17 @@ struct SymbolInformation
 		, name(std::move(name_))
 		, value(std::move(value_))
 		, constant(constant_)
+		, current_pass(true)
+		, type(type_)
+	{}
+
+	SymbolInformation(uint64_t hash_, size_t index_, std::wstring name_, std::wstring value_, bool constant_, bool current_pass_, ValueType type_)
+		: hash(hash_)
+		, index(index_)
+		, name(std::move(name_))
+		, value(std::move(value_))
+		, constant(constant_)
+		, current_pass(current_pass_)
 		, type(type_)
 	{}
 

          
@@ 710,19 723,99 @@ struct SymbolInformation
 	std::wstring name; ///< Readable symbol name.
 	std::wstring value; ///< Readable value contents.
 	bool constant; ///< True if constant, otherwise variable.
+	bool current_pass; ///< True if found in current pass. This is only used when comparing variables across passes.
 	ValueType type; ///< Type of the value.
 };
 
+void Assembler::create_difference_report(std::wstringstream &ss)
+{
+	// collect and sort symbol names
+	std::vector<SymbolInformation> symbol_list;
+	symbol_list.reserve(_current_pass.value_lookup.size() + 100);
+
+	// find symbols in either pass
+	core::HashMap<uint64_t, bool, core::NullHashCompare<uint64_t>> used_symbols;
+    std::wstring no_name = L"<no name>";
+	for(const auto &pair : _current_pass.value_lookup) {
+		uint64_t hash = pair.first;
+		size_t value_index = pair.second;
+		const Value &value = _current_pass.values[value_index];
+		auto it = _symbol_names.find(hash);
+        const std::wstring &symbol_name = it == _symbol_names.end() ? no_name : it->second;
+		symbol_list.emplace_back(hash, value_index, symbol_name, to_string(_strings, value), value.storage_type != StorageType::Variable, value.type);
+		used_symbols[hash] = true;
+	}
+	for(const auto &pair : _previous_pass.value_lookup) {
+		uint64_t hash = pair.first;
+		if (UNLIKELY(!used_symbols.has(hash))) {
+			size_t value_index = pair.second;
+			const Value &value = _previous_pass.values[value_index];
+			auto it = _symbol_names.find(hash);
+            const std::wstring &symbol_name = it == _symbol_names.end() ? no_name : it->second;
+			symbol_list.emplace_back(hash, value_index, symbol_name, to_string(_strings, value), value.storage_type != StorageType::Variable, false, value.type);
+			used_symbols[hash] = true;
+		}
+	}
+	std::sort(symbol_list.begin(), symbol_list.end());
+
+	// compare values with previous pass and print differences
+	for(const SymbolInformation &symbol : symbol_list) {
+		if (symbol.current_pass) {
+			// first check if the symbol exists in the other pass
+			auto it = _previous_pass.value_lookup.find(symbol.hash);
+			if (it != _previous_pass.value_lookup.end()) {
+				const Value &current = _current_pass.values[symbol.index];
+				const Value &previous = _previous_pass.values[it->second];
+				if (current.hash(0) == previous.hash(0)) {
+					// they are actually the same, so skip printing this
+					continue;
+				} else {
+					ss << std::setw(16) << to_string(_strings, previous) << std::setw(0);
+					ss << L" | ";
+					ss << std::setw(16) << symbol.value << std::setw(0);
+				}
+			} else {
+				ss << std::setw(16) << L"<missing>" << std::setw(0);
+				ss << L" | ";
+				ss << std::setw(16) << symbol.value << std::setw(0);
+			}
+
+		} else {
+			// first check if the symbol exists in the other pass
+			auto it = _current_pass.value_lookup.find(symbol.hash);
+			if (it != _current_pass.value_lookup.end()) {
+				const Value &current = _current_pass.values[it->second];
+				const Value &previous = _previous_pass.values[symbol.index];
+				if (current.hash(0) == previous.hash(0)) {
+					// they are actually the same, so skip printing this
+					continue;
+				} else {
+					ss << std::setw(16) << symbol.value << std::setw(0);
+					ss << L" | ";
+					ss << std::setw(16) << to_string(_strings, current) << std::setw(0);
+				}
+			} else {
+				ss << std::setw(16) << symbol.value << std::setw(0);
+				ss << L" | ";
+				ss << std::setw(16) << L"<missing>" << std::setw(0);
+			}
+		}
+
+		ss << L" | ";
+		ss << symbol.name << L'\n';
+	}
+}
+
 void Assembler::dump_symbols(const std::wstring &filename)
 {
 	// extract all relevant symbol information
 	std::vector<SymbolInformation> symbol_list;
-	symbol_list.reserve(_current_pass_value_lookup.size());
+	symbol_list.reserve(_current_pass.value_lookup.size());
 
-	for(const auto &pair : _current_pass_value_lookup) {
+	for(const auto &pair : _current_pass.value_lookup) {
 		uint64_t hash = pair.first;
 		size_t value_index = pair.second;
-		const Value &value = _current_pass_values[value_index];
+		const Value &value = _current_pass.values[value_index];
 		auto it = _symbol_names.find(hash);
 		symbol_list.emplace_back(hash, value_index, it == _symbol_names.end() ? L"<no name>" : it->second, to_string(_strings, value), value.storage_type != StorageType::Variable, value.type);
 	}

          
@@ 791,16 884,16 @@ void Assembler::dump_vice_symbols(const 
 	std::set<uint32_t> breakpoints;
 	std::set<uint32_t> r_breakpoints;
 	std::set<uint32_t> w_breakpoints;
-	symbol_list.reserve(_current_pass_value_lookup.size());
+	symbol_list.reserve(_current_pass.value_lookup.size());
 
 	std::wstring breakpoint_begin = L"breakpoint";
 	std::wstring r_breakpoint_begin = L"read_breakpoint";
 	std::wstring w_breakpoint_begin = L"write_breakpoint";
 
-	for(const auto &pair : _current_pass_value_lookup) {
+	for(const auto &pair : _current_pass.value_lookup) {
 		uint64_t hash = pair.first;
 		size_t value_index = pair.second;
-		const Value &value = _current_pass_values[value_index];
+		const Value &value = _current_pass.values[value_index];
 		auto it = _symbol_hash_names.find(hash);
 
 		// only add constants which we have stored names for

          
@@ 897,12 990,12 @@ void Assembler::dump_gba_symbols(const s
 {
 	// extract all relevant symbol information
 	std::vector<SimpleSymbolInformation> symbol_list;
-	symbol_list.reserve(_current_pass_value_lookup.size());
+	symbol_list.reserve(_current_pass.value_lookup.size());
 
-	for(const auto &pair : _current_pass_value_lookup) {
+	for(const auto &pair : _current_pass.value_lookup) {
 		uint64_t hash = pair.first;
 		size_t value_index = pair.second;
-		const Value &value = _current_pass_values[value_index];
+		const Value &value = _current_pass.values[value_index];
 		auto it = _symbol_hash_names.find(hash);
 
 		// only add constants which we have stored names for

          
@@ 1035,8 1128,9 @@ void Assembler::assemble()
 	int pass = 1;
 	while (true) {
 		TimerScope timer(L"Assemble pass");
-		constexpr bool place_labels = false;
-		run_assembly_pass(place_labels, pass);
+		prepare_next_assembly_pass();
+		constexpr bool generate_code = false;
+		run_assembly_pass(generate_code, pass);
 
 		++pass;
 

          
@@ 1044,11 1138,27 @@ void Assembler::assemble()
 			break;
 	}
 
+	if (_oscillating_state) {
+		error() <<
+			L"The variable state doesn't stabilize!\n"
+			L"This may indicate that the source code contains conflicting conditional code.\n"
+			L"Try disabling the latest changes in conditional blocks or code referring to\n"
+			L"conditional blocks to see what triggers this condition. Unfortunately it is\n"
+			L"not possible right now to get detailed information about exactly where the\n"
+			L"problem is.\n";
+
+		std::wstringstream ss;
+		ss << L"Differing symbols in last two passes:\n";
+		create_difference_report(ss);
+		debug() << ss.str();
+	}
+
 	{
 		TimerScope timer(L"Generate pass");
+		prepare_next_assembly_pass();
 		constexpr bool generate_code = true;
 		run_assembly_pass(generate_code, pass);
-		if (_num_errors != 0)
+		if (_num_errors != 0 || _oscillating_state)
 			throw AssemblyException(L"Assembly ended with errors.");
 	}
 

          
M jasm/assembling/assembler_impl/assembler_impl.h +32 -23
@@ 88,8 88,8 @@ private:
 
 	inline Value &get_value(bool current_pass, uint64_t combined_hash)
 	{
-		ImmovableValueVector &vv = current_pass ? _current_pass_values : _previous_pass_values;
-		const ValueMap &vm = current_pass ? _current_pass_value_lookup : _previous_pass_value_lookup;
+		ImmovableValueVector &vv = current_pass ? _current_pass.values : _previous_pass.values;
+		const ValueMap &vm = current_pass ? _current_pass.value_lookup : _previous_pass.value_lookup;
 		auto it = vm.find(combined_hash);
 		assert(it != vm.end());
 		auto variable_index = it->second;

          
@@ 97,8 97,8 @@ private:
 	}
 	inline const Value &get_value(bool current_pass, uint64_t combined_hash) const
 	{
-		const ImmovableValueVector &vv = current_pass ? _current_pass_values : _previous_pass_values;
-		const ValueMap &vm = current_pass ? _current_pass_value_lookup : _previous_pass_value_lookup;
+		const ImmovableValueVector &vv = current_pass ? _current_pass.values : _previous_pass.values;
+		const ValueMap &vm = current_pass ? _current_pass.value_lookup : _previous_pass.value_lookup;
 		auto it = vm.find(combined_hash);
 		assert(it != vm.end());
 		auto variable_index = it->second;

          
@@ 333,14 333,14 @@ private:
 	/// 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));
+		TypeDescriptionWithPayload &type = *static_cast<TypeDescriptionWithPayload *>(_current_pass.types.reserve(type_size));
 		memset(&type, 0, type_size);
 		return type;
 	}
 
 	inline uint64_t add_type_to_type_map(const TypeDescription &type, TokenReadPosition position, uint64_t seed = 0) {
 		uint64_t hash = type.hash(seed);
-		_current_pass_type_lookup[hash] = position;
+		_current_pass.type_lookup[hash] = position;
 		return hash;
 	}
 

          
@@ 975,6 975,12 @@ private:
 	/// Log any type of error.
 	void report_error(const SourceLocation &location, AssemblyErrorCodes error_code, const std::wstring &msg, bool fatal);
 
+	/// Create a report of the difference between current and previous pass.
+	void create_difference_report(std::wstringstream &ss);
+
+	/// Move state from current pass to previous pass.
+	void prepare_next_assembly_pass();
+
 	void run_assembly_pass(bool generate, int pass);
 
 	bool progress_was_made();

          
@@ 1005,6 1011,23 @@ private:
 	/// Remove the bss sections to only leave the ones producing output.
 	void cleanup_sections();
 
+	using TypeMap = core::HashMap<uint64_t, TokenReadPosition, core::NullHashCompare<uint64_t>>;
+	using HashSet = std::set<uint64_t>;
+
+	struct Pass
+	{
+		Pass(uint32_t type_buffer_size)
+			: types(type_buffer_size)
+		{}
+
+		ImmovableValueVector values; ///< Variable values in the assembly pass.
+		TokenChain types; ///< Type information data for the assembly pass.
+		TokenReader type_reader; ///< Reads the static type information.
+		TypeMap type_lookup; ///< Map from type hash to index for type information in 'types'.
+		ValueMap value_lookup; ///< Map from global namespace+symbol hash or local instance+symbol hash to value array index.
+		HashSet namespaces; ///< Combined hashes for pass namespaces. This is used to determine uses of undeclared namespaces.
+	};
+
 	// input data
 	bool _multiple_output_files; ///< When true, write one file per section. Otherwise merge them together.
 	bool _multi_bank_mode; ///< When true, the assembler will truncate addresses in instructions.

          
@@ 1025,27 1048,13 @@ private:
 	StringConversions _string_conversions; ///< Converts strings to platform specific formats. 
 	TokenReader _input_reader; ///< Reads the syntax input tokens.
 
-	ImmovableValueVector _current_pass_values; ///< Variable values in the current assembly pass.
-	ImmovableValueVector _previous_pass_values; ///< Variable values in the previous assembly pass.
-	TokenChain _current_pass_types; ///< Type information data for the current assembly pass.
-	TokenChain _previous_pass_types; ///< Type information data for the previous assembly pass.
-	TokenReader _current_pass_type_reader; ///< Reads the static type information.
-	TokenReader _previous_pass_type_reader; ///< Reads the static type information.
+	Pass _current_pass;
+	Pass _previous_pass;
+	bool _oscillating_state;
 
-	using TypeMap = core::HashMap<uint64_t, TokenReadPosition, core::NullHashCompare<uint64_t>>;
-	TypeMap _current_pass_type_lookup; ///< Map from type hash to index for type information in _current_pass_types.
-	TypeMap _previous_pass_type_lookup; ///< Map from type hash to index for type information in _previous_pass_types.
-
-	ValueMap _current_pass_value_lookup; ///< Map from global namespace+symbol hash or local instance+symbol hash to value array index.
-	ValueMap _previous_pass_value_lookup; ///< Map from global namespace+symbol hash or local instance+symbol hash to value array index.
-
-	using HashSet = std::set<uint64_t>;
 	HashSet _symbol_storage_hashes; ///< Hashes for previous symbol storage snapshots. This is used to determine loops in assembly state.
 	uint64_t _last_pass_hash; ///< The state hash of the previous pass. This is used to determine if the state has stabilized or loops.
 
-	HashSet _previous_pass_namespaces; ///< Combined hashes for previous pass namespaces. This is used to determine uses of undeclared namespaces.
-	HashSet _current_pass_namespaces; ///< Combined hashes for current pass namespaces. This is used to determine uses of undeclared namespaces.
-
 	SymbolEnvironment _symbol_environment; ///< Information about the current scope.
 
 	std::vector<uint64_t> _section_mappings; ///< This contains pairs of source and target section names used in section parts to remap sections. This is searched back to front (in pairs) to get overrides to work.

          
M jasm/assembling/assembler_impl/symbols_impl.cpp +32 -32
@@ 320,13 320,13 @@ void Assembler::store_symbol_string_in_r
 
 void Assembler::store_symbol_value(uint64_t symbol_hash, const Value &value, bool global, bool is_address, StorageType type)
 {
-	size_t value_index = _current_pass_values.size();
-	_current_pass_values.push_back(value);
+	size_t value_index = _current_pass.values.size();
+	_current_pass.values.push_back(value);
 
 	// store hash and value index
-	_current_pass_value_lookup.insert(symbol_hash) = value_index;
+	_current_pass.value_lookup.insert(symbol_hash) = value_index;
 
-	Value &new_value = _current_pass_values.back();
+	Value &new_value = _current_pass.values.back();
 	new_value.global = global;
 	new_value.set_contains_address(is_address);
 	new_value.storage_type = type;

          
@@ 338,21 338,21 @@ void Assembler::store_symbol_value(uint6
 
 bool Assembler::find_global_symbol_in_passes(uint64_t combined_hash, bool &found_in_current_pass, Value *&found_value)
 {
-	ValueMap::const_iterator it = _current_pass_value_lookup.find(combined_hash);
-	if (it != _current_pass_value_lookup.end()) {
+	ValueMap::const_iterator it = _current_pass.value_lookup.find(combined_hash);
+	if (it != _current_pass.value_lookup.end()) {
 		found_in_current_pass = true;
-		found_value = &_current_pass_values[it->second];
+		found_value = &_current_pass.values[it->second];
 		return true;
 	}
 
 	// Not found in current pass so we must look in the previous pass
-	it = _previous_pass_value_lookup.find(combined_hash);
-	if (it != _previous_pass_value_lookup.end()) {
-		const Value &previous_value = _previous_pass_values[it->second];
+	it = _previous_pass.value_lookup.find(combined_hash);
+	if (it != _previous_pass.value_lookup.end()) {
+		const Value &previous_value = _previous_pass.values[it->second];
 		// variables can't use previous pass
 		if (previous_value.storage_type != StorageType::Variable) {
 			found_in_current_pass = false;
-			found_value = &_previous_pass_values[it->second];
+			found_value = &_previous_pass.values[it->second];
 			return true;
 		}
 	}

          
@@ 554,7 554,7 @@ const Value *Assembler::find_local_symbo
 	//    a) another instance is matched in the previous pass
 
 	const Value *value;
-	bool found = find_local_symbol_in_pass(symbol_hash, _current_pass_value_lookup, _current_pass_values, result_hash, value);
+	bool found = find_local_symbol_in_pass(symbol_hash, _current_pass.value_lookup, _current_pass.values, result_hash, value);
 	if (found) {
 		if (value->storage_type == StorageType::Variable) {
 			found_in_current_pass = true;

          
@@ 563,7 563,7 @@ const Value *Assembler::find_local_symbo
 		// check case a) for another instance matched in the previous pass
 		uint64_t previous_pass_hash;
 		const Value *previous_value;
-		found = find_local_symbol_in_pass(symbol_hash, _previous_pass_value_lookup, _previous_pass_values, previous_pass_hash, previous_value);
+		found = find_local_symbol_in_pass(symbol_hash, _previous_pass.value_lookup, _previous_pass.values, previous_pass_hash, previous_value);
 		if (found && previous_pass_hash != result_hash) {
 			// a) is satisfied
 			found_in_current_pass = false;

          
@@ 574,7 574,7 @@ const Value *Assembler::find_local_symbo
 		found_in_current_pass = true;
 		return value;
 	}
-	found = find_local_symbol_in_pass(symbol_hash, _previous_pass_value_lookup, _previous_pass_values, result_hash, value);
+	found = find_local_symbol_in_pass(symbol_hash, _previous_pass.value_lookup, _previous_pass.values, result_hash, value);
 	if (found) {
 		// variables can't use previous pass
 		if (value->storage_type != StorageType::Variable) {

          
@@ 636,16 636,16 @@ void Assembler::evaluate_symbol(bool gen
 
 const TypeDescriptionWithPayload &Assembler::find_type(uint64_t type_hash) const
 {
-	auto it = _current_pass_type_lookup.find(type_hash);
-	if (it != _current_pass_type_lookup.end()) {
+	auto it = _current_pass.type_lookup.find(type_hash);
+	if (it != _current_pass.type_lookup.end()) {
 		// found in current pass
-		return _current_pass_type_reader.next_type<TypeDescriptionWithPayload>(it->second);
+		return _current_pass.type_reader.next_type<TypeDescriptionWithPayload>(it->second);
 	}
-	it = _previous_pass_type_lookup.find(type_hash);
-	assert(it != _previous_pass_type_lookup.end());
+	it = _previous_pass.type_lookup.find(type_hash);
+	assert(it != _previous_pass.type_lookup.end());
 
 	// found in previous pass
-	return _previous_pass_type_reader.next_type<TypeDescriptionWithPayload>(it->second);
+	return _previous_pass.type_reader.next_type<TypeDescriptionWithPayload>(it->second);
 }
 
 void Assembler::enter_variable_scope(uint64_t local_hash, bool add_loop_variable)

          
@@ 703,7 703,7 @@ uint64_t Assembler::enter_namespace(uint
 	store_namespace_string(combined_hash, _symbol_environment.namespace_names_scope_stack);
 
 	// remember all namespaces to detect using statements with undefined namespaces
-	_current_pass_namespaces.insert(combined_hash);
+	_current_pass.namespaces.insert(combined_hash);
 
 	return combined_hash;
 }

          
@@ 801,8 801,8 @@ bool Assembler::create_label(bool genera
 	// verify uniqueness
 	bool unique = true;
 	// check for duplicate names
-	ValueMap::iterator it = _current_pass_value_lookup.find(symbol_hash);
-	if (it != _current_pass_value_lookup.end()) {
+	ValueMap::iterator it = _current_pass.value_lookup.find(symbol_hash);
+	if (it != _current_pass.value_lookup.end()) {
 		// The symbol already exists. This can in rare cases be valid in early
 		// passes so we need to let it go unnoticed by skipping the declaration.
 		// However, in the generation pass this is a fatal error.

          
@@ 828,10 828,10 @@ bool Assembler::create_label(bool genera
 	}
 
 	// The symbol is unique. Create a new symbol value.
-	size_t value_index = _current_pass_values.size();
-	_current_pass_values.emplace_back();
+	size_t value_index = _current_pass.values.size();
+	_current_pass.values.emplace_back();
 
-	Value &value = _current_pass_values.back();
+	Value &value = _current_pass.values.back();
 	value.global = global;
 	value.storage_type = storage_type;
 	value.owning_module = _symbol_environment.module_namespace_stack.back();

          
@@ 840,7 840,7 @@ bool Assembler::create_label(bool genera
 
 	// store hash and value index
 	if (unique)
-		_current_pass_value_lookup.insert(symbol_hash) = value_index;
+		_current_pass.value_lookup.insert(symbol_hash) = value_index;
 
 	return unique;
 }

          
@@ 861,10 861,10 @@ Value &Assembler::create_unique_label(ui
 Value &Assembler::create_combined_unique_label(uint64_t combined_hash, bool global)
 {
 	// Create a new symbol
-	size_t value_index = _current_pass_values.size();
-	_current_pass_values.emplace_back();
+	size_t value_index = _current_pass.values.size();
+	_current_pass.values.emplace_back();
 
-	Value &value = _current_pass_values.back();
+	Value &value = _current_pass.values.back();
 	value.global = global;
 	value.storage_type = StorageType::Constant;
 	value.owning_module = _symbol_environment.module_namespace_stack.back();

          
@@ 872,8 872,8 @@ Value &Assembler::create_combined_unique
 		value.set_is_public(true);
 
 	// store hash and value index
-	_current_pass_value_lookup.insert(combined_hash) = value_index;
-	return _current_pass_values.back();
+	_current_pass.value_lookup.insert(combined_hash) = value_index;
+	return _current_pass.values.back();
 }
 
 } // namespace jasm

          
M jasm/assembling/assembler_impl/syntax_impl.cpp +27 -27
@@ 444,7 444,7 @@ const SyntaxToken *Assembler::parse_decl
 	if (create_label(generate, symbol_hash, global, def_token->storage_type, def_token->source_location)) {
 		// The symbol is unique. Store evaluated expression.
 
-		Value &value = _current_pass_values.back();
+		Value &value = _current_pass.values.back();
 
 		// parse and store value
 		const Value value_or_ref = evaluate_expression(generate, t);

          
@@ 524,7 524,7 @@ const SyntaxToken *Assembler::parse_inst
 		}
 
 		if (create_label(generate, instruction_token.data_label_symbol_hash, instruction_token.global_data_label, StorageType::Constant, instruction_token.address_label_location)) {
-			Value &new_label = _current_pass_values.back();
+			Value &new_label = _current_pass.values.back();
 			set_integer(new_label, _program_counter.integer_value + 1);
 			new_label.set_contains_address(true);
 			if (export_enabled) {

          
@@ 692,7 692,7 @@ const SyntaxToken *Assembler::parse_inst
 				}
 
 				if (create_label(generate, instruction_token.data_label_symbol_hash[i], instruction_token.global_data_label[i], StorageType::Constant, instruction_token.address_label_location[i])) {
-					Value &new_label = _current_pass_values.back();
+					Value &new_label = _current_pass.values.back();
 					set_integer(new_label, _program_counter.integer_value + argument_data_offset(i, opcode_data));
 					new_label.set_contains_address(true);
 					if (export_enabled) {

          
@@ 1009,9 1009,9 @@ const SyntaxToken *Assembler::parse_rese
 	if (reserve.named)
 		create_label(generate, reserve.name_hash, reserve.global, StorageType::Constant, reserve.source_location); // ignoring if it was unique...
 	else
-		_current_pass_values.emplace_back(); // create a dummy value to simplify code below
-
-	Value &value = _current_pass_values.back();
+		_current_pass.values.emplace_back(); // create a dummy value to simplify code below
+
+	Value &value = _current_pass.values.back();
 	value.set_contains_address(true); // mark as address
 	if (export_enabled)
 		value.set_is_public(true);

          
@@ 1406,9 1406,9 @@ const SyntaxToken *Assembler::parse_defi
 	if (define.named)
 		create_label(generate, define.name_hash, define.global, StorageType::Constant, define.source_location); // ignoring if it was unique...
 	else
-		_current_pass_values.emplace_back(); // create a dummy value to simplify code below
-
-	Value &value = _current_pass_values.back();
+		_current_pass.values.emplace_back(); // create a dummy value to simplify code below
+
+	Value &value = _current_pass.values.back();
 	value.set_contains_address(true); // mark as address
 	if (export_enabled)
 		value.set_is_public(true);

          
@@ 1481,7 1481,7 @@ const SyntaxToken *Assembler::parse_macr
 
 	create_label(generate, macro.name_hash, macro.global, StorageType::Constant, macro.source_location); // ignoring if it was unique...
 
-	Value &value = _current_pass_values.back();
+	Value &value = _current_pass.values.back();
 	value.type = ValueType::MacroReference;
 	value.type_hash = _static_macro_reference;
 	value.macro_chain_index = macro.chain_index;

          
@@ 1539,8 1539,8 @@ const SyntaxToken *Assembler::parse_rang
 
 	// create a new variable
 	create_label(generate, loop_token.loop_value_hash, loop_token.global_value, StorageType::Variable, loop_token.value_location); // return value is ignored here since it is ok to use the dummy value
-	Value &loop_value = _current_pass_values.back();
-	size_t new_variable_index = _current_pass_values.size() - 1;
+	Value &loop_value = _current_pass.values.back();
+	size_t new_variable_index = _current_pass.values.size() - 1;
 
 	// make a copy of the loop vector to avoid all kinds of iteration problems since the list can be
 	// modified while iterating

          
@@ 1575,7 1575,7 @@ const SyntaxToken *Assembler::parse_rang
 			uint64_t symbol_hash = murmur_hash3_x64_64(&loop_token.loop_value_hash, sizeof(loop_token.loop_value_hash), scope_hash);
 			if (UNLIKELY(_dump_symbols))
 				combine_and_store_hash_name(scope_hash, symbol_hash, variable_name(loop_token.loop_value_hash, loop_token.global_value));
-			_current_pass_value_lookup[symbol_hash] = new_variable_index;
+			_current_pass.value_lookup[symbol_hash] = new_variable_index;
 
 			// add the loop iteration variable @i for convenience
 			uint64_t loop_symbol = hash_constant(0x1ded7765ceceebccULL, L"@i");

          
@@ 1610,13 1610,13 @@ const SyntaxToken *Assembler::parse_rang
 
 	// create key variable
 	create_label(generate, loop_token.loop_key_hash, loop_token.global_key, StorageType::Variable, loop_token.key_location); // return value is ignored here since it is ok to use the dummy value
-	Value &loop_key = _current_pass_values.back();
-	size_t new_key_index = _current_pass_values.size() - 1;
+	Value &loop_key = _current_pass.values.back();
+	size_t new_key_index = _current_pass.values.size() - 1;
 
 	// create value variable
 	create_label(generate, loop_token.loop_value_hash, loop_token.global_value, StorageType::Variable, loop_token.value_location); // return value is ignored here since it is ok to use the dummy value
-	Value &loop_value = _current_pass_values.back();
-	size_t new_variable_index = _current_pass_values.size() - 1;
+	Value &loop_value = _current_pass.values.back();
+	size_t new_variable_index = _current_pass.values.size() - 1;
 
 	// make a copy of the loop vector to avoid all kinds of iteration problems since the map can be
 	// modified while iterating

          
@@ 1652,13 1652,13 @@ const SyntaxToken *Assembler::parse_rang
 			uint64_t key_symbol_hash = murmur_hash3_x64_64(&loop_token.loop_key_hash, sizeof(loop_token.loop_key_hash), key_scope_hash);
 			if (UNLIKELY(_dump_symbols))
 				combine_and_store_hash_name(key_scope_hash, key_symbol_hash, variable_name(loop_token.loop_key_hash, loop_token.global_key));
-			_current_pass_value_lookup[key_symbol_hash] = new_key_index;
+			_current_pass.value_lookup[key_symbol_hash] = new_key_index;
 
 			uint64_t scope_hash = loop_token.global_value ? _symbol_environment.namespace_scope_stack.back() : _symbol_environment.local_symbol_scope_stack.back();
 			uint64_t symbol_hash = murmur_hash3_x64_64(&loop_token.loop_value_hash, sizeof(loop_token.loop_value_hash), scope_hash);
 			if (UNLIKELY(_dump_symbols))
 				combine_and_store_hash_name(scope_hash, symbol_hash, variable_name(loop_token.loop_value_hash, loop_token.global_value));
-			_current_pass_value_lookup[symbol_hash] = new_variable_index;
+			_current_pass.value_lookup[symbol_hash] = new_variable_index;
 		};
 		t = parse_scope(generate, t, early_return, inject_variable);
 

          
@@ 1725,7 1725,7 @@ const SyntaxToken *Assembler::parse_for_
 	// to solve the problem of generating an error when the variable is redeclared
 	// in the loop body. A symbol link is injected in the scope below pointing to this
 	// value.
-	size_t new_variable_index = _current_pass_values.size();
+	size_t new_variable_index = _current_pass.values.size();
 	if (loop_token->has_pre_statement) {
 		t = parse_statement(generate, t, early_return);
 		assert(!early_return); // return not allowed in for loop pre statement

          
@@ 1789,7 1789,7 @@ const SyntaxToken *Assembler::parse_for_
 				uint64_t symbol_hash = murmur_hash3_x64_64(&loop_token->loop_variable_hash, sizeof(loop_token->loop_variable_hash), scope_hash);
 				if (UNLIKELY(_dump_symbols))
 					combine_and_store_hash_name(scope_hash, symbol_hash, variable_name(loop_token->loop_variable_hash, loop_token->global));
-				_current_pass_value_lookup[symbol_hash] = new_variable_index;
+				_current_pass.value_lookup[symbol_hash] = new_variable_index;
 			};
 			t = parse_scope(generate, t, early_return, inject_variable);
 		} else {

          
@@ 1911,7 1911,7 @@ const SyntaxToken *Assembler::parse_enum
 	t = consume_next_token();
 
 	// create a new type description
-	TokenReadPosition position = _current_pass_types.position();
+	TokenReadPosition position = _current_pass.types.position();
 	TypeDescriptionWithPayload &type_desc = reserve_type(enum_def.num_definitions);
 	type_desc.type = ValueType::EnumReference;
 	type_desc.num_properties = enum_def.num_definitions;

          
@@ 1983,7 1983,7 @@ const SyntaxToken *Assembler::parse_enum
 
 	// now create a real value and assign the temporary value to it
 	create_label(generate, enum_def.name_hash, enum_def.global, StorageType::Constant, enum_def.source_location); // ignoring if it was unique...
-	Value &value = _current_pass_values.back();
+	Value &value = _current_pass.values.back();
 	value = std::move(temp_value);
 
 	return t;

          
@@ 2136,7 2136,7 @@ const SyntaxToken *Assembler::parse_usin
 	// detect using missing namespace
 	if (generate) {
 		// since last pass was identical to the current, only the last pass namespaces needs to be considered
-		if (_previous_pass_namespaces.find(combined_hash) == _previous_pass_namespaces.end()) {
+		if (_previous_pass.namespaces.find(combined_hash) == _previous_pass.namespaces.end()) {
 			std::wstringstream ss;
 			ss << L"Using an undefined namespace.";
 			report_error(using_token.source_location, AssemblyErrorCodes::UsingUndefinedNamespace, ss.str());

          
@@ 2211,8 2211,8 @@ const SyntaxToken *Assembler::parse_modu
 				// for the, say, 10 first imports with minimal work.
 				constexpr bool global = true;
 				if (create_label(generate, import_hash, global, StorageType::Constant, mt.imports_location)) {
-					assert(_current_pass_value_lookup.has(import_combined_hash));
-					Value &new_import_value = _current_pass_values[_current_pass_value_lookup[import_combined_hash]];
+					assert(_current_pass.value_lookup.has(import_combined_hash));
+					Value &new_import_value = _current_pass.values[_current_pass.value_lookup[import_combined_hash]];
 					set_unknown(new_import_value);
 					new_import_value.set_is_module_import(true);
 					new_import_value.set_is_public(true);

          
@@ 2303,7 2303,7 @@ const SyntaxToken *Assembler::parse_subr
 	// create the label after parsing to be able to construct the value with the size of the range
 	create_label(generate, subroutine.name_hash, subroutine.global, StorageType::Constant, subroutine.source_location); // ignore if it was unique
 
-	Value &value = _current_pass_values.back();
+	Value &value = _current_pass.values.back();
 	set_range(value, start_address, _program_counter.integer_value - start_address);
 	value.set_contains_address(true); // mark as address
 	if (export_enabled)

          
M jasm/docs/jasm.md +5 -12
@@ 42,7 42,6 @@ This documentation covers the language a
 * [Compiling jAsm](#compiling-jasm)
    * [Fetching Source Code](#fetching-source-code)
    * [Compiling Using CMake](#compiling-using-cmake)
-   * [Compiling Using Code::Blocks](#compiling-using-codeblocks)
    * [Compiling Using Visual Studio](#compiling-using-vs)
 * [Starting jAsm](#starting-jasm)
    * [Bank Mode](#bank-mode)

          
@@ 955,10 954,10 @@ Now you know the basics of jAsm and shou
 ## Fetching Source Code
 </a>
 
-You need to fetch the source code from BitBucket to get started. If you have a command line git client you can clone the repository like this.
+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.
 
 	[text]
-	git clone https://bitbucket.org/bjonte/jasm.git jasm
+	hg clone ssh://hg@bitbucket.org/bjonte/jasm
 
 jAsm compiles using CMake and Clang or using Code::Blocks or Visual Studio.
 

          
@@ 966,15 965,15 @@ jAsm compiles using CMake and Clang or u
 ## Compiling Using CMake
 </a>
 
-To build with CMake you need CMake 3.5, Clang, git and python3 installed. On Debian, Ubuntu or Mint systems you can use apt-get to fetch the dependencies like this.
+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.
 
 	[text]
-	sudo apt-get install cmake clang git python3
+	sudo apt-get install cmake clang mercurial python3
 
 Clone the repository into a directory called 'jasm' and build it like this.
 
 	[text]
-	git clone https://bitbucket.org/bjonte/jasm.git jasm
+	hg clone ssh://hg@bitbucket.org/bjonte/jasm
 	cd jasm
 	export CXX=/usr/bin/clang++
 	mkdir build

          
@@ 982,12 981,6 @@ Clone the repository into a directory ca
 	cmake -DCMAKE_BUILD_TYPE=Release ..
 	sudo make install
 
-<a name="compiling-using-codeblocks">
-## Compiling Using Code::Blocks
-</a>
-
-jAsm compiles using Clang under Linux in Code::Blocks. Open the jasm.workspace file, select the release configuration for a desired processor and build.
-
 <a name="compiling-using-vs">
 ## Compiling Using Visual Studio
 </a>

          
M jasm/unit_tests/results/test_unstable_variable_state.stdout +5 -6
@@ 1,8 1,7 @@ 
-unit_tests/test_unstable_variable_state.asm(1,1) : Error 3093 : The variable state doesn't stabilize!
-This is an indication that the source code contains conflicting conditional
-code. Try disabling the latest changes in conditional blocks or code referring
-to conditional blocks to see what triggers this condition. Unfortunately it is
+The variable state doesn't stabilize!
+This may indicate that the source code contains conflicting conditional code.
+Try disabling the latest changes in conditional blocks or code referring to
+conditional blocks to see what triggers this condition. Unfortunately it is
 not possible right now to get detailed information about exactly where the
 problem is.
-
-Fatal error, aborting assembly.
+Assembly ended with errors.

          
M jasm/version.h +1 -1
@@ 1,1 1,1 @@ 
-1,21
+1,22

          
M jasm/version.py +18 -18
@@ 27,26 27,26 @@ def source_revision():
 Returns a tuple with revision (as int) and hash (as string)."""
 
 	# Code for Mercurial
-	#hg_output = subprocess.check_output(["hg", "summary"])
-	#hg_output = hg_output.decode("latin1")
-	#for line in hg_output.splitlines():
-	#	m = re.match(r"parent:\s*(\d+):([0-9a-zA-Z]+)\s+.*", line)
-	#	if m:
-	#		return (int(m.group(1)), m.group(2))
+	hg_output = subprocess.check_output(["hg", "summary"])
+	hg_output = hg_output.decode("latin1")
+	for line in hg_output.splitlines():
+		m = re.match(r"parent:\s*(\d+):([0-9a-zA-Z]+)\s+.*", line)
+		if m:
+			return (int(m.group(1)), m.group(2))
 
 	# Code for Git
-	current_branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).decode("utf-8").strip()
-	revs_in_master = int(subprocess.check_output(["git", "rev-list", current_branch, "--count"]))
-	revs_in_branch = int(subprocess.check_output(["git", "rev-list", current_branch+"..", "--count"]))
-	if revs_in_branch == 0:
-		rev = revs_in_master
-	else:
-		revs_from_branch_to_master = int(subprocess.check_output(["git", "rev-list", current_branch+"...", "--count"]))
-		revs_to_branch = revs_in_master - (revs_from_branch_to_master - revs_in_branch)
-		rev = revs_to_branch + revs_in_branch
-
-	hsh = subprocess.check_output(["git", "rev-parse", "HEAD"]).strip().decode('utf-8-sig')
-	return (rev, hsh)
+	#current_branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"]).decode("utf-8").strip()
+	#revs_in_master = int(subprocess.check_output(["git", "rev-list", current_branch, "--count"]))
+	#revs_in_branch = int(subprocess.check_output(["git", "rev-list", current_branch+"..", "--count"]))
+	#if revs_in_branch == 0:
+	#	rev = revs_in_master
+	#else:
+	#	revs_from_branch_to_master = int(subprocess.check_output(["git", "rev-list", current_branch+"...", "--count"]))
+	#	revs_to_branch = revs_in_master - (revs_from_branch_to_master - revs_in_branch)
+	#	rev = revs_to_branch + revs_in_branch
+	#
+	#hsh = subprocess.check_output(["git", "rev-parse", "HEAD"]).strip().decode('utf-8-sig')
+	#return (rev, hsh)
 
 def update_revision():
 	"""Get source code revision and update revision and hash files."""

          
M jasm/website/site/docs/index.html +7 -16
@@ 59,7 59,6 @@ 
 <ul>
 <li><a href="#fetching-source-code">Fetching Source Code</a></li>
 <li><a href="#compiling-using-cmake">Compiling Using CMake</a></li>
-<li><a href="#compiling-using-codeblocks">Compiling Using Code::Blocks</a></li>
 <li><a href="#compiling-using-vs">Compiling Using Visual Studio</a></li>
 </ul></li>
 <li><a href="#starting-jasm">Starting jAsm</a>

          
@@ 1030,9 1029,9 @@ main.jasm(25,7) : Error 3004 : Reference
 
 <p></a></p>
 
-<p>You need to fetch the source code from BitBucket to get started. If you have a command line git client you can clone the repository like this.</p>
-
-<pre><code>git clone https://bitbucket.org/bjonte/jasm.git jasm
+<p>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.</p>
+
+<pre><code>hg clone ssh://hg@bitbucket.org/bjonte/jasm
 </code></pre>
 
 <p>jAsm compiles using CMake and Clang or using Code::Blocks or Visual Studio.</p>

          
@@ 1043,14 1042,14 @@ main.jasm(25,7) : Error 3004 : Reference
 
 <p></a></p>
 
-<p>To build with CMake you need CMake 3.5, Clang, git and python3 installed. On Debian, Ubuntu or Mint systems you can use apt-get to fetch the dependencies like this.</p>
-
-<pre><code>sudo apt-get install cmake clang git python3
+<p>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.</p>
+
+<pre><code>sudo apt-get install cmake clang mercurial python3
 </code></pre>
 
 <p>Clone the repository into a directory called 'jasm' and build it like this.</p>
 
-<pre><code>git clone https://bitbucket.org/bjonte/jasm.git jasm
+<pre><code>hg clone ssh://hg@bitbucket.org/bjonte/jasm
 cd jasm
 export CXX=/usr/bin/clang++
 mkdir build

          
@@ 1059,14 1058,6 @@ cmake -DCMAKE_BUILD_TYPE=Release ..
 sudo make install
 </code></pre>
 
-<p><a name="compiling-using-codeblocks"></p>
-
-<h2>Compiling Using Code::Blocks</h2>
-
-<p></a></p>
-
-<p>jAsm compiles using Clang under Linux in Code::Blocks. Open the jasm.workspace file, select the release configuration for a desired processor and build.</p>
-
 <p><a name="compiling-using-vs"></p>
 
 <h2>Compiling Using Visual Studio</h2>

          
M jasm/website/site/index.html +9 -1
@@ 81,7 81,7 @@ 
 				<p>
 					Windows binaries requires the <a href="http://www.microsoft.com/en-us/download/details.aspx?id=48145">C++ Redistributable for VS2015</a> to be installed.
 					<ul>
-						<li><a href="binaries/jasm_1.21_win64.7z">jAsm 1.21 for 64-bit Windows</a></li>
+						<li><a href="binaries/jasm_1.22_win64.7z">jAsm 1.22 for 64-bit Windows</a></li>
 					</ul>
 				</p>
 				<h1>The Source</h1>

          
@@ 97,6 97,14 @@ 
 				<p>
 					<ul>
 						<li>
+							1.22
+							<ul>
+								<li>Symbol difference dump added at debug verbosity when an oscillating state occurs. Also the generation pass is run to give more error information if that happens.</li>
+								<li>Removed CodeBlocks project in favor of KDevelop.</li>
+								<li>Migrated from Git to Mercurial.</li>
+							</ul>
+						</li>
+						<li>
 							1.21
 							<ul>
 								<li>Fixed broken instruction data labels on Z80.</li>