OpenCores
URL https://opencores.org/ocsvn/ssbcc/ssbcc/trunk

Subversion Repositories ssbcc

[/] [ssbcc/] [trunk/] [core/] [9x8/] [asmDef_9x8.py] - Blame information for rev 13

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 sinclairrf
################################################################################
2
#
3 12 sinclairrf
# Copyright 2012-2015, Sinclair R.F., Inc.
4 2 sinclairrf
#
5
# Assembly language definitions for SSBCC 9x8.
6
#
7
################################################################################
8
 
9
import copy
10 3 sinclairrf
import os
11
import re
12 2 sinclairrf
import string
13 3 sinclairrf
import sys
14
import types
15 2 sinclairrf
 
16
import asmDef
17
 
18
class asmDef_9x8:
19
  """
20
  Class for core-specific opcodes, macros, etc. for core/9x8.
21
  """
22
 
23
  ################################################################################
24
  #
25
  # External interface to the directives.
26
  #
27
  ################################################################################
28
 
29
  def IsDirective(self,name):
30
    """
31
    Indicate whether or not the string "name" is a directive.
32
    """
33
    return name in self.directives['list'];
34
 
35
  ################################################################################
36
  #
37
  # Record symbols
38
  #
39
  ################################################################################
40
 
41
  def AddSymbol(self,name,stype,body=None):
42
    """
43
    Add the named global symbol to the list of symbols including its mandatory
44
    type and an optional body.\n
45 9 sinclairrf
    Note:  Symbols include memory names, variables, constants, defines,
46
           functions, parameters, inports, outports, ...
47 2 sinclairrf
    """
48
    if self.IsSymbol(name):
49
      raise Exception('Program Bug -- name "%s" already exists is symbols' % name);
50
    self.symbols['list'].append(name);
51
    self.symbols['type'].append(stype);
52
    self.symbols['body'].append(body);
53
 
54
  def IsSymbol(self,name):
55
    return name in self.symbols['list'];
56
 
57
  def SymbolDict(self):
58
    """
59
    Return a dict object usable by the eval function with the currently defines
60 13 sinclairrf
    symbols for constants, variables, memory and I/O signal lengths, variable
61
    lengths, and stack lengths.
62 2 sinclairrf
    """
63
    t = dict();
64 13 sinclairrf
    # Add constants and variable locations.
65 2 sinclairrf
    for ixSymbol in range(len(self.symbols['list'])):
66
      name = self.symbols['list'][ixSymbol];
67
      stype = self.symbols['type'][ixSymbol];
68
      if stype == 'constant':
69
        t[name] = self.symbols['body'][ixSymbol][0];
70
      elif stype == 'variable':
71
        t[name] = self.symbols['body'][ixSymbol]['start'];
72 13 sinclairrf
    # Construct and add dictionary of sizes.
73 2 sinclairrf
    sizes=dict();
74 13 sinclairrf
    def AddSize(name,value):
75
      if name in sizes:
76
        raise Exception('Program Bug:  Symbol "%s" multiply defined');
77
      sizes[name] = value;
78 2 sinclairrf
    for name in self.memoryLength:
79 13 sinclairrf
      AddSize(name,self.memoryLength[name]);
80
    for ix in [ix for ix in range(len(self.symbols['type'])) if self.symbols['type'][ix] == 'variable']:
81
      AddSize(self.symbols['list'][ix],len(self.symbols['body'][ix]['value']));
82 2 sinclairrf
    for name in self.stackLength:
83 13 sinclairrf
      AddSize(name,self.stackLength[name]);
84 2 sinclairrf
    t['size'] = sizes;
85 13 sinclairrf
    # That's all.
86 2 sinclairrf
    return t;
87
 
88
  ################################################################################
89
  #
90
  # Configure the class for identifying and processing macros.
91
  #
92
  ################################################################################
93
 
94 11 sinclairrf
  def AddMacro(self,name,macroLength,args,doc=None):
95 2 sinclairrf
    """
96
    Add a macro to the list of recognized macros.
97
      name              string with the name of the macro
98
      macroLength       number of instructions the macro expands to
99
                        Note:  A negative value means that the macro has a
100
                               variable length (see MacroLength below)
101
      args              list of the arguments
102
                        each element of this list is an array of strings specifying the following:
103
                          1.  If the first element is the empty string, then
104
                              there is no default value for the argument,
105
                              otherwise the listed string is the default
106
                              value of the optional argument.
107
                          2.  The remaining elements of the list are the types
108
                              of arguments that can be accepted for the
109
                              required or optional arguments.
110
                        Note:  Only the last list in args is allowed to
111
                               indicate an optional value for that argument.
112 11 sinclairrf
      doc               doc string for the macro
113
 
114 2 sinclairrf
    Also record the allowed number of allowed arguments to the macro.
115
    """
116
    if name in self.macros['list']:
117
      raise Exception('Program Bug -- name "%s" has already been listed as a macro' % name);
118
    self.macros['list'].append(name);
119
    self.macros['length'].append(macroLength);
120
    self.macros['args'].append(args);
121 11 sinclairrf
    self.macros['doc'].append(doc)
122 2 sinclairrf
    # Compute the range of the number of allowed arguments by first counting
123
    # the number of required arguments and then determining whether or not
124
    # there is at most one optional argument.
125
    nRequired = 0;
126
    while (nRequired < len(args)) and (args[nRequired][0] == ''):
127
      nRequired = nRequired + 1;
128
    if nRequired < len(args)-1:
129
      raise Exception('Program Bug -- Only the last macro argument can be optional');
130
    self.macros['nArgs'].append(range(nRequired,len(args)+1));
131
 
132 3 sinclairrf
  def AddMacroSearchPath(self,path):
133
    self.macroSearchPaths.append(path);
134
 
135
  def AddUserMacro(self,macroName,macroSearchPaths=None):
136
    """
137
    Add a user-defined macro by processing the associated Python script.
138
      macroName         name of the macro
139
                        The associated Python script must be named
140
                        <macroName>.py and must be in the project directory, an
141
                        included directory, or must be one of the macros
142
                        provided in "macros" subdirectory of this directory.
143
    """
144
    if not macroSearchPaths:
145
      macroSearchPaths = self.macroSearchPaths;
146
    for testPath in macroSearchPaths:
147
      fullMacro = os.path.join(testPath,'%s.py' % macroName);
148
      if os.path.isfile(fullMacro):
149
        break;
150
    else:
151
      raise asmDef.AsmException('Definition for macro "%s" not found' % macroName);
152
    execfile(fullMacro);
153
    exec('%s(self)' % macroName);
154 11 sinclairrf
    exec('docString = %s.__doc__' % macroName)
155
    if docString and not self.macros['doc'][-1]:
156
      self.macros['doc'][-1] = docString
157 3 sinclairrf
 
158
  def IsBuiltInMacro(self,name):
159
    """
160
    Indicate if the macro is built-in to the assembler or is taken from the
161
    ./macros directory.
162
    """
163
    return name in self.macros['builtIn'];
164
 
165 2 sinclairrf
  def IsMacro(self,name):
166
    """
167
    Indicate whether or not the string "name" is a recognized macro.
168
    """
169
    return name in self.macros['list'];
170
 
171
  def IsSingleMacro(self,name):
172
    """
173
    Indicate whether or not the macro is only one instruction long.
174
    """
175
    if name not in self.macros['list']:
176
      raise Exception('Program Bug -- name "%s" is not a macro' % name);
177
    ix = self.macros['list'].index(name);
178
    return (self.macros['length'][ix] == 1);
179
 
180
  def MacroArgTypes(self,name,ixArg):
181
    """
182
    Return the list of allowed types for the macro name for argument ixArg.
183
    """
184
    if name not in self.macros['list']:
185
      raise Exception('Program Bug -- name "%s" is not a macro' % name);
186
    ix = self.macros['list'].index(name);
187
    return self.macros['args'][ix][ixArg][1:];
188
 
189
  def MacroDefault(self,name,ixArg):
190
    """
191
    Return the default argument for the macro name for argument ixArg.
192
    """
193
    if name not in self.macros['list']:
194
      raise Exception('Program Bug -- name "%s" is not a macro' % name);
195
    ix = self.macros['list'].index(name);
196
    return self.macros['args'][ix][ixArg][0];
197
 
198
  def MacroLength(self,token):
199
    """
200
    Return the length of fixed-length macros or compute and return the length
201 3 sinclairrf
    of variable-length macros.
202 2 sinclairrf
    """
203
    if token['value'] not in self.macros['list']:
204
      raise Exception('Program Bug -- name "%s" is not a macro' % token['value']);
205
    ix = self.macros['list'].index(token['value']);
