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

Subversion Repositories ssbcc

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

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