beebasm

changeset 10:ff3105395e34 v0.05

BeebASM v0.05 - first released Jan 10 2008 10:55:42
author Samwise <samwise@bagshot-row.org>
date Sun May 02 12:15:51 2010 +0100
parents eff10c20561e
children ba36b411d841
files about.txt beebasm.exe demo.ssd src/Makefile src/Makefile.inc src/commands.cpp src/globaldata.cpp src/globaldata.h src/lineparser.cpp src/lineparser.h src/main.cpp src/objectcode.cpp src/objectcode.h src/sourcefile.cpp
diffstat 14 files changed, 291 insertions(+), 100 deletions(-) [+]
line diff
     1.1 --- a/about.txt	Sun May 02 12:12:52 2010 +0100
     1.2 +++ b/about.txt	Sun May 02 12:15:51 2010 +0100
     1.3 @@ -1,6 +1,6 @@
     1.4  *******************************************************************************
     1.5  *                                                                             *
     1.6 -*                               BeebAsm V0.04                                 *
     1.7 +*                               BeebAsm V0.05                                 *
     1.8  *                                                                             *
     1.9  *             A portable 6502 assembler with BBC Micro style syntax           *
    1.10  *                                                                             *
    1.11 @@ -156,7 +156,8 @@
    1.12  
    1.13  -d
    1.14  
    1.15 -Dumps all symbols in Swift-compatible format after assembly
    1.16 +Dumps all global symbols in Swift-compatible format after assembly.
    1.17 +This is used internally by Swift, and is just documented for completeness.
    1.18  
    1.19  
    1.20  
    1.21 @@ -243,93 +244,118 @@
    1.22  
    1.23  ORG <addr>
    1.24  
    1.25 -Set the address to be assembled from.  This can be changed multiple times
    1.26 -during a source file if you wish (for example) to assemble two separate blocks
    1.27 -of code at different addresses, but share the labels between both blocks.
    1.28 -This is exactly equivalent to BBC BASIC's 'P%=<addr>'.
    1.29 +  Set the address to be assembled from.  This can be changed multiple times
    1.30 +  during a source file if you wish (for example) to assemble two separate
    1.31 +  blocks of code at different addresses, but share the labels between both
    1.32 +  blocks.  This is exactly equivalent to BBC BASIC's 'P%=<addr>'.
    1.33  
    1.34  
    1.35  SKIP <bytes>
    1.36  
    1.37 -Moves the address pointer on by the specified number of bytes.  Use this to
    1.38 -reserve a space of a fixed size in the code.
    1.39 +  Moves the address pointer on by the specified number of bytes.  Use this to
    1.40 +  reserve a space of a fixed size in the code.
    1.41  
    1.42  
    1.43  SKIPTO <addr>
    1.44  
    1.45 -Moves the address pointer to the specified address.  An error is generated if
    1.46 -this address is behind the current address pointer.
    1.47 +  Moves the address pointer to the specified address.  An error is generated
    1.48 +  if this address is behind the current address pointer.
    1.49  
    1.50  
    1.51  ALIGN <alignment>
    1.52  
    1.53 -Used to align the address pointer to the next boundary, e.g. use ALIGN &100 to
    1.54 -move to the next page (useful perhaps for positioning a table at a page
    1.55 -boundary so that index accesses don't incur a "page crossed" penalty.
    1.56 +  Used to align the address pointer to the next boundary, e.g. use ALIGN &100
    1.57 +  to move to the next page (useful perhaps for positioning a table at a page
    1.58 +  boundary so that index accesses don't incur a "page crossed" penalty.
    1.59  
    1.60  
    1.61  INCLUDE "filename"
    1.62  
    1.63 -Includes the specified source file in the code at this point.
    1.64 +  Includes the specified source file in the code at this point.
    1.65  
    1.66  
    1.67  INCBIN "filename"
    1.68  
    1.69 -Includes the specified binary file in the object code at this point.
    1.70 +  Includes the specified binary file in the object code at this point.
    1.71  
    1.72  
    1.73  EQUB a [, b, c, ...]
    1.74  
    1.75 -Insert the specified byte(s) into the code.  Note, unlike BBC BASIC, that a
    1.76 -comma-separated sequence can be inserted.
    1.77 +  Insert the specified byte(s) into the code.  Note, unlike BBC BASIC, that a
    1.78 +  comma-separated sequence can be inserted.
    1.79  
    1.80  
    1.81  EQUW a [, b, c, ...]
    1.82  
    1.83 -Insert the specified 16-bit word(s) into the code.
    1.84 +  Insert the specified 16-bit word(s) into the code.
    1.85  
    1.86  
    1.87  EQUS "string" [, "string", byte, ...]
    1.88  
    1.89 -Inserts the specified string into the code.  Note that this can take a comma-
    1.90 -separated list of parameters which may also include bytes.  So, to zero-
    1.91 -terminate a string, you can write:
    1.92 +  Inserts the specified string into the code.  Note that this can take a comma-
    1.93 +  separated list of parameters which may also include bytes.  So, to zero-
    1.94 +  terminate a string, you can write:
    1.95  
    1.96              EQUS "My string", 0
    1.97  
    1.98 -In fact, under the surface, there is no difference between EQUS and EQUB,
    1.99 -which is also able to take strings!
   1.100 +  In fact, under the surface, there is no difference between EQUS and EQUB,
   1.101 +  which is also able to take strings!
   1.102 +
   1.103 +
   1.104 +MAPCHAR <ascii code>, <remapped code>
   1.105 +MAPCHAR <start ascii code>, <end ascii code>, <remapped code>
   1.106 +
   1.107 +  By default, when EQUS "string" is assembled, the ASCII codes of each
   1.108 +  character are written into the object code.  MAPCHAR allows you to specify
   1.109 +  which value should be written to the object code for each character.
   1.110 +  Suppose you have a font which contains the following symbols - space,
   1.111 +  followed by A-Z, followed by digits 0-9, followed by .,!?-'
   1.112 +
   1.113 +  You could specify this with MAPCHAR as follows:
   1.114 +
   1.115 +             MAPCHAR ' ', 0
   1.116 +             MAPCHAR 'A','Z', 1
   1.117 +             MAPCHAR '0','9', 27
   1.118 +             MAPCHAR '.', 37
   1.119 +             MAPCHAR ',', 38
   1.120 +             MAPCHAR '!', 39
   1.121 +             MAPCHAR '?', 40
   1.122 +             MAPCHAR '-', 41
   1.123 +             MAPCHAR ''', 42
   1.124 +
   1.125 +  Now, when writing strings with EQUS, these codes will be written out instead
   1.126 +  of the default ASCII codes.
   1.127  
   1.128  
   1.129  GUARD <addr>
   1.130  
   1.131 -Puts a 'guard' on the specified address which will cause an error if you
   1.132 -attempt to assemble code over this address.
   1.133 +  Puts a 'guard' on the specified address which will cause an error if you
   1.134 +  attempt to assemble code over this address.
   1.135  
   1.136  
   1.137  CLEAR <start>, <end>
   1.138  
   1.139 -Clears all guards between the <start> and <end> addresses specified.  This can
   1.140 -also be used to reset a section of memory which has had code assembled in it
   1.141 -previously.  BeebAsm will complain if you attempt to assemble code over
   1.142 -previously assembled code at the same address without having saved the previous
   1.143 -code.
   1.144 +  Clears all guards between the <start> and <end> addresses specified.  This
   1.145 +  can also be used to reset a section of memory which has had code assembled
   1.146 +  in it previously.  BeebAsm will complain if you attempt to assemble code
   1.147 +  over previously assembled code at the same address without having CLEARed
   1.148 +  it first.
   1.149  
   1.150  
   1.151  SAVE "filename", start, end [, exec]
   1.152  
   1.153 -Saves out object code to either a DFS disc image (if one has been specified),
   1.154 -or to the current directory as a standalone file.  'exec' specifies the
   1.155 -execution address of the file when saved to a disc image.  A source file must
   1.156 -have at least one SAVE statement in it, otherwise nothing will be output.
   1.157 -BeebAsm will warn if this is the case.
   1.158 +  Saves out object code to either a DFS disc image (if one has been
   1.159 +  specified), or to the current directory as a standalone file.  'exec'
   1.160 +  specifies the execution address of the file when saved to a disc image.  A
   1.161 +  source file must have at least one SAVE statement in it, otherwise nothing
   1.162 +  will be output.  BeebAsm will warn if this is the case.
   1.163  
   1.164  
   1.165  PRINT
   1.166  
   1.167 -Displays some text.  PRINT takes a comma-separated list of strings or values.
   1.168 -To print a value in hex, prefix the expression with a '~' character.
   1.169 -Examples:
   1.170 +  Displays some text.  PRINT takes a comma-separated list of strings or values.
   1.171 +  To print a value in hex, prefix the expression with a '~' character.
   1.172 +  Examples:
   1.173  
   1.174              PRINT "Value of label 'start' =", ~start
   1.175              PRINT "numdots =", numdots, "dottable size =", dotend-dotstart
   1.176 @@ -337,10 +363,10 @@
   1.177  
   1.178  FOR <var>, start, end [, step] ... NEXT
   1.179  
   1.180 -I wanted this to have exactly the same syntax as BASIC, but I couldn't without
   1.181 -rewriting my expression parser, so we're stuck with this for now.
   1.182 +  I wanted this to have exactly the same syntax as BASIC, but I couldn't
   1.183 +  without rewriting my expression parser, so we're stuck with this for now.
   1.184  
   1.185 -It works exactly like BASIC's FOR...NEXT.  For example:
   1.186 +  It works exactly like BASIC's FOR...NEXT.  For example:
   1.187  
   1.188              FOR n, 0, 10, 2    ; loop with n = 0, 2, 4, 6, 8, 10
   1.189                PRINT n
   1.190 @@ -348,11 +374,12 @@
   1.191                LDA #n:STA &901+n
   1.192              NEXT
   1.193  
   1.194 -The variable n only exists for the scope of the FOR...NEXT loop.
   1.195 -Also, any labels or variables defined within the loop are only visible within
   1.196 -it.  However, unlike BBC BASIC, forward references to labels inside the loop
   1.197 -will work properly, so, for example, this little multiply routine is perfectly
   1.198 -ok:
   1.199 +  The variable n only exists for the scope of the FOR...NEXT loop.
   1.200 +  Also, any labels or variables defined within the loop are only visible within
   1.201 +  it.  However, unlike BBC BASIC, forward references to labels inside the loop
   1.202 +  will work properly, so, for example, this little multiply routine is
   1.203 +  perfectly ok:
   1.204 +  
   1.205              .multiply
   1.206              \\ multiplies A*X, puts result in product/product+1
   1.207              CPX #0:BEQ zero
   1.208 @@ -369,11 +396,12 @@
   1.209  
   1.210  IF...ELSE...ENDIF
   1.211  
   1.212 -Use to assemble conditionally.  Like anything else in BeebAsm, these statements
   1.213 -can be placed on one line, separated by colons, but even if they are, ENDIF
   1.214 -must be present to denote the end of the IF block (unlike BBC BASIC).
   1.215 +  Use to assemble conditionally.  Like anything else in BeebAsm, these
   1.216 +  statements can be placed on one line, separated by colons, but even if they
   1.217 +  are, ENDIF must be present to denote the end of the IF block (unlike BBC
   1.218 +  BASIC).
   1.219  
   1.220 -Examples of use:
   1.221 +  Examples of use:
   1.222  
   1.223              \\ build a rather strange table
   1.224              FOR n, 0, 9
   1.225 @@ -390,36 +418,37 @@
   1.226  
   1.227  { ... }
   1.228  
   1.229 -Curly braces can be used to specify a block of code in which all symbols and
   1.230 -labels defined will exist only within this block.  Effectively, this is a
   1.231 -mechanism for providing 'local labels' without the slightly cumbersome syntax
   1.232 -demanded by some other assemblers.  These can be nested.  Any symbols defined
   1.233 -within a block will override any of the same name outside of the block, exactly
   1.234 -like C/C++ - not sure if I like this behaviour, but for now it will stay.
   1.235 +  Curly braces can be used to specify a block of code in which all symbols and
   1.236 +  labels defined will exist only within this block.  Effectively, this is a
   1.237 +  mechanism for providing 'local labels' without the slightly cumbersome syntax
   1.238 +  demanded by some other assemblers.  These can be nested.  Any symbols defined
   1.239 +  within a block will override any of the same name outside of the block,
   1.240 +  exactly like C/C++ - not sure if I like this behaviour, but for now it will
   1.241 +  stay.
   1.242  
   1.243 -Example of use:
   1.244 +  Example of use:
   1.245  
   1.246 -    .initialise
   1.247 -    {
   1.248 -        LDY #31
   1.249 -        LDA #0
   1.250 -    .loop                ; label visible only within the braces
   1.251 -        STA buffer,Y
   1.252 -        DEY
   1.253 -        BPL loop
   1.254 -        RTS
   1.255 -    }
   1.256 -
   1.257 -    .copy
   1.258 -    {
   1.259 -        LDY #31
   1.260 -    .loop                ; perfectly ok to define .loop again in a new block
   1.261 -        LDA source,Y
   1.262 -        STA dest,Y
   1.263 -        DEY
   1.264 -        BPL loop
   1.265 -        RTS
   1.266 -    }
   1.267 +      .initialise
   1.268 +      {
   1.269 +          LDY #31
   1.270 +          LDA #0
   1.271 +      .loop              ; label visible only within the braces
   1.272 +          STA buffer,Y
   1.273 +          DEY
   1.274 +          BPL loop
   1.275 +          RTS
   1.276 +      }
   1.277 +  
   1.278 +      .copy
   1.279 +      {
   1.280 +          LDY #31
   1.281 +      .loop              ; perfectly ok to define .loop again in a new block
   1.282 +          LDA source,Y
   1.283 +          STA dest,Y
   1.284 +          DEY
   1.285 +          BPL loop
   1.286 +          RTS
   1.287 +      }
   1.288  
   1.289  
   1.290  
   1.291 @@ -523,10 +552,16 @@
   1.292  
   1.293  9. VERSION HISTORY
   1.294  
   1.295 +09/01/2008  0.05  Added MAPCHAR.  Fixed SKIPTO (see, told you I was doing
   1.296 +                  it quickly!).  Enforce '%' as an end-of-symbol
   1.297 +                  character.  Fixed bug in overlayed assembly.  Warns if
   1.298 +                  there is no SAVE command in the source file.
   1.299  06/01/2008  0.04  Added braces for scoping labels.  Added INCBIN, SKIPTO.
   1.300                    Added some missing functions (NOT, LOG, LN, EXP).
   1.301                    Tightened up error checking on assembling out of range
   1.302 -                  addresses (negative, or greater than &FFFF).
   1.303 +                  addresses (negative, or greater than &FFFF).  Now
   1.304 +                  distinguishes internally between labels and other
   1.305 +                  symbols.
   1.306  05/01/2008  0.03  Added symbol dump for use with Swift.
   1.307  20/12/2007  0.02  Fixed small bug which withheld filename and line number
   1.308                    display in error messages.
     2.1 Binary file beebasm.exe has changed
     3.1 Binary file demo.ssd has changed
     4.1 --- a/src/Makefile	Sun May 02 12:12:52 2010 +0100
     4.2 +++ b/src/Makefile	Sun May 02 12:15:51 2010 +0100
     4.3 @@ -35,7 +35,7 @@
     4.4  
     4.5  # Parameters to the executable
     4.6  
     4.7 -PARAMS			:=		-i ..\demo.asm -do ..\demo.ssd -boot Code -d
     4.8 +PARAMS			:=		-i ..\demo.asm -do ..\demo.ssd -boot Code
     4.9  
    4.10  
    4.11  
     5.1 --- a/src/Makefile.inc	Sun May 02 12:12:52 2010 +0100
     5.2 +++ b/src/Makefile.inc	Sun May 02 12:15:51 2010 +0100
     5.3 @@ -57,9 +57,9 @@
     5.4  CC				:=		C:/MinGW/bin/gcc
     5.5  CXX				:=		C:/MinGW/bin/gcc
     5.6  LD				:=		C:/MinGW/bin/gcc
     5.7 -MKDIR			:=		C:/Utilities/mkdir -p
     5.8 -RM				:=		C:/Utilities/rm -f
     5.9 -ECHO			:=		@@C:/Utilities/echo -e
    5.10 +MKDIR			:=		C:/Cygwin/bin/mkdir -p
    5.11 +RM				:=		C:/Cygwin/bin/rm -f
    5.12 +ECHO			:=		@@C:/Cygwin/bin/echo -e
    5.13  
    5.14  
    5.15  # Declare default number of CPUs
     6.1 --- a/src/commands.cpp	Sun May 02 12:12:52 2010 +0100
     6.2 +++ b/src/commands.cpp	Sun May 02 12:15:51 2010 +0100
     6.3 @@ -41,13 +41,14 @@
     6.4  	{ "ELSE",		&LineParser::HandleElse },
     6.5  	{ "ENDIF",		&LineParser::HandleEndif },
     6.6  	{ "ALIGN",		&LineParser::HandleAlign },
     6.7 +	{ "SKIPTO",		&LineParser::HandleSkipTo },
     6.8  	{ "SKIP",		&LineParser::HandleSkip },
     6.9  	{ "GUARD",		&LineParser::HandleGuard },
    6.10  	{ "CLEAR",		&LineParser::HandleClear },
    6.11 -	{ "SKIPTO",		&LineParser::HandleSkipTo },
    6.12  	{ "INCBIN",		&LineParser::HandleIncBin },
    6.13  	{ "{",			&LineParser::HandleOpenBrace },
    6.14 -	{ "}",			&LineParser::HandleCloseBrace }
    6.15 +	{ "}",			&LineParser::HandleCloseBrace },
    6.16 +	{ "MAPCHAR",	&LineParser::HandleMapChar }
    6.17  };
    6.18  
    6.19  
    6.20 @@ -269,6 +270,84 @@
    6.21  
    6.22  /*************************************************************************************************/
    6.23  /**
    6.24 +	LineParser::HandleMapChar()
    6.25 +*/
    6.26 +/*************************************************************************************************/
    6.27 +void LineParser::HandleMapChar()
    6.28 +{
    6.29 +	// get parameters - either 2 or 3
    6.30 +
    6.31 +	int param3 = -1;
    6.32 +	int param1 = EvaluateExpressionAsInt();
    6.33 +
    6.34 +	if ( m_line[ m_column ] != ',' )
    6.35 +	{
    6.36 +		// did not find a comma
    6.37 +		throw AsmException_SyntaxError_InvalidCharacter( m_line, m_column );
    6.38 +	}
    6.39 +
    6.40 +	m_column++;
    6.41 +
    6.42 +	int param2 = EvaluateExpressionAsInt();
    6.43 +
    6.44 +	if ( m_line[ m_column ] == ',' )
    6.45 +	{
    6.46 +		m_column++;
    6.47 +
    6.48 +		param3 = EvaluateExpressionAsInt();
    6.49 +	}
    6.50 +
    6.51 +	if ( m_line[ m_column ] == ',' )
    6.52 +	{
    6.53 +		// Unexpected comma (remembering that an expression can validly end with a comma)
    6.54 +		throw AsmException_SyntaxError_UnexpectedComma( m_line, m_column );
    6.55 +	}
    6.56 +
    6.57 +	// range checks
    6.58 +
    6.59 +	if ( param1 < 32 || param1 > 126 )
    6.60 +	{
    6.61 +		throw AsmException_SyntaxError_OutOfRange( m_line, m_column );
    6.62 +	}
    6.63 +
    6.64 +	if ( param3 == -1 )
    6.65 +	{
    6.66 +		// two parameters
    6.67 +
    6.68 +		if ( param2 < 0 || param2 > 255 )
    6.69 +		{
    6.70 +			throw AsmException_SyntaxError_OutOfRange( m_line, m_column );
    6.71 +		}
    6.72 +
    6.73 +		// do single character remapping
    6.74 +		ObjectCode::Instance().SetMapping( param1, param2 );
    6.75 +	}
    6.76 +	else
    6.77 +	{
    6.78 +		// three parameters
    6.79 +
    6.80 +		if ( param2 < 32 || param2 > 126 || param2 < param1 )
    6.81 +		{
    6.82 +			throw AsmException_SyntaxError_OutOfRange( m_line, m_column );
    6.83 +		}
    6.84 +
    6.85 +		if ( param3 < 0 || param3 > 255 )
    6.86 +		{
    6.87 +			throw AsmException_SyntaxError_OutOfRange( m_line, m_column );
    6.88 +		}
    6.89 +
    6.90 +		// remap a block
    6.91 +		for ( int i = param1; i <= param2; i++ )
    6.92 +		{
    6.93 +			ObjectCode::Instance().SetMapping( i, param3 + i - param1 );
    6.94 +		}
    6.95 +	}
    6.96 +}
    6.97 +
    6.98 +
    6.99 +
   6.100 +/*************************************************************************************************/
   6.101 +/**
   6.102  	LineParser::HandleAlign()
   6.103  */
   6.104  /*************************************************************************************************/
   6.105 @@ -525,11 +604,13 @@
   6.106  
   6.107  				for ( size_t i = 0; i < equs.length(); i++ )
   6.108  				{
   6.109 +					int mappedchar = ObjectCode::Instance().GetMapping( equs[ i ] );
   6.110 +
   6.111  					if ( GlobalData::Instance().ShouldOutputAsm() )
   6.112  					{
   6.113  						if ( i < 3 )
   6.114  						{
   6.115 -							cout << setw(2) << static_cast< int >( equs[ i ] ) << " ";
   6.116 +							cout << setw(2) << mappedchar << " ";
   6.117  						}
   6.118  						else if ( i == 3 )
   6.119  						{
   6.120 @@ -539,7 +620,8 @@
   6.121  
   6.122  					try
   6.123  					{
   6.124 -						ObjectCode::Instance().PutByte( equs[ i ] );
   6.125 +						// remap character from string as per character mapping table
   6.126 +						ObjectCode::Instance().PutByte( mappedchar );
   6.127  					}
   6.128  					catch ( AsmException_AssembleError& e )
   6.129  					{
   6.130 @@ -849,6 +931,8 @@
   6.131  
   6.132  			objFile.close();
   6.133  		}
   6.134 +
   6.135 +		GlobalData::Instance().SetSaved();
   6.136  	}
   6.137  }
   6.138  
     7.1 --- a/src/globaldata.cpp	Sun May 02 12:12:52 2010 +0100
     7.2 +++ b/src/globaldata.cpp	Sun May 02 12:15:51 2010 +0100
     7.3 @@ -54,7 +54,8 @@
     7.4  	:	m_pBootFile( NULL ),
     7.5  		m_bVerbose( false ),
     7.6  		m_bUseDiscImage( false ),
     7.7 -		m_pDiscImage( NULL )
     7.8 +		m_pDiscImage( NULL ),
     7.9 +		m_bSaved( false )
    7.10  {
    7.11  }
    7.12  
     8.1 --- a/src/globaldata.h	Sun May 02 12:12:52 2010 +0100
     8.2 +++ b/src/globaldata.h	Sun May 02 12:15:51 2010 +0100
     8.3 @@ -27,6 +27,7 @@
     8.4  	inline void SetUseDiscImage( bool b )		{ m_bUseDiscImage = b; }
     8.5  	inline void SetDiscImage( DiscImage* d )	{ m_pDiscImage = d; }
     8.6  	inline void ResetForId()					{ m_forId = 0; }
     8.7 +	inline void SetSaved()						{ m_bSaved = true; }
     8.8  
     8.9  	inline int GetPass() const					{ return m_pass; }
    8.10  	inline bool IsFirstPass() const				{ return ( m_pass == 0 ); }
    8.11 @@ -36,6 +37,7 @@
    8.12  	inline bool UsesDiscImage() const			{ return m_bUseDiscImage; }
    8.13  	inline DiscImage* GetDiscImage() const		{ return m_pDiscImage; }
    8.14  	inline int GetNextForId()					{ return m_forId++; }
    8.15 +	inline bool IsSaved() const					{ return m_bSaved; }
    8.16  
    8.17  
    8.18  private:
    8.19 @@ -52,6 +54,7 @@
    8.20  	DiscImage*					m_pDiscImage;
    8.21  	int							m_forId;
    8.22  	int							m_randomSeed;
    8.23 +	bool						m_bSaved;
    8.24  };
    8.25  
    8.26  
     9.1 --- a/src/lineparser.cpp	Sun May 02 12:12:52 2010 +0100
     9.2 +++ b/src/lineparser.cpp	Sun May 02 12:15:51 2010 +0100
     9.3 @@ -343,10 +343,11 @@
     9.4  	{
     9.5  		symbolName += m_line[ m_column++ ];
     9.6  
     9.7 -	} while ( isalpha( m_line[ m_column ] ) ||
     9.8 -			  isdigit( m_line[ m_column ] ) ||
     9.9 -			  m_line[ m_column ] == '_' ||
    9.10 -			  m_line[ m_column ] == '%' );
    9.11 +	} while ( ( isalpha( m_line[ m_column ] ) ||
    9.12 +				isdigit( m_line[ m_column ] ) ||
    9.13 +				m_line[ m_column ] == '_' ||
    9.14 +				m_line[ m_column ] == '%' ) &&
    9.15 +				m_line[ m_column - 1 ] != '%' );
    9.16  
    9.17  	return symbolName;
    9.18  }
    10.1 --- a/src/lineparser.h	Sun May 02 12:12:52 2010 +0100
    10.2 +++ b/src/lineparser.h	Sun May 02 12:15:51 2010 +0100
    10.3 @@ -124,6 +124,7 @@
    10.4  	void			HandleSkipTo();
    10.5  	void			HandleGuard();
    10.6  	void			HandleClear();
    10.7 +	void			HandleMapChar();
    10.8  
    10.9  	// expression evaluating methods
   10.10  
    11.1 --- a/src/main.cpp	Sun May 02 12:12:52 2010 +0100
    11.2 +++ b/src/main.cpp	Sun May 02 12:15:51 2010 +0100
    11.3 @@ -23,7 +23,7 @@
    11.4  using namespace std;
    11.5  
    11.6  
    11.7 -#define VERSION "0.03"
    11.8 +#define VERSION "0.05"
    11.9  
   11.10  
   11.11  /*************************************************************************************************/
   11.12 @@ -207,6 +207,11 @@
   11.13  		SymbolTable::Instance().Dump();
   11.14  	}
   11.15  
   11.16 +	if ( !GlobalData::Instance().IsSaved() )
   11.17 +	{
   11.18 +		cerr << "warning: no SAVE command in source file." << endl;
   11.19 +	}
   11.20 +
   11.21  	ObjectCode::Destroy();
   11.22  	SymbolTable::Destroy();
   11.23  	GlobalData::Destroy();
    12.1 --- a/src/objectcode.cpp	Sun May 02 12:12:52 2010 +0100
    12.2 +++ b/src/objectcode.cpp	Sun May 02 12:15:51 2010 +0100
    12.3 @@ -65,6 +65,13 @@
    12.4  {
    12.5  	memset( m_aMemory, 0, sizeof m_aMemory );
    12.6  	memset( m_aFlags, 0, sizeof m_aFlags );
    12.7 +
    12.8 +	// initialise ascii mapping table
    12.9 +
   12.10 +	for ( int i = 0; i < 96; i++ )
   12.11 +	{
   12.12 +		m_aMapChar[ i ] = i + 32;
   12.13 +	}
   12.14  }
   12.15  
   12.16  
   12.17 @@ -86,7 +93,7 @@
   12.18  /**
   12.19  	ObjectCode::PutByte()
   12.20  
   12.21 -	Puts one byte to memory image
   12.22 +	Puts one byte to memory image, never doing pass consistency checks
   12.23  */
   12.24  /*************************************************************************************************/
   12.25  void ObjectCode::PutByte( unsigned int byte )
   12.26 @@ -135,6 +142,8 @@
   12.27  	assert( opcode < 0x100 );
   12.28  
   12.29  	if ( GlobalData::Instance().IsSecondPass() &&
   12.30 +		 ( m_aFlags[ m_PC ] & CHECK ) &&
   12.31 +		 !( m_aFlags[ m_PC ] & DONT_CHECK ) &&
   12.32  		 m_aMemory[ m_PC ] != opcode )
   12.33  	{
   12.34  		throw AsmException_AssembleError_InconsistentCode();
   12.35 @@ -150,7 +159,7 @@
   12.36  		throw AsmException_AssembleError_Overlap();
   12.37  	}
   12.38  
   12.39 -	m_aFlags[ m_PC ] |= USED;
   12.40 +	m_aFlags[ m_PC ] |= ( USED | CHECK );
   12.41  	m_aMemory[ m_PC++ ] = opcode;
   12.42  
   12.43  	SymbolTable::Instance().ChangeSymbol( "P%", m_PC );
   12.44 @@ -177,6 +186,8 @@
   12.45  	assert( val < 0x100 );
   12.46  
   12.47  	if ( GlobalData::Instance().IsSecondPass() &&
   12.48 +		 ( m_aFlags[ m_PC ] & CHECK ) &&
   12.49 +		 !( m_aFlags[ m_PC ] & DONT_CHECK ) &&
   12.50  		 m_aMemory[ m_PC ] != opcode )
   12.51  	{
   12.52  		throw AsmException_AssembleError_InconsistentCode();
   12.53 @@ -194,7 +205,7 @@
   12.54  		throw AsmException_AssembleError_Overlap();
   12.55  	}
   12.56  
   12.57 -	m_aFlags[ m_PC ] |= USED;
   12.58 +	m_aFlags[ m_PC ] |= ( USED | CHECK );
   12.59  	m_aMemory[ m_PC++ ] = opcode;
   12.60  	m_aFlags[ m_PC ] |= USED;
   12.61  	m_aMemory[ m_PC++ ] = val;
   12.62 @@ -223,6 +234,8 @@
   12.63  	assert( addr < 0x10000 );
   12.64  
   12.65  	if ( GlobalData::Instance().IsSecondPass() &&
   12.66 +		 ( m_aFlags[ m_PC ] & CHECK ) &&
   12.67 +		 !( m_aFlags[ m_PC ] & DONT_CHECK ) &&
   12.68  		 m_aMemory[ m_PC ] != opcode )
   12.69  	{
   12.70  		throw AsmException_AssembleError_InconsistentCode();
   12.71 @@ -242,7 +255,7 @@
   12.72  		throw AsmException_AssembleError_Overlap();
   12.73  	}
   12.74  
   12.75 -	m_aFlags[ m_PC ] |= USED;
   12.76 +	m_aFlags[ m_PC ] |= ( USED | CHECK );
   12.77  	m_aMemory[ m_PC++ ] = opcode;
   12.78  	m_aFlags[ m_PC ] |= USED;
   12.79  	m_aMemory[ m_PC++ ] = addr & 0xFF;
   12.80 @@ -280,9 +293,22 @@
   12.81  
   12.82  	if ( bAll )
   12.83  	{
   12.84 +		// via CLEAR command
   12.85 +		// as soon as we force a block to be cleared, we can no longer do inconsistency checks on
   12.86 +		// the object code, so we flag the whole block as DONT_CHECK
   12.87  		memset( m_aMemory + start, 0, end - start );
   12.88 +		memset( m_aFlags + start, DONT_CHECK, end - start );
   12.89  	}
   12.90 -	memset( m_aFlags + start, 0, end - start );
   12.91 +	else
   12.92 +	{
   12.93 +		// between first and second pass
   12.94 +		// we preserve the memory image and the CHECK flags so that we can test for inconsistencies
   12.95 +		// in the assembled code between first and second passes
   12.96 +		for ( unsigned char* i = m_aFlags + start; i < m_aFlags + end; i++ )
   12.97 +		{
   12.98 +			(*i) &= ( CHECK | DONT_CHECK );
   12.99 +		}
  12.100 +	}
  12.101  }
  12.102  
  12.103  
  12.104 @@ -318,3 +344,31 @@
  12.105  
  12.106  	binfile.close();
  12.107  }
  12.108 +
  12.109 +
  12.110 +
  12.111 +/*************************************************************************************************/
  12.112 +/**
  12.113 +	ObjectCode::SetMapping()
  12.114 +*/
  12.115 +/*************************************************************************************************/
  12.116 +void ObjectCode::SetMapping( int ascii, int mapped )
  12.117 +{
  12.118 +	assert( ascii > 31 && ascii < 127 );
  12.119 +	assert( mapped >= 0 && mapped < 256 );
  12.120 +
  12.121 +	m_aMapChar[ ascii - 32 ] = mapped;
  12.122 +}
  12.123 +
  12.124 +
  12.125 +
  12.126 +/*************************************************************************************************/
  12.127 +/**
  12.128 +	ObjectCode::SetMapping()
  12.129 +*/
  12.130 +/*************************************************************************************************/
  12.131 +int ObjectCode::GetMapping( int ascii ) const
  12.132 +{
  12.133 +	assert( ascii > 31 && ascii < 127 );
  12.134 +	return m_aMapChar[ ascii - 32 ];
  12.135 +}
    13.1 --- a/src/objectcode.h	Sun May 02 12:12:52 2010 +0100
    13.2 +++ b/src/objectcode.h	Sun May 02 12:15:51 2010 +0100
    13.3 @@ -33,13 +33,18 @@
    13.4  	void SetGuard( int i );
    13.5  	void Clear( int start, int end, bool bAll = true );
    13.6  
    13.7 +	void SetMapping( int ascii, int mapped );
    13.8 +	int GetMapping( int ascii ) const;
    13.9 +
   13.10  
   13.11  private:
   13.12  
   13.13  	enum FLAGS
   13.14  	{
   13.15  		USED  = (1 << 0),
   13.16 -		GUARD = (1 << 1)
   13.17 +		GUARD = (1 << 1),
   13.18 +		CHECK = (1 << 2),
   13.19 +		DONT_CHECK = (1 << 3)
   13.20  	};
   13.21  
   13.22  
   13.23 @@ -50,6 +55,8 @@
   13.24  	unsigned char				m_aFlags[ 0x10000 ];
   13.25  	int							m_PC;
   13.26  
   13.27 +	unsigned char				m_aMapChar[ 96 ];
   13.28 +
   13.29  	static ObjectCode*			m_gInstance;
   13.30  };
   13.31  
    14.1 --- a/src/sourcefile.cpp	Sun May 02 12:12:52 2010 +0100
    14.2 +++ b/src/sourcefile.cpp	Sun May 02 12:15:51 2010 +0100
    14.3 @@ -163,7 +163,7 @@
    14.4  
    14.5  	if ( GlobalData::Instance().IsFirstPass() )
    14.6  	{
    14.7 -		cerr << "Processed file '" << m_pFilename << "' ok" << endl << endl;
    14.8 +		cerr << "Processed file '" << m_pFilename << "' ok" << endl;
    14.9  	}
   14.10  }
   14.11