206
    length = self.macros['length'][ix];
207 3 sinclairrf
    if type(length) == int:
208 2 sinclairrf
      return length;
209 3 sinclairrf
    elif type(length) == types.FunctionType:
210
      return length(self,token['argument']);
211
    else:
212
      raise Exception('Program Bug -- Unrecognized variable length macro "%s"' % token['value']);
213 2 sinclairrf
 
214
  def MacroNumberArgs(self,name):
215
    """
216
    Return the range of the number of allowed arguments to the named macro.
217
    """
218
    if name not in self.macros['list']:
219
      raise Exception('Program bug -- name "%s" is not a macro' % name);
220
    ix = self.macros['list'].index(name);
221
    return self.macros['nArgs'][ix];
222
 
223
  ################################################################################
224
  #
225
  # Configure the class for processing instructions.
226
  #
227
  ################################################################################
228
 
229
  def AddInstruction(self,name,opcode):
230
    """
231
    Add an instruction to the list of recognized instructions.
232
    """
233
    self.instructions['list'].append(name);
234
    self.instructions['opcode'].append(opcode);
235
 
236
  def IsInstruction(self,name):
237
    """
238
    Indicate whether or not the argument is an instruction.
239
    """
240
    return name in self.instructions['list'];
241
 
242
  def InstructionOpcode(self,name):
243
    """
244
    Return the opcode for the specified instruction.
245
    """
246
    if not self.IsInstruction(name):
247
      raise Exception('Program Bug:  "%s" not in instruction list' % name);
248
    ix = self.instructions['list'].index(name);
249
    return self.instructions['opcode'][ix];
250
 
251
  ################################################################################
252
  #
253
  # Register input and output port names and addresses.
254
  #
255
  ################################################################################
256
 
257
  def IsConstant(self,name):
258
    """
259
    Indicate whether or not the named symbol is an inport.
260
    """
261
    if not self.IsSymbol(name):
262
      return False;
263
    ix = self.symbols['list'].index(name);
264
    return self.symbols['type'][ix] == 'constant';
265
 
266
  def IsInport(self,name):
267
    """
268
    Indicate whether or not the named symbol is an inport.
269
    """
270
    if not self.IsSymbol(name):
271
      return False;
272
    ix = self.symbols['list'].index(name);
273
    return self.symbols['type'][ix] == 'inport';
274
 
275
  def IsOutport(self,name):
276
    """
277
    Indicate whether or not the named symbol is an outport.
278
    """
279
    if not self.IsSymbol(name):
280
      return False;
281
    ix = self.symbols['list'].index(name);
282
    return self.symbols['type'][ix] == 'outport';
283
 
284
  def IsOutstrobe(self,name):
285
    """
286
    Indicate whether or not the named symbol is a strobe-only outport.
287
    """
288
    if not self.IsSymbol(name):
289
      return False;
290
    ix = self.symbols['list'].index(name);
291
    return self.symbols['type'][ix] == 'outstrobe';
292
 
293
  def IsParameter(self,name):
294
    """
295
    Indicate whether or not the named symbol is a parameter.
296
    """
297
    if not self.IsSymbol(name):
298
      return False;
299
    ix = self.symbols['list'].index(name);
300
    return self.symbols['type'][ix] == 'parameter';
301
 
302
  def InportAddress(self,name):
303
    """
304
    Return the address of the named inport.
305
    """
306
    if not self.IsInport(name):
307
      raise Exception('Program Bug -- "%s" is not an inport' % name);
308
    ix = self.symbols['list'].index(name);
309
    return self.symbols['body'][ix];
310
 
311
  def OutportAddress(self,name):
312
    """
313
    Return the address of the named outport.
314
    """
315
    if not self.IsOutport(name) and not self.IsOutstrobe(name):
316
      raise Exception('Program Bug -- "%s" is not an outport' % name);
317
    ix = self.symbols['list'].index(name);
318
    return self.symbols['body'][ix];
319
 
320
  def RegisterInport(self,name,address):
321
    """
322
    Add the named inport to the list of recognized symbols and record its
323
    address as the body of the inport.
324
    """
325
    if self.IsSymbol(name):
326
      raise Exception('Program Bug -- repeated symbol name "%s"' % name);
327
    self.AddSymbol(name,'inport',address);
328
 
329
  def RegisterOutport(self,name,address):
330
    """
331
    Add the named outport to the list of recognized symbols and record its
332
    address as the body of the outport.
333
    """
334
    if self.IsSymbol(name):
335
      raise Exception('Program Bug -- repeated symbol name "%s"' % name);
336
    self.AddSymbol(name,'outport',address);
337
 
338
  def RegisterOutstrobe(self,name,address):
339
    """
340
    Add the named outport to the list of recognized symbols and record its
341
    address as the body of the strobe-only outports.
342
    """
343
    if self.IsSymbol(name):
344
      raise Exception('Program Bug -- repeated symbol name "%s"' % name);
345
    self.AddSymbol(name,'outstrobe',address);
346
 
347
  def RegisterParameterName(self,name):
348
    """
349
    Add the named parameter to the list of regognized symbols.\n
350
    Note:  Parameters do not have a body.
351
    """
352
    if self.IsSymbol(name):
353
      raise Exception('Program Bug -- repeated symbol name "%s"' % name);
354
    self.AddSymbol(name,'parameter');
355
 
356
  def RegisterMemoryLength(self,name,length):
357
    """
358
    Record the length of the specified memory.\n
359
    Note:  This is used to evaluate "size[name]" in "${...}" expressions.
360
    """
361
    self.memoryLength[name] = length;
362
 
363
  def RegisterStackLength(self,name,length):
364
    """
365
    Record the length of the specified stack.\n
366 13 sinclairrf
    Note:  This is used to evaluate "size[name]" in "${...}" expressions.\n
367
    Note:  This differs from RegisterMemoryLength() in that the stack lengths
368
           can be larger than 256.
369 2 sinclairrf
    """
370
    self.stackLength[name] = length;
371
 
372
  ################################################################################
373
  #
374
  # Check a list of raw tokens to ensure their proper format.
375
  #
376
  ################################################################################
377
 
378
  def CheckSymbolToken(self,name,allowableTypes,loc):
379
    """
380
    Syntax check for symbols, either by themselves or as a macro argument.\n
381
    Note:  This is used by CheckRawTokens.
382
    """
383
    if not self.IsSymbol(name):
384
      raise asmDef.AsmException('Undefined symbol "%s" at %s' % (name,loc));
385
    ixName = self.symbols['list'].index(name);
386
    if self.symbols['type'][ixName] not in allowableTypes:
387
      raise asmDef.AsmException('Illegal symbol at %s' % loc);
388
 
389
  def CheckRawTokens(self,rawTokens):
390
    """
391
    Syntax check for directive bodies.\n
392
    Note:  This core-specific method is called by the top-level assembler after
393
           the RawTokens method.
394
    """
395
    # Ensure the first token is a directive.
396
    firstToken = rawTokens[0];
397
    if firstToken['type'] != 'directive':
398
      raise Exception('Program Bug triggered at %s' % firstToken['loc']);
399
    # Ensure the directive bodies are not too short.
400
    if (firstToken['value'] in ('.main','.interrupt',)) and not (len(rawTokens) > 1):
401
      raise asmDef.AsmException('"%s" missing body at %s' % (firstToken['value'],firstToken['loc'],));
402 9 sinclairrf
    if (firstToken['value'] in ('.define','.macro',)) and not (len(rawTokens) == 2):
403 3 sinclairrf
      raise asmDef.AsmException('body for "%s" directive must have exactly one argument at %s' % (firstToken['value'],firstToken['loc'],));
404 2 sinclairrf
    if (firstToken['value'] in ('.constant','.function','.memory','.variable',)) and not (len(rawTokens) >= 3):
405
      raise asmDef.AsmException('body for "%s" directive too short at %s' % (firstToken['value'],firstToken['loc'],));
406
    # Ensure no macros and no instructions in non-"functions".
407
    # Byproduct:  No labels allowed in non-"functions".
408
    if firstToken['value'] not in ('.function','.interrupt','.main',):
409
      for token in rawTokens[2:]:
410
        if (token['type'] == 'macro'):
411
          raise asmDef.AsmException('Macro not allowed in directive at %s' % token['loc']);
412
        if token['type'] == 'instruction':
413
          raise asmDef.AsmException('Instruction not allowed in directive at %s' % token['loc']);
414
    # Ensure local labels are defined and used.
415
    labelDefs = list();
416
    for token in rawTokens:
417
      if token['type'] == 'label':
418
        name = token['value'];
419
        if name in labelDefs:
420
          raise asmDef.AsmException('Repeated label definition "%s" at %s' % (name,token['loc'],));
421
        labelDefs.append(name);
422
    labelsUsed = list();
423
    for token in rawTokens:
424
      if (token['type'] == 'macro') and (token['value'] in ('.jump','.jumpc',)):
425
        target = token['argument'][0]['value'];
426
        if target not in labelDefs:
427
          raise asmDef.AsmException('label definition for target missing at %s' % token['loc']);
428
        labelsUsed.append(target);
429
    labelsUnused = set(labelDefs) - set(labelsUsed);
430
    if labelsUnused:
431
      raise asmDef.AsmException('Unused label(s) %s in body %s' % (labelsUnused,firstToken['loc']));
432
    # Ensure referenced symbols are already defined (other than labels and
433
    # function names for call and jump macros).
434
    checkBody = False;
435
    if (rawTokens[0]['type'] == 'directive') and (rawTokens[0]['value'] in ('.function','.interrupt','.main',)):
436
      checkBody = True;
437
    if checkBody:
438
      for token in rawTokens[2:]:
439
        if token['type'] == 'symbol':
440
          allowableTypes = ('constant','inport','macro','outport','outstrobe','parameter','variable',);
441
          self.CheckSymbolToken(token['value'],allowableTypes,token['loc']);
442
        elif token['type'] == 'macro':
443
          allowableTypes = ('RAM','ROM','constant','inport','outport','outstrobe','parameter','variable',);
444
          ixFirst = 1 if token['value'] in self.MacrosWithSpecialFirstSymbol else 0;
445
          for arg in  token['argument'][ixFirst:]:
446
            if arg['type'] == 'symbol':
447
              self.CheckSymbolToken(arg['value'],allowableTypes,arg['loc']);
448 9 sinclairrf
    # Ensure the main body ends in a ".jump".
449
    lastToken = rawTokens[-1];
450
    if firstToken['value'] == '.main':
451
      if (lastToken['type'] != 'macro') or (lastToken['value'] != '.jump'):
452
        raise asmDef.AsmException('.main body does not end in ".jump" at %s' % lastToken['loc']);
453 12 sinclairrf
    # Ensure the interrupt body contains a ".returni" and does not contain any ".return"s.
454
    if firstToken['value'] == '.interrupt':
455
      for token in [token for token in rawTokens if (token['type'] == 'macro') and (token['value'] == '.return')]:
456
        raise asmDef.AsmException('".return" macro prohibited in .interrupt at %s' % token['loc']);
457
      foundReturni = [token for token in rawTokens if (token['type'] == 'macro') and (token['value'] == '.returni')];
458
      if not foundReturni:
459
        raise asmDef.AsmException('.returni missing in .interrupt body at %s' % rawTokens[0]['loc']);
460
      if (lastToken['type'] != 'macro') or (lastToken['value'] not in ('.jump','.returni',)):
461
        raise asmDef.AsmException('.interrupt must end in .jump or .returni instead of "%s" at %s' % (lastToken['value'],lastToken['loc'],));
462
    # Ensure functions end in a ".jump" or ".return".
463
    if firstToken['value'] == '.function':
464 9 sinclairrf
      if (lastToken['type'] != 'macro') or (lastToken['value'] not in ('.jump','.return',)):
465 12 sinclairrf
        raise asmDef.AsmException('function "%s" must end in .jump or .return instead of "%s" at %s' % (firstToken['value'],lastToken['value'],lastToken['loc'],));
466
    # Ensure that .main and normal functions do not use ".returni".
467
    if firstToken['value'] in ('.main','.function',):
468
      for token in [token for token in rawTokens if (token['type'] == 'macro') and (token['value'] == '.returni')]:
469
        raise asmDef.AsmException('.returni prohibited outside .interrupt at %s' % token['loc']);
470 2 sinclairrf
 
471
  ################################################################################
472
  #
473
  # fill in symbols, etc. in the list of raw tokens.
474
  #
475
  ################################################################################
476
 
477
  def ByteList(self,rawTokens,limit=False):
478
    """
479
    Return either (1) a list comprised of a single token which may not be a
480
    byte or (2) a list comprised of multiple tokens, each of which is a single
481
    byte.\n
482 9 sinclairrf
    Note:  This is called by FillRawTokens.\n
483
    Note:  Multi-value lists must be single-byte values (i.e., in the range -128 to 255)
484 2 sinclairrf
    """
485
    if len(rawTokens) > 1:
486
      limit = True;
487
    values = list();
488 9 sinclairrf
    for token in rawTokens:
489
      if token['type'] == 'symbol':
490
        ix = self.symbols['list'].index(token['value']);
491
        symbolType = self.symbols['type'][ix];
492
        if symbolType != 'constant':
493
          raise asmDef.AsmException('Illegal symbol "%s" at %s' % (token['value'],token['loc'],));
494
        value = self.symbols['body'][ix];
495
      elif token['type'] == 'value':
496
        value = token['value'];
497
      else:
498
        raise asmDef.AsmException('Illegal token "%s" with value "%s" at %s' % (token['type'],token['value'],token['loc'],));
499
      if type(value) == int:
500
        value = [value];
501
      else:
502
        limit = True;
503
      for v in value:
504
        if limit and not (-128 <= v < 256):
505
          raise asmDef.AsmException('Out-of-rarnge value "%d" at %s' % (v,token['loc'],))
506
        values.append(v);
507 2 sinclairrf
    return values;
508
 
509
  def ExpandSymbol(self,token,singleValue):
510
    """
511
    Convert the token for a symbol into a token for its specific type.
512
    Optionally ensure constants expand to a single byte.  For parameters,
513
    ensure that a range is provided.\n
514
    Note:  Symbols must be defined before the directive bodies in which they
515
           are used.\n
516
    Note:  This is called in two spots.  The first is ExpandTokens, where
517
           isolated symbols are processed, for example to get the value of a
518
           constant.  The second is in EmitOptArg where symbols in arguments to
519
           macros are expanded (this allows the macro-specific processing to
520
           identify labels vs. symbols).
521
    """
522
    if not self.IsSymbol(token['value']):
523
      raise asmDef.AsmException('Symbol "%s" not in symbol list at %s' %(token['value'],token['loc'],));
524
    ix = self.symbols['list'].index(token['value']);
525
    symbolType = self.symbols['type'][ix];
526
    if symbolType == 'RAM':
527
      return dict(type='RAM', value=token['value'], loc=token['loc']);
528
    elif symbolType == 'ROM':
529
      return dict(type='ROM', value=token['value'], loc=token['loc']);
530
    elif symbolType == 'constant':
531
      if singleValue:
532
        thisBody = self.symbols['body'][ix];
533
        if len(thisBody) != 1:
534
          raise asmDef.AsmException('Constant "%s" must evaluate to a single byte at %s' % (token['value'],token['loc'],))
535
        thisBody = thisBody[0];
536
        if not (-128 <= thisBody < 256):
537
          raise asmDef.AsmException('Constant "%s" must be a byte value at %s' % (token['value'],token['loc'],));
538
      return dict(type='constant', value=token['value'], loc=token['loc']);
539
    elif symbolType == 'inport':
540
      return dict(type='inport', value=token['value'], loc=token['loc']);
541
    elif symbolType == 'outport':
542
      return dict(type='outport', value=token['value'], loc=token['loc']);
543
    elif symbolType == 'outstrobe':
544
      return dict(type='outstrobe', value=token['value'], loc=token['loc']);
545
    elif symbolType == 'parameter':
546
      if 'range' in token:
547
        trange = token['range'];
548
      else:
549
        trange = '[0+:8]';
550
      return dict(type='parameter', value=token['value'], range=trange, loc=token['loc']);
551
    elif symbolType == 'variable':
552
      return dict(type='variable', value=token['value'], loc=token['loc']);
553
    else:
554
      raise Exception('Program Bug -- unrecognized symbol type "%s"' % symbolType);
555
 
556
  def ExpandTokens(self,rawTokens):
557
    """
558
    Compute the relative addresses for tokens within function bodies.\n
559
    The return is a list of the tokens in the function body, each of which has
560
    a type, value, offset (relative address), and location within the source
561
    code.  Macro types also have the list of arguments provided to the macro.
562
    """
563
    tokens = list();
564
    offset = 0;
565
    for token in rawTokens:
566
      # insert labels
567
      if token['type'] == 'label':
568
        tokens.append(dict(type=token['type'], value=token['value'], offset=offset, loc=token['loc']));
569
        # labels don't change the offset
570
      # append instructions
571
      elif token['type'] == 'instruction':
572
        tokens.append(dict(type=token['type'], value=token['value'], offset=offset, loc=token['loc']));
573
        offset = offset + 1;
574
      # append values
575
      elif token['type'] == 'value':
576
        if type(token['value']) == int:
577
          tokens.append(dict(type=token['type'], value=token['value'], offset=offset, loc=token['loc']));
578
          offset = offset + 1;
579
        else:
580
          revTokens = copy.copy(token['value']);
581
          revTokens.reverse();
582
          for lToken in revTokens:
583
            tokens.append(dict(type=token['type'], value=lToken, offset=offset, loc=token['loc']));
584
            offset = offset + 1;
585
      # append macros
586
      elif token['type'] == 'macro':
587
        tokens.append(dict(type=token['type'], value=token['value'], offset=offset, argument=token['argument'], loc=token['loc']));
588
        offset = offset + self.MacroLength(token);
589
      # interpret and append symbols
590
      elif token['type'] == 'symbol':
591
        newToken = self.ExpandSymbol(token,singleValue=False);
592
        newToken['offset'] = offset;
593
        newToken['loc'] = token['loc'];
594
        tokens.append(newToken);
595
        if token['type'] == 'constant':
596
          ix = self.symbols['list'].index(newToken['value']);
597
          offset = offset + len(self.symbols['body'][ix]);
598
        else:
599
          offset = offset + 1;
600
      # anything else is a program bug
601
      else:
602
        raise Exception('Program bug:  unexpected token type "%s"' % token['type']);
603
    return dict(tokens=tokens, length=offset);
604
 
605
  def FillRawTokens(self,rawTokens):
606
    """
607
    Do one of the following as required for the specified directive:
608
      .constant         add the constant and its body to the list of symbols
609
      .function         add the function and its body, along with the relative
610
                        addresses, to the list of symbols
611
      .interrupt        record the function body and relative addresses
612 3 sinclairrf
      .macro            register the user-defined macro
613 2 sinclairrf
      .main             record the function body and relative addresses
614
      .memory           record the definition of the memory and make it current
615
                        for subsequent variable definitions.
616
      .variable         add the variable and its associated memory, length, and
617
                        initial values to the list of symbols
618
    """
619
    firstToken = rawTokens[0];
620
    secondToken = rawTokens[1];
621
    # Perform syntax check common to several directives.
622
    if firstToken['value'] in ('.constant','.function','.variable',):
623
      if secondToken['type'] != 'symbol':
624
        raise asmDef.AsmException('Expected symbol, not "%s", at %s' % (secondToken['value'],secondToken['loc'],));
625
      if self.IsSymbol(secondToken['value']):
626
        raise asmDef.AsmException('Symbol "%s" already defined at %s' % (secondToken['value'],secondToken['loc'],));
627
    # Perform syntax-specific processing.
628
    if firstToken['value'] == '.constant':
629
      byteList = self.ByteList(rawTokens[2:]);
630
      self.AddSymbol(secondToken['value'],'constant',body=byteList);
631 9 sinclairrf
    # Process ".define" directive
632
    elif firstToken['value'] == '.define':
633
      self.AddSymbol(secondToken['value'],'define');
634 2 sinclairrf
    # Process ".function" definition.
635
    elif firstToken['value'] == '.function':
636
      self.AddSymbol(secondToken['value'],'function',self.ExpandTokens(rawTokens[2:]));
637
    # Process ".interrupt" definition.
638
    elif firstToken['value'] == '.interrupt':
639
      if self.interrupt:
640
        raise asmDef.AsmException('Second definition of ".interrupt" at %s' % firstToken['loc']);
641
      self.interrupt = self.ExpandTokens(rawTokens[1:]);
642 3 sinclairrf
    # Process user-defined macros (the ".macro XXX" directive can be repeated for non-built-in macros).
643
    elif firstToken['value'] == '.macro':
644
      macroName = secondToken['value'];
645
      fullMacroName = '.' + macroName;
646
      if fullMacroName in self.directives:
647
        raise asmDef.AsmException('Macro "%s" is a directive at %s' % (fullMacroName,secondToken['loc'],));
648
      if fullMacroName in self.instructions:
649
        raise asmDef.AsmException('Macro "%s" is an instruction at %s' % (fullMacroName,secondToken['loc'],));
650
      if self.IsBuiltInMacro(fullMacroName):
651
        raise asmDef.AsmException('Macro "%s" is a built-in macro at %s' % (fullMacroName,secondToken['loc'],));
652
      if fullMacroName not in self.macros['list']:
653
        self.AddUserMacro(macroName);
654 2 sinclairrf
    # Process ".main" definition.
655
    elif firstToken['value'] == '.main':
656
      if self.main:
657
        raise asmDef.AsmException('Second definition of ".main" at %s' % firstToken['loc']);
658
      self.main = self.ExpandTokens(rawTokens[1:]);
659
    # Process ".memory" declaration.
660
    elif firstToken['value'] == '.memory':
661
      if len(rawTokens) != 3:
662
        raise asmDef.AsmException('".memory" directive requires exactly two arguments at %s' % firstToken['loc']);
663
      if (secondToken['type'] != 'symbol') or (secondToken['value'] not in ('RAM','ROM',)):
664
        raise asmDef.AsmException('First argument to ".memory" directive must be "RAM" or "RAM" at %s' % secondToken['loc']);
665
      thirdToken = rawTokens[2];
666
      if thirdToken['type'] != 'symbol':
667
        raise asmDef.AsmException('".memory" directive requires name for second argument at %s' % thirdToken['loc']);
668
      if self.IsSymbol(thirdToken['value']):
669
        ix = self.symbols['list'].index(thirdToken['value']);
670
        if self.symbols['type'] != secondToken['value']:
671
          raise asmDef.AsmException('Redefinition of ".memory %s %s" not allowed at %s' % (secondToken['value'],thirdToken['value'],firstToken['loc']));
672
      else:
673
        self.AddSymbol(thirdToken['value'],secondToken['value'],dict(length=0));
674
      self.currentMemory = thirdToken['value'];
675
    # Process ".variable" declaration.
676
    elif firstToken['value'] == '.variable':
677
      if not self.currentMemory:
678
        raise asmDef.AsmException('".memory" directive required before ".variable" directive at %s' % firstToken['line']);
679
      ixMem = self.symbols['list'].index(self.currentMemory);
680
      currentMemoryBody = self.symbols['body'][ixMem];
681
      byteList = self.ByteList(rawTokens[2:],limit=True);
682
      body = dict(memory=self.currentMemory, start=currentMemoryBody['length'], value=byteList);
683
      self.AddSymbol(secondToken['value'], 'variable', body=body);
684
      currentMemoryBody['length'] = currentMemoryBody['length'] + len(byteList);
685
      if currentMemoryBody['length'] > 256:
686
        raise asmDef.AsmException('Memory "%s" becomes too long at %s' % (self.currentMemory,firstToken['loc']));
687
    # Everything else is an error.
688
    else:
689
      raise Exception('Program Bug:  Unrecognized directive %s at %s' % (firstToken['value'],firstToken['loc']));
690
 
691
  def Main(self):
692
    """
693
    Return the body of the .main function.
694
    Note:  This is used by the top-level assembler to verify that the .main
695
           function has been defined.
696
    """
697
    return self.main;
698
 
699
  def Interrupt(self):
700
    """
701
    Return the body of the .interrupt function.
702
    Note:  This is used by the top-level assembler to verify that the .interrupt
703
           function has or has not been defined.
704
    """
705
    return self.interrupt;
706
 
707
  ################################################################################
708
  #
709
  # Compute the memory bank indices.
710
  #
711
  ################################################################################
712
 
713
  def EvaluateMemoryTree(self):
714
    """
715
    Ensure defined memories are used.  Add the memory name, type, and length to
716
    the list of memories.  Compute the bank index ascending from 0 for RAMs and
717
    descending from 3 for ROMs and add that index to the memory attributes.
718
    Ensure that no more than 4 memories are listed.
719
    """
720
    self.memories = dict(list=list(), type=list(), length=list(), bank=list());
721
    ramBank = 0;
722
    romBank = 3;
723
    for ix in range(len(self.symbols['list'])):
724
      if self.symbols['type'][ix] in ('RAM','ROM',):
725
        memBody = self.symbols['body'][ix];
726
        if memBody['length'] == 0:
727
          raise asmDef.AsmException('Empty memory:  %s' % self.symbols['list'][ix]);
728
        self.memories['list'].append(self.symbols['list'][ix]);
729
        self.memories['type'].append(self.symbols['type'][ix]);
730
        self.memories['length'].append(memBody['length']);
731
        if self.symbols['type'][ix] == 'RAM':
732
          self.memories['bank'].append(ramBank);
733
          ramBank = ramBank + 1;
734
        else:
735
          self.memories['bank'].append(romBank);
736
          romBank = romBank - 1;
737
    if len(self.memories['list']) > 4:
738
      raise asmDef.AsmException('Too many memory banks');
739
 
740
  ################################################################################
741
  #
742
  # Generate the list of required functions from the ".main" and ".interrupt"
743
  # bodies.
744
  #
745
  # Look for function calls with the bodies of the required functions.  If the
746
  # function has not already been identified as a required function then (1)
747
  # ensure it exists and is a function and then (2) add it to the list of
748
  # required functions.
749
  #
750
  # Whenever a function is added to the list, set its start address and get its
751
  # length.
752
  #
753
  ################################################################################
754
 
755
  def EvaluateFunctionTree(self):
756
    """
757
    Create a list of the functions required by the program, starting with the
758
    required .main function and the optional .interrupt function.\n
759
    Record the length of each function, its body, and its start address and
760
    calculate the addresses of the labels within each function body.\n
761
    Finally, ensure the function address space does not exceed the absolute
762
    8192 address limit.
763
    """
764
    self.functionEvaluation = dict(list=list(), length=list(), body=list(), address=list());
765
    nextStart = 0;
766 12 sinclairrf
    # ".interrupt" is optionally required (and is sure to exist by this
767
    # function call if it is required).  The interrupt handler always starts at
768
    # address 3 so that address 0 can be a jump to ".main".
769
    if self.interrupt:
770
      nextStart = 3;
771
      self.functionEvaluation['list'].append('.interrupt');
772
      self.functionEvaluation['length'].append(self.interrupt['length']);
773
      self.functionEvaluation['body'].append(self.interrupt['tokens']);
774
      self.functionEvaluation['address'].append(nextStart);
775
      nextStart = nextStart + self.functionEvaluation['length'][-1];
776 2 sinclairrf
    # ".main" is always required.
777
    self.functionEvaluation['list'].append('.main');
778
    self.functionEvaluation['length'].append(self.main['length']);
779
    self.functionEvaluation['body'].append(self.main['tokens']);
780
    self.functionEvaluation['address'].append(nextStart);
781
    nextStart = nextStart + self.functionEvaluation['length'][-1];
782
    # Loop through the required function bodies as they are identified.
783
    ix = 0;
784
    while ix < len(self.functionEvaluation['body']):
785
      for token in self.functionEvaluation['body'][ix]:
786
        if (token['type'] == 'macro') and (token['value'] in ('.call','.callc',)):
787
          callName = token['argument'][0]['value'];
788
          if callName not in self.functionEvaluation['list']:
789
            if not self.IsSymbol(callName):
790
              raise asmDef.AsmException('Function "%s" not defined for function "%s"' % (callName,self.functionEvaluation['list'][ix],));
791
            ixName = self.symbols['list'].index(callName);
792
            if self.symbols['type'][ixName] != 'function':
793 9 sinclairrf
              raise asmDef.AsmException('Function "%s" called by "%s" is not a function' % (callName, self.functionEvaluation['list'][ix],));
794 2 sinclairrf
            self.functionEvaluation['list'].append(callName);
795
            self.functionEvaluation['length'].append(self.symbols['body'][ixName]['length']);
796
            self.functionEvaluation['body'].append(self.symbols['body'][ixName]['tokens']);
797
            self.functionEvaluation['address'].append(nextStart);
798
            nextStart = nextStart + self.functionEvaluation['length'][-1];
799
      ix = ix + 1;
800
    # Within each function, compute the list of label addresses and then fill in
801
    # the address for all jumps and calls.
802
    for ix in range(len(self.functionEvaluation['list'])):
803
      startAddress = self.functionEvaluation['address'][ix];
804
      labelAddress = dict(list=list(), address=list());
805
      for token in self.functionEvaluation['body'][ix]:
806
        if token['type'] == 'label':
807
          labelAddress['list'].append(token['value']);
808
          labelAddress['address'].append(startAddress + token['offset']);
809
      for token in self.functionEvaluation['body'][ix]:
810
        if token['type'] != 'macro':
811
          continue;
812
        if token['value'] in ('.jump','.jumpc',):
813
          ix = labelAddress['list'].index(token['argument'][0]['value']);
814
          token['address'] = labelAddress['address'][ix];
815
        elif token['value'] in ('.call','.callc',):
816
          ix = self.functionEvaluation['list'].index(token['argument'][0]['value']);
817
          token['address'] = self.functionEvaluation['address'][ix];
818
    # Sanity checks for address range
819
    if self.functionEvaluation['address'][-1] + self.functionEvaluation['length'][-1] >= 2**13:
820
      raise asmDef.AsmException('Max address for program requires more than 13 bits');
821
 
822
  ################################################################################
823
  #
824
  # Emit the meta code for the memories.
825
  #
826
  ################################################################################
827
 
828
  def EmitMemories(self,fp):
829
    """
830
    Print the memories to the metacode file.\n
831
    The first line for each memory has the format
832
      :memory type mem_name bank length
833
    where
834
      type              is RAM or ROM
835
      mem_name          is the name of the memory
836
      bank              is the assigned bank address
837
      length            is the number of bytes used by the memory\n
838
    The subsequent lines are sequences of
839
      - variable_name
840
      value(s)
841
    where
842
      '-'               indicates a variable name is present
843
      variable_name     is the name of the variable
844
      values(s)         is one or more lines for the values with one byte per line
845
                        Note:  because the lines with variable names start with
846
                               '-', negative values are converted to unsigned
847
                               values\n
848
    """
849
    # Emit the individual memories.
850
    for ixMem in range(len(self.memories['list'])):
851
      fp.write(':memory %s %s %d %d\n' % (self.memories['type'][ixMem],self.memories['list'][ixMem],self.memories['bank'][ixMem],self.memories['length'][ixMem]));
852
      memName = self.memories['list'][ixMem];
853
      address = 0;
854
      for ixSymbol in range(len(self.symbols['list'])):
855
        if self.symbols['type'][ixSymbol] != 'variable':
856
          continue;
857
        vBody = self.symbols['body'][ixSymbol];
858
        if vBody['memory'] != memName:
859
          continue;
860
        fp.write('- %s\n' % self.symbols['list'][ixSymbol]);
861
        for v in vBody['value']:
862
          if not (-128 <=v < 256):
863
            raise Exception('Program Bug -- value not representable by a byte');
864
          fp.write('%02X\n' % (v % 0x100,));
865
      fp.write('\n');
866
 
867
  ################################################################################
868
  #
869
  # Emit the metacode for the program.
870
  #
871
  ################################################################################
872
 
873
  #
874
  # Utilities for building opcodes or the associated description strings.
875
  #
876
  # Note:  These utilities do not write to the metacode file.
877
  #
878
 
879
  def Emit_AddLabel(self,name):
880
    """
881
    Append the label to the labels associated with the current program address.
882
    """
883
    self.emitLabelList += ':' + name + ' ';
884
 
885
  def Emit_EvalSingleValue(self,token):
886
    """
887
    Evaluate the optional single-byte value for a macro.
888
    """
889
    if token['type'] == 'symbol':
890
      token = self.ExpandSymbol(token,singleValue=True);
891
    if token['type'] == 'constant':
892
      name = token['value'];
893
      if not self.IsSymbol(name):
894
        raise Exception('Program Bug');
895
      ix = self.symbols['list'].index(name);
896
      if len(self.symbols['body'][ix]) != 1:
897
        raise asmDef.AsmException('Optional constant can only be one byte at %s' % token['loc']);
898
      return self.symbols['body'][ix][0]
899
    elif token['type'] == 'value':
900
      return token['value']
901
    else:
902
      raise asmDef.AsmException('Unrecognized optional argument "%s"' % token['value']);
903
 
904 5 sinclairrf
  def Emit_GetAddrAndBank(self,token):
905 2 sinclairrf
    """
906
    For the specified variable, return an ordered tuple of the memory address
907
    within its bank, the corresponding bank index, and the corresponding bank
908
    name.\n
909 3 sinclairrf
    Note:  This is used by several user-defined macros that fetch from or store
910
           to variables.
911 2 sinclairrf
    """
912 5 sinclairrf
    name = token['value'];
913 2 sinclairrf
    if not self.IsSymbol(name):
914 5 sinclairrf
      raise asmDef.AsmException('"%s" is not a recognized symbol at %s' % (name,token['loc'],));
915 2 sinclairrf
    ixName = self.symbols['list'].index(name);
916
    if self.symbols['type'][ixName] != 'variable':
917 5 sinclairrf
      raise asmDef.AsmException('"%s" is not a variable at %s' % (name,token['loc'],));
918 2 sinclairrf
    body = self.symbols['body'][ixName];
919
    bankName = body['memory'];
920
    ixMem = self.memories['list'].index(bankName);
921
    return (body['start'],self.memories['bank'][ixMem],bankName,);
922
 
923
  def Emit_GetBank(self,name):
924
    """
925
    For the specified variable, return the memory bank index.\n
926 3 sinclairrf
    Note:  This is used by the .fetch, .fetch+, .fetch-, .store, .store+, and
927 2 sinclairrf
           .store- macros.
928
    """
929
    if name not in self.memories['list']:
930
      raise asmDef.AsmException('"%s" not a memory' % name);
931
    ixMem = self.memories['list'].index(name);
932
    return self.memories['bank'][ixMem];
933
 
934
  def Emit_String(self,name=''):
935
    """
936
    Append the specified string to the list of labels for the current
937
    instruction, restart the list of labels, and return the composite string.
938
    """
939
    name = self.emitLabelList + name;
940
    self.emitLabelList = '';
941
    return name;
942
 
943 4 sinclairrf
  def Emit_IntegerValue(self,token):
944
    """
945
    Return the integer value associated with a constant or a numeric expression.
946
    """
947
    if token['type'] == 'value':
948
      v = token['value'];
949
    elif token['type'] == 'symbol':
950
      name = token['value'];
951
      if not self.IsSymbol(name):
952
        raise asmDef.AsmException('Symbol "%s" not recognized at %s' % (token['value'],token['loc'],));
953
      ix = self.symbols['list'].index(name);
954
      v = self.symbols['body'][ix];
955
      if len(v) != 1:
956
        raise asmDef.AsmException('Argument can only be one value at %s' % token['loc']);
957
      v = v[0];
958
    else:
959
      raise asmDef.AsmException('Argument "%s" of type "%s" not recognized at %s' % (token['value'],token['type'],token['loc'],));
960
    if type(v) != int:
961
      raise Exception('Program Bug -- value should be an "int"');
962
    return v;
963
 
964 2 sinclairrf
  #
965
  # Utilities to write single instructions to the metacode file.
966
  #
967
  # Note:  Other than the program header and the function names, these
968
  #        utilities write the function bodies.
969
  #
970
 
971
  def EmitOpcode(self,fp,opcode,name):
972
    """
973
    Write the specified opcode and the associated comment string.\n
974
    The leading bit for an opcode is always a '0'.
975
    """
976
    if not (0 <= opcode < 256):
977
      raise Exception('Program Bug -- opcode "0x%X" out of range');
978
    fp.write('0%02X %s\n' % (opcode,self.Emit_String(name)));
979
 
980
  def EmitParameter(self,fp,token):
981
    """
982
    Write the name (and range) of the specified parameter and the optional
983
    associated comment string.\n
984
    The string 'p' specifies that the parameter is to be inserted into the
985
    instruction body.\n
986
    Note:  The comment string may be the empty string if there were no labels
987
           immediately preceding the parameter.
988
    """
989
    name = token['value'];
990
    if not self.IsParameter(name):
991
      raise Exception('Program Bug');
992
    commentString = self.Emit_String();
993
    if commentString:
994
      fp.write('p %s%s %s\n' % (name,token['range'],commentString,));
995
    else:
996
      fp.write('p %s%s\n' % (name,token['range'],));
997
 
998
  def EmitPush(self,fp,value,name=None,tokenLoc=None):
999
    """
1000
    Write the opcode to push a value onto the data stack.  Include the comment
1001
    string including either the optionally provided symbol name or a printable
1002
    representation of the value being pushed onto the stack.\n
1003
    Note:  The printable value is included when a name is not provided so that
1004
           the contents of single characters or of strings being pushed onto
1005
           the stack can be read.\n
1006
    Note:  The token location is an optional input required when the range of
1007
           the provided value may not have been previously ensured to fit in
1008
           one byte.
1009
    """
1010
    if not (-128 <= value < 256):
1011
      if tokenLoc == None:
1012
        raise Exception('Program Bug -- untrapped out-of-range token "%s"' % value);
1013
      else:
1014
        raise asmDef.AsmException('Value not representable by a byte at "%s"' % tokenLoc);
1015
    if value < 0:
1016
      value = value + 256;
1017
    if type(name) == str:
1018
      fp.write('1%02X %s\n' % ((value % 0x100),self.Emit_String(name)));
1019
    elif (chr(value) in string.printable) and (chr(value) not in string.whitespace):
1020
      fp.write('1%02X %s\n' % ((value % 0x100),self.Emit_String('%02X \'%c\'' % (value,value,))));
1021
    else:
1022
      fp.write('1%02X %s\n' % ((value % 0x100),self.Emit_String('0x%02X' % value)));
1023
 
1024
  def EmitVariable(self,fp,name):
1025
    """
1026
    Use the EmitPush method to push the address of a variable onto the data
1027
    stack.
1028
    """
1029
    if not self.IsSymbol(name):
1030
      raise asmDef.AsmException('Variable "%s" not recognized' % name);
1031
    ixName = self.symbols['list'].index(name);
1032
    if self.symbols['type'][ixName] != 'variable':
1033
      raise asmDef.AsmException('"%s" is not a variable' % name);
1034
    self.EmitPush(fp,self.symbols['body'][ixName]['start'],name);
1035
 
1036
  #
1037
  # EmitOpcode, EmitMacro, and EmitProgram emit composite or more complicated
1038
  # bodies.
1039
  #
1040
 
1041
  def EmitOptArg(self,fp,token):
1042
    """
1043
    Write the metacode for optional arguments to macros.\n
1044
    These must be single-instruction arguments.
1045
    """
1046
    # Symbols encountered in macros are expanded here instead of the
1047
    # ExpandTokens method -- the code is much simpler this way even though the
1048
    # associated error detection was deferred in the processing.  The symbol
1049
    # must expand to a single value.
1050
    if token['type'] == 'symbol':
1051
      token = self.ExpandSymbol(token,singleValue=True);
1052
    if token['type'] == 'constant':
1053
      name = token['value'];
1054
      if not self.IsSymbol(name):
1055
        raise Exception('Program Bug');
1056
      ix = self.symbols['list'].index(name);
1057
      if len(self.symbols['body'][ix]) != 1:
1058
        raise asmDef.AsmException('Optional constant can only be one byte at %s' % token['loc']);
1059
      self.EmitPush(fp,self.symbols['body'][ix][0],self.Emit_String(name),tokenLoc=token['loc']);
1060
    elif token['type'] in ('inport','outport','outstrobe'):
1061
      name = token['value'];
1062
      if not self.IsSymbol(name):
1063
        raise Exception('Program Bug -- unrecognized inport/outport name "%s"');
1064
      ix = self.symbols['list'].index(name);
1065
      self.EmitPush(fp,self.symbols['body'][ix],self.Emit_String(name));
1066
    elif token['type'] == 'instruction':
1067
      self.EmitOpcode(fp,self.InstructionOpcode(token['value']),token['value']);
1068
    elif token['type'] == 'parameter':
1069
      self.EmitParameter(fp,token);
1070
    elif token['type'] == 'value':
1071
      self.EmitPush(fp,token['value'],tokenLoc=token['loc']);
1072
    elif token['type'] == 'variable':
1073
      self.EmitVariable(fp,token['value']);
1074
    elif token['type'] == 'macro':
1075
      self.EmitMacro(fp,token);
1076
    else:
1077
      raise asmDef.AsmException('Unrecognized optional argument "%s"' % token['value']);
1078
 
1079
  def EmitMacro(self,fp,token):
1080
    """
1081 3 sinclairrf
    Write the metacode for a macro.\n
1082
    The macros coded here are required to access intrinsics.
1083 2 sinclairrf
    """
1084
    # .call
1085
    if token['value'] == '.call':
1086
      self.EmitPush(fp,token['address'] & 0xFF,'');
1087
      self.EmitOpcode(fp,self.specialInstructions['call'] | (token['address'] >> 8),'call '+token['argument'][0]['value']);
1088
      self.EmitOptArg(fp,token['argument'][1]);
1089
    # .callc
1090
    elif token['value'] == '.callc':
1091
      self.EmitPush(fp,token['address'] & 0xFF,'');
1092
      self.EmitOpcode(fp,self.specialInstructions['callc'] | (token['address'] >> 8),'callc '+token['argument'][0]['value']);
1093
      self.EmitOptArg(fp,token['argument'][1]);
1094 12 sinclairrf
    # .dis
1095
    elif token['value'] == '.dis':
1096
      if not self.interruptsEnabled:
1097
        raise Exception('Program Bug -- interrupts not enabled');
1098
      dis_outport = self.interrupt_dis_outport;
1099
      self.EmitPush(fp,self.OutportAddress(dis_outport),dis_outport);
1100
      self.EmitOpcode(fp,self.InstructionOpcode('outport'),'outport (.dis)');
1101
    # .ena
1102
    elif token['value'] == '.ena':
1103
      if not self.interruptsEnabled:
1104
        raise Exception('Program Bug -- interrupts not enabled');
1105
      ena_outport = self.interrupt_ena_outport;
1106
      self.EmitPush(fp,self.OutportAddress(ena_outport),ena_outport);
1107
      self.EmitOpcode(fp,self.InstructionOpcode('outport'),'outport (.ena)');
1108 2 sinclairrf
    # .fetch
1109
    elif token['value'] == '.fetch':
1110
      name = token['argument'][0]['value'];
1111
      ixBank = self.Emit_GetBank(name);
1112
      self.EmitOpcode(fp,self.specialInstructions['fetch'] | ixBank,'fetch '+name);
1113
    # .fetch+
1114
    elif token['value'] == '.fetch+':
1115
      name = token['argument'][0]['value'];
1116
      ixBank = self.Emit_GetBank(name);
1117
      self.EmitOpcode(fp,self.specialInstructions['fetch+'] | ixBank,'fetch+('+name+')');
1118
    # .fetch-
1119
    elif token['value'] == '.fetch-':
1120
      name = token['argument'][0]['value'];
1121
      ixBank = self.Emit_GetBank(name);
1122
      self.EmitOpcode(fp,self.specialInstructions['fetch-'] | ixBank,'fetch-('+name+')');
1123
    # .jump
1124
    elif token['value'] == '.jump':
1125
      self.EmitPush(fp,token['address'] & 0xFF,'');
1126
      self.EmitOpcode(fp,self.specialInstructions['jump'] | (token['address'] >> 8),'jump '+token['argument'][0]['value']);
1127
      self.EmitOptArg(fp,token['argument'][1]);
1128
    # .jumpc
1129
    elif token['value'] == '.jumpc':
1130
      self.EmitPush(fp,token['address'] & 0xFF,'');
1131
      self.EmitOpcode(fp,self.specialInstructions['jumpc'] | (token['address'] >> 8),'jumpc '+token['argument'][0]['value']);
1132
      self.EmitOptArg(fp,token['argument'][1]);
1133
    # .return
1134
    elif token['value'] == '.return':
1135
      self.EmitOpcode(fp,self.specialInstructions['return'],'return');
1136
      self.EmitOptArg(fp,token['argument'][0]);
1137 12 sinclairrf
    # .returni
1138
    elif token['value'] == '.returni':
1139
      if not self.interruptsEnabled:
1140
        raise Exception('Program Bug -- interrupts not enabled');
1141
      ena_outport = self.interrupt_ena_outport;
1142
      self.EmitPush(fp,self.OutportAddress(ena_outport),ena_outport);
1143
      self.EmitOpcode(fp,self.specialInstructions['return'],'return');
1144
      self.EmitOpcode(fp,self.InstructionOpcode('outport'),'outport (.ena)');
1145 2 sinclairrf
    # .store
1146
    elif token['value'] == '.store':
1147
      name = token['argument'][0]['value'];
1148
      ixBank = self.Emit_GetBank(name);
1149
      self.EmitOpcode(fp,self.specialInstructions['store'] | ixBank,'store '+name);
1150
    # .store+
1151
    elif token['value'] == '.store+':
1152
      name = token['argument'][0]['value'];
1153
      ixBank = self.Emit_GetBank(name);
1154
      self.EmitOpcode(fp,self.specialInstructions['store+'] | ixBank,'store+ '+name);
1155
    # .store-
1156
    elif token['value'] == '.store-':
1157
      name = token['argument'][0]['value'];
1158
      ixBank = self.Emit_GetBank(name);
1159
      self.EmitOpcode(fp,self.specialInstructions['store-'] | ixBank,'store- '+name);
1160 3 sinclairrf
    # user-defined macro
1161
    elif token['value'] in self.EmitFunction:
1162
      self.EmitFunction[token['value']](self,fp,token['argument']);
1163 2 sinclairrf
    # error
1164
    else:
1165
      raise Exception('Program Bug -- Unrecognized macro "%s"' % token['value']);
1166
 
1167
  def EmitProgram(self,fp):
1168
    """
1169
    Write the program to the metacode file.\n
1170
    The frist line for the program has the format
1171
      :program address_main address_interrupt
1172
    where
1173
      address_main      is the address of the .main function (this should be 0)
1174
      address_interrupt is either the address of the optional interrupt
1175
                        function if it was defined or the 2-character string
1176
                        '[]'\n
1177
    The subsequent lines are sequences of
1178
      - function_name   indicates the start of a new function body and the name
1179
                        of the function
1180
      instructions      is multiple lines, one for each instruction in the
1181
                        function\n
1182
    The formats of the instruction lines are as follows:
1183
      value string      value is the next instruction to store and string is an
1184
                        optional string describing the instruction
1185
                        Note:  "value" must be a 3-digit hex string
1186
                               representing a 9-bit value
1187
                        Note:  The only place string should be empty is when
1188
                               pushing the 8 lsb of an address onto the start
1189
                               prior to a call, callc, jump, or jumpc
1190
                               instruction
1191
      p name            the single 'p' means that the name of a parameter and
1192
                        its range are to be converted into an instruction
1193
    """
1194
    # Write the program marker, address of .main, address or "[]" of .interrupt,
1195
    # and the total program length.
1196
    fp.write(':program');
1197
    if self.interrupt:
1198 12 sinclairrf
      fp.write(' %d' % self.functionEvaluation['address'][1]);  # .main
1199
      fp.write(' %d' % self.functionEvaluation['address'][0]);  # .interrupt
1200 2 sinclairrf
    else:
1201 12 sinclairrf
      fp.write(' %d' % self.functionEvaluation['address'][0]);  # .main
1202
      fp.write(' []');                                          # no .interrupt
1203 2 sinclairrf
    fp.write(' %d' % (self.functionEvaluation['address'][-1] + self.functionEvaluation['length'][-1]));
1204
    fp.write('\n');
1205
    # Emit the bodies
1206 12 sinclairrf
    if self.interrupt:
1207
      self.emitLabelList = '';
1208
      mainAddress = self.functionEvaluation['address'][1];
1209
      self.EmitPush(fp,mainAddress & 0xFF,name='');
1210
      self.EmitOpcode(fp,self.specialInstructions['jump'] | (mainAddress >> 8),'jump .main');
1211
      self.EmitOpcode(fp,self.InstructionOpcode('nop'),'nop');
1212 2 sinclairrf
    for ix in range(len(self.functionEvaluation['list'])):
1213
      fp.write('- %s\n' % self.functionEvaluation['list'][ix]);
1214
      self.emitLabelList = '';
1215
      for token in self.functionEvaluation['body'][ix]:
1216
        if token['type'] == 'value':
1217
          self.EmitPush(fp,token['value'],tokenLoc=token['loc']);
1218
        elif token['type'] == 'label':
1219
          self.Emit_AddLabel(token['value']);
1220
        elif token['type'] == 'constant':
1221
          if not self.IsSymbol(token['value']):
1222
            raise Exception('Program Bug');
1223
          ix = self.symbols['list'].index(token['value']);
1224
          body = self.symbols['body'][ix];
1225
          self.EmitPush(fp,body[-1],token['value'],tokenLoc=token['loc']);
1226
          for v in body[-2::-1]:
1227
            self.EmitPush(fp,v,tokenLoc=token['loc']);
1228
        elif token['type'] in ('inport','outport','outstrobe',):
1229
          if not self.IsSymbol(token['value']):
1230
            raise Exception('Program Bug');
1231
          ix = self.symbols['list'].index(token['value']);
1232
          self.EmitPush(fp,self.symbols['body'][ix],token['value'],tokenLoc=token['loc']);
1233
        elif token['type'] == 'instruction':
1234
          self.EmitOpcode(fp,self.InstructionOpcode(token['value']),token['value']);
1235
        elif token['type'] == 'macro':
1236
          self.EmitMacro(fp,token);
1237
        elif token['type'] == 'parameter':
1238
          self.EmitParameter(fp,token);
1239
        elif token['type'] == 'symbol':
1240
          self.EmitPush(fp,token['value'],token['name'],tokenLoc=token['loc']);
1241
        elif token['type'] == 'variable':
1242
          self.EmitVariable(fp,token['value']);
1243
        else:
1244
          raise Exception('Program Bug:  Unrecognized type "%s"' % token['type']);
1245
 
1246
  ################################################################################
1247
  #
1248
  # Initialize the object.
1249
  #
1250
  ################################################################################
1251
 
1252 12 sinclairrf
  def __init__(self,enableInterrupts):
1253 2 sinclairrf
    """
1254
    Initialize the tables definining the following:
1255
      directly invokable instruction mnemonics and the associated opcodes
1256
      indirectly inivoked instruction mnemonics and the associated opcodes
1257
        Note:  These are accessed through macros since they require an argument
1258
               or are part of multi-instruction sequences.
1259
      directives (other than ".include")
1260
      macros with type restrictions for required arguments and defaults and
1261
        restrictions for optional arguments\n
1262
    Initialize lists and members to record memory attributes, stack lengths,
1263
    body of the .main function, body of the optional .interrupt function,
1264 12 sinclairrf
    current memory for variable definitions, etc.\n
1265
    If enableInterrupts is True, then also define the ".ena", ".dis", and
1266
    ".returni" macros to enable and disable interrupts and to return from the
1267
    interrupt handler.
1268 2 sinclairrf
    """
1269
 
1270
    #
1271 3 sinclairrf
    # Enumerate the directives
1272
    # Note:  The ".include" directive is handled within asmDef.FileBodyIterator.
1273
    #
1274
 
1275
    self.directives = dict();
1276
 
1277
    self.directives['list']= list();
1278
    self.directives['list'].append('.constant');
1279 9 sinclairrf
    self.directives['list'].append('.define');
1280 3 sinclairrf
    self.directives['list'].append('.function');
1281
    self.directives['list'].append('.interrupt');
1282
    self.directives['list'].append('.macro');
1283
    self.directives['list'].append('.main');
1284
    self.directives['list'].append('.memory');
1285
    self.directives['list'].append('.variable');
1286
 
1287
    #
1288 2 sinclairrf
    # Configure the instructions.
1289
    #
1290
 
1291
    self.instructions = dict(list=list(), opcode=list());
1292
    self.AddInstruction('&',            0x050);
1293
    self.AddInstruction('+',            0x018);
1294 7 sinclairrf
    self.AddInstruction('+c',           0x00B);
1295 2 sinclairrf
    self.AddInstruction('-',            0x01C);
1296
    self.AddInstruction('-1<>',         0x023);
1297
    self.AddInstruction('-1=',          0x022);
1298 7 sinclairrf
    self.AddInstruction('-c',           0x00F);
1299 2 sinclairrf
    self.AddInstruction('0<>',          0x021);
1300
    self.AddInstruction('0=',           0x020);
1301
    self.AddInstruction('0>>',          0x004);
1302
    self.AddInstruction('1+',           0x058);
1303
    self.AddInstruction('1-',           0x05C);
1304
    self.AddInstruction('1>>',          0x005);
1305
    self.AddInstruction('<<0',          0x001);
1306
    self.AddInstruction('<<1',          0x002);
1307
    self.AddInstruction('<<msb',        0x003);
1308
    self.AddInstruction('>r',           0x040);
1309
    self.AddInstruction('^',            0x052);
1310
    self.AddInstruction('drop',         0x054);
1311
    self.AddInstruction('dup',          0x008);
1312
    self.AddInstruction('inport',       0x030);
1313
    self.AddInstruction('lsb>>',        0x007);
1314
    self.AddInstruction('msb>>',        0x006);
1315
    self.AddInstruction('nip',          0x053);
1316
    self.AddInstruction('nop',          0x000);
1317
    self.AddInstruction('or',           0x051);
1318
    self.AddInstruction('outport',      0x038);
1319
    self.AddInstruction('over',         0x00A);
1320
    self.AddInstruction('r>',           0x049);
1321
    self.AddInstruction('r@',           0x009);
1322
    self.AddInstruction('swap',         0x012);
1323
 
1324
    self.specialInstructions = dict();
1325
    self.specialInstructions['call']    = 0x0C0;
1326
    self.specialInstructions['callc']   = 0x0E0;
1327
    self.specialInstructions['fetch']   = 0x068;
1328
    self.specialInstructions['fetch+']  = 0x078;
1329
    self.specialInstructions['fetch-']  = 0x07C;
1330
    self.specialInstructions['jump']    = 0x080;
1331
    self.specialInstructions['jumpc']   = 0x0A0;
1332
    self.specialInstructions['return']  = 0x028;
1333
    self.specialInstructions['store']   = 0x060;
1334
    self.specialInstructions['store+']  = 0x070;
1335
    self.specialInstructions['store-']  = 0x074;
1336
 
1337
    #
1338
    # Configure the pre-defined macros
1339
    # Note:  'symbol' is a catch-call for functions, labels, variables, etc.
1340
    #        These are restricted to the appropriate types when the macros are
1341
    #        expanded.
1342
    #
1343
 
1344 11 sinclairrf
    self.macros = dict(list=list(), length=list(), args=list(), nArgs=list(), builtIn = list(), doc = list());
1345 3 sinclairrf
    self.EmitFunction = dict();
1346
 
1347
    # Macros built in to the assembler (to access primitives).
1348 2 sinclairrf
    self.AddMacro('.call',              3, [
1349
                                             ['','symbol'],
1350
                                             ['nop','instruction','singlemacro','singlevalue','symbol']
1351
                                           ]);
1352
    self.AddMacro('.callc',             3, [
1353
                                             ['','symbol'],
1354
                                             ['drop','instruction','singlevalue','symbol']
1355
                                           ]);
1356
    self.AddMacro('.fetch',             1, [ ['','symbol'] ]);
1357
    self.AddMacro('.fetch+',            1, [ ['','symbol'] ]);
1358
    self.AddMacro('.fetch-',            1, [ ['','symbol'] ]);
1359
    self.AddMacro('.jump',              3, [
1360
                                             ['','symbol'],
1361
                                             ['nop','instruction','singlemacro','singlevalue','symbol']
1362
                                           ]);
1363
    self.AddMacro('.jumpc',             3, [
1364
                                             ['','symbol'],
1365
                                             ['drop','instruction','singlemacro','singlevalue','symbol']
1366
                                           ]);
1367 4 sinclairrf
    self.AddMacro('.return',            2, [ ['nop','instruction','singlemacro','singlevalue','symbol'] ]);
1368 2 sinclairrf
    self.AddMacro('.store',             1, [ ['','symbol'] ]);
1369
    self.AddMacro('.store+',            1, [ ['','symbol'] ]);
1370
    self.AddMacro('.store-',            1, [ ['','symbol'] ]);
1371
 
1372 3 sinclairrf
    # User-defined macros in ./macros that are "built in" to the assembler.
1373
    macroSearchPath = os.path.join(sys.path[0],'macros');
1374
    for macroName in os.listdir(macroSearchPath):
1375
      if not re.match(r'.*\.py$',macroName):
1376
        continue;
1377
      self.AddUserMacro(macroName[:-3],macroSearchPaths=[macroSearchPath]);
1378
    for macroName in self.macros['list']:
1379
      self.macros['builtIn'].append(macroName);
1380
 
1381 2 sinclairrf
    #
1382
    # List the macros that have special symbols for their first argument.
1383
    #
1384
 
1385
    self.MacrosWithSpecialFirstSymbol = ('.call','.callc','.jump','.jumpc',);
1386
 
1387
    #
1388
    # Externally defined parameters.
1389
    #
1390
 
1391
    self.memoryLength = dict();
1392
    self.stackLength = dict();
1393
 
1394
    #
1395 12 sinclairrf
    # Conditional implementation of interrupts.
1396
    #
1397
 
1398
    if type(enableInterrupts) != bool:
1399
      raise Exception('Program Bug -- enableInterrupts must be a boolean');
1400
    self.interruptsEnabled = enableInterrupts;
1401
    if enableInterrupts:
1402
      self.interrupt_dis_outport = 'O_INTERRUPT_DIS';
1403
      self.interrupt_ena_outport = 'O_INTERRUPT_ENA';
1404
      self.AddMacro('.dis',             2, []);
1405
      self.AddMacro('.ena',             2, []);
1406
      self.AddMacro('.returni',         3, []);
1407
 
1408
    #
1409 2 sinclairrf
    # Configure the containers for the expanded main, interrupt, function,
1410
    # macro, etc. definitions.
1411
    #
1412
 
1413 3 sinclairrf
    self.currentMemory = None;
1414 2 sinclairrf
    self.interrupt = None;
1415
    self.main = None;
1416 3 sinclairrf
    self.macroSearchPaths = ['.','./macros'];
1417 2 sinclairrf
    self.symbols = dict(list=list(), type=list(), body=list());

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.