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

Subversion Repositories ssbcc

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

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 sinclairrf
################################################################################
2
#
3 3 sinclairrf
# Copyright 2012-2014, 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
    # Ensure functions and interrupts end in a ".jump" or ".return".
443
    if firstToken['value'] in ('.function','.interrupt',):
444
      if (lastToken['type'] != 'macro') or (lastToken['value'] not in ('.jump','.return',)):
445
        raise asmDef.AsmException('Last entry in ".function" or ".interrupt" must be a ".jump" or ".return" at %s' % lastToken['loc']);
446 2 sinclairrf
 
447
  ################################################################################
448
  #
449
  # fill in symbols, etc. in the list of raw tokens.
450
  #
451
  ################################################################################
452
 
453
  def ByteList(self,rawTokens,limit=False):
454
    """
455
    Return either (1) a list comprised of a single token which may not be a
456
    byte or (2) a list comprised of multiple tokens, each of which is a single
457
    byte.\n
458 9 sinclairrf
    Note:  This is called by FillRawTokens.\n
459
    Note:  Multi-value lists must be single-byte values (i.e., in the range -128 to 255)
460 2 sinclairrf
    """
461
    if len(rawTokens) > 1:
462
      limit = True;
463
    values = list();
464 9 sinclairrf
    for token in rawTokens:
465
      if token['type'] == 'symbol':
466
        ix = self.symbols['list'].index(token['value']);
467
        symbolType = self.symbols['type'][ix];
468
        if symbolType != 'constant':
469
          raise asmDef.AsmException('Illegal symbol "%s" at %s' % (token['value'],token['loc'],));
470
        value = self.symbols['body'][ix];
471
      elif token['type'] == 'value':
472
        value = token['value'];
473
      else:
474
        raise asmDef.AsmException('Illegal token "%s" with value "%s" at %s' % (token['type'],token['value'],token['loc'],));
475
      if type(value) == int:
476
        value = [value];
477
      else:
478
        limit = True;
479
      for v in value:
480
        if limit and not (-128 <= v < 256):
481
          raise asmDef.AsmException('Out-of-rarnge value "%d" at %s' % (v,token['loc'],))
482
        values.append(v);
483 2 sinclairrf
    return values;
484
 
485
  def ExpandSymbol(self,token,singleValue):
486
    """
487
    Convert the token for a symbol into a token for its specific type.
488
    Optionally ensure constants expand to a single byte.  For parameters,
489
    ensure that a range is provided.\n
490
    Note:  Symbols must be defined before the directive bodies in which they
491
           are used.\n
492
    Note:  This is called in two spots.  The first is ExpandTokens, where
493
           isolated symbols are processed, for example to get the value of a
494
           constant.  The second is in EmitOptArg where symbols in arguments to
495
           macros are expanded (this allows the macro-specific processing to
496
           identify labels vs. symbols).
497
    """
498
    if not self.IsSymbol(token['value']):
499
      raise asmDef.AsmException('Symbol "%s" not in symbol list at %s' %(token['value'],token['loc'],));
500
    ix = self.symbols['list'].index(token['value']);
501
    symbolType = self.symbols['type'][ix];
502
    if symbolType == 'RAM':
503
      return dict(type='RAM', value=token['value'], loc=token['loc']);
504
    elif symbolType == 'ROM':
505
      return dict(type='ROM', value=token['value'], loc=token['loc']);
506
    elif symbolType == 'constant':
507
      if singleValue:
508
        thisBody = self.symbols['body'][ix];
509
        if len(thisBody) != 1:
510
          raise asmDef.AsmException('Constant "%s" must evaluate to a single byte at %s' % (token['value'],token['loc'],))
511
        thisBody = thisBody[0];
512
        if not (-128 <= thisBody < 256):
513
          raise asmDef.AsmException('Constant "%s" must be a byte value at %s' % (token['value'],token['loc'],));
514
      return dict(type='constant', value=token['value'], loc=token['loc']);
515
    elif symbolType == 'inport':
516
      return dict(type='inport', value=token['value'], loc=token['loc']);
517
    elif symbolType == 'outport':
518
      return dict(type='outport', value=token['value'], loc=token['loc']);
519
    elif symbolType == 'outstrobe':
520
      return dict(type='outstrobe', value=token['value'], loc=token['loc']);
521
    elif symbolType == 'parameter':
522
      if 'range' in token:
523
        trange = token['range'];
524
      else:
525
        trange = '[0+:8]';
526
      return dict(type='parameter', value=token['value'], range=trange, loc=token['loc']);
527
    elif symbolType == 'variable':
528
      return dict(type='variable', value=token['value'], loc=token['loc']);
529
    else:
530
      raise Exception('Program Bug -- unrecognized symbol type "%s"' % symbolType);
531
 
532
  def ExpandTokens(self,rawTokens):
533
    """
534
    Compute the relative addresses for tokens within function bodies.\n
535
    The return is a list of the tokens in the function body, each of which has
536
    a type, value, offset (relative address), and location within the source
537
    code.  Macro types also have the list of arguments provided to the macro.
538
    """
539
    tokens = list();
540
    offset = 0;
541
    for token in rawTokens:
542
      # insert labels
543
      if token['type'] == 'label':
544
        tokens.append(dict(type=token['type'], value=token['value'], offset=offset, loc=token['loc']));
545
        # labels don't change the offset
546
      # append instructions
547
      elif token['type'] == 'instruction':
548
        tokens.append(dict(type=token['type'], value=token['value'], offset=offset, loc=token['loc']));
549
        offset = offset + 1;
550
      # append values
551
      elif token['type'] == 'value':
552
        if type(token['value']) == int:
553
          tokens.append(dict(type=token['type'], value=token['value'], offset=offset, loc=token['loc']));
554
          offset = offset + 1;
555
        else:
556
          revTokens = copy.copy(token['value']);
557
          revTokens.reverse();
558
          for lToken in revTokens:
559
            tokens.append(dict(type=token['type'], value=lToken, offset=offset, loc=token['loc']));
560
            offset = offset + 1;
561
      # append macros
562
      elif token['type'] == 'macro':
563
        tokens.append(dict(type=token['type'], value=token['value'], offset=offset, argument=token['argument'], loc=token['loc']));
564
        offset = offset + self.MacroLength(token);
565
      # interpret and append symbols
566
      elif token['type'] == 'symbol':
567
        newToken = self.ExpandSymbol(token,singleValue=False);
568
        newToken['offset'] = offset;
569
        newToken['loc'] = token['loc'];
570
        tokens.append(newToken);
571
        if token['type'] == 'constant':
572
          ix = self.symbols['list'].index(newToken['value']);
573
          offset = offset + len(self.symbols['body'][ix]);
574
        else:
575
          offset = offset + 1;
576
      # anything else is a program bug
577
      else:
578
        raise Exception('Program bug:  unexpected token type "%s"' % token['type']);
579
    return dict(tokens=tokens, length=offset);
580
 
581
  def FillRawTokens(self,rawTokens):
582
    """
583
    Do one of the following as required for the specified directive:
584
      .constant         add the constant and its body to the list of symbols
585
      .function         add the function and its body, along with the relative
586
                        addresses, to the list of symbols
587
      .interrupt        record the function body and relative addresses
588 3 sinclairrf
      .macro            register the user-defined macro
589 2 sinclairrf
      .main             record the function body and relative addresses
590
      .memory           record the definition of the memory and make it current
591
                        for subsequent variable definitions.
592
      .variable         add the variable and its associated memory, length, and
593
                        initial values to the list of symbols
594
    """
595
    firstToken = rawTokens[0];
596
    secondToken = rawTokens[1];
597
    # Perform syntax check common to several directives.
598
    if firstToken['value'] in ('.constant','.function','.variable',):
599
      if secondToken['type'] != 'symbol':
600
        raise asmDef.AsmException('Expected symbol, not "%s", at %s' % (secondToken['value'],secondToken['loc'],));
601
      if self.IsSymbol(secondToken['value']):
602
        raise asmDef.AsmException('Symbol "%s" already defined at %s' % (secondToken['value'],secondToken['loc'],));
603
    # Perform syntax-specific processing.
604
    if firstToken['value'] == '.constant':
605
      byteList = self.ByteList(rawTokens[2:]);
606
      self.AddSymbol(secondToken['value'],'constant',body=byteList);
607 9 sinclairrf
    # Process ".define" directive
608
    elif firstToken['value'] == '.define':
609
      self.AddSymbol(secondToken['value'],'define');
610 2 sinclairrf
    # Process ".function" definition.
611
    elif firstToken['value'] == '.function':
612
      self.AddSymbol(secondToken['value'],'function',self.ExpandTokens(rawTokens[2:]));
613
    # Process ".interrupt" definition.
614
    elif firstToken['value'] == '.interrupt':
615
      if self.interrupt:
616
        raise asmDef.AsmException('Second definition of ".interrupt" at %s' % firstToken['loc']);
617
      self.interrupt = self.ExpandTokens(rawTokens[1:]);
618 3 sinclairrf
    # Process user-defined macros (the ".macro XXX" directive can be repeated for non-built-in macros).
619
    elif firstToken['value'] == '.macro':
620
      macroName = secondToken['value'];
621
      fullMacroName = '.' + macroName;
622
      if fullMacroName in self.directives:
623
        raise asmDef.AsmException('Macro "%s" is a directive at %s' % (fullMacroName,secondToken['loc'],));
624
      if fullMacroName in self.instructions:
625
        raise asmDef.AsmException('Macro "%s" is an instruction at %s' % (fullMacroName,secondToken['loc'],));
626
      if self.IsBuiltInMacro(fullMacroName):
627
        raise asmDef.AsmException('Macro "%s" is a built-in macro at %s' % (fullMacroName,secondToken['loc'],));
628
      if fullMacroName not in self.macros['list']:
629
        self.AddUserMacro(macroName);
630 2 sinclairrf
    # Process ".main" definition.
631
    elif firstToken['value'] == '.main':
632
      if self.main:
633
        raise asmDef.AsmException('Second definition of ".main" at %s' % firstToken['loc']);
634
      self.main = self.ExpandTokens(rawTokens[1:]);
635
    # Process ".memory" declaration.
636
    elif firstToken['value'] == '.memory':
637
      if len(rawTokens) != 3:
638
        raise asmDef.AsmException('".memory" directive requires exactly two arguments at %s' % firstToken['loc']);
639
      if (secondToken['type'] != 'symbol') or (secondToken['value'] not in ('RAM','ROM',)):
640
        raise asmDef.AsmException('First argument to ".memory" directive must be "RAM" or "RAM" at %s' % secondToken['loc']);
641
      thirdToken = rawTokens[2];
642
      if thirdToken['type'] != 'symbol':
643
        raise asmDef.AsmException('".memory" directive requires name for second argument at %s' % thirdToken['loc']);
644
      if self.IsSymbol(thirdToken['value']):
645
        ix = self.symbols['list'].index(thirdToken['value']);
646
        if self.symbols['type'] != secondToken['value']:
647
          raise asmDef.AsmException('Redefinition of ".memory %s %s" not allowed at %s' % (secondToken['value'],thirdToken['value'],firstToken['loc']));
648
      else:
649
        self.AddSymbol(thirdToken['value'],secondToken['value'],dict(length=0));
650
      self.currentMemory = thirdToken['value'];
651
    # Process ".variable" declaration.
652
    elif firstToken['value'] == '.variable':
653
      if not self.currentMemory:
654
        raise asmDef.AsmException('".memory" directive required before ".variable" directive at %s' % firstToken['line']);
655
      ixMem = self.symbols['list'].index(self.currentMemory);
656
      currentMemoryBody = self.symbols['body'][ixMem];
657
      byteList = self.ByteList(rawTokens[2:],limit=True);
658
      body = dict(memory=self.currentMemory, start=currentMemoryBody['length'], value=byteList);
659
      self.AddSymbol(secondToken['value'], 'variable', body=body);
660
      currentMemoryBody['length'] = currentMemoryBody['length'] + len(byteList);
661
      if currentMemoryBody['length'] > 256:
662
        raise asmDef.AsmException('Memory "%s" becomes too long at %s' % (self.currentMemory,firstToken['loc']));
663
    # Everything else is an error.
664
    else:
665
      raise Exception('Program Bug:  Unrecognized directive %s at %s' % (firstToken['value'],firstToken['loc']));
666
 
667
  def Main(self):
668
    """
669
    Return the body of the .main function.
670
    Note:  This is used by the top-level assembler to verify that the .main
671
           function has been defined.
672
    """
673
    return self.main;
674
 
675
  def Interrupt(self):
676
    """
677
    Return the body of the .interrupt function.
678
    Note:  This is used by the top-level assembler to verify that the .interrupt
679
           function has or has not been defined.
680
    """
681
    return self.interrupt;
682
 
683
  ################################################################################
684
  #
685
  # Compute the memory bank indices.
686
  #
687
  ################################################################################
688
 
689
  def EvaluateMemoryTree(self):
690
    """
691
    Ensure defined memories are used.  Add the memory name, type, and length to
692
    the list of memories.  Compute the bank index ascending from 0 for RAMs and
693
    descending from 3 for ROMs and add that index to the memory attributes.
694
    Ensure that no more than 4 memories are listed.
695
    """
696
    self.memories = dict(list=list(), type=list(), length=list(), bank=list());
697
    ramBank = 0;
698
    romBank = 3;
699
    for ix in range(len(self.symbols['list'])):
700
      if self.symbols['type'][ix] in ('RAM','ROM',):
701
        memBody = self.symbols['body'][ix];
702
        if memBody['length'] == 0:
703
          raise asmDef.AsmException('Empty memory:  %s' % self.symbols['list'][ix]);
704
        self.memories['list'].append(self.symbols['list'][ix]);
705
        self.memories['type'].append(self.symbols['type'][ix]);
706
        self.memories['length'].append(memBody['length']);
707
        if self.symbols['type'][ix] == 'RAM':
708
          self.memories['bank'].append(ramBank);
709
          ramBank = ramBank + 1;
710
        else:
711
          self.memories['bank'].append(romBank);
712
          romBank = romBank - 1;
713
    if len(self.memories['list']) > 4:
714
      raise asmDef.AsmException('Too many memory banks');
715
 
716
  ################################################################################
717
  #
718
  # Generate the list of required functions from the ".main" and ".interrupt"
719
  # bodies.
720
  #
721
  # Look for function calls with the bodies of the required functions.  If the
722
  # function has not already been identified as a required function then (1)
723
  # ensure it exists and is a function and then (2) add it to the list of
724
  # required functions.
725
  #
726
  # Whenever a function is added to the list, set its start address and get its
727
  # length.
728
  #
729
  ################################################################################
730
 
731
  def EvaluateFunctionTree(self):
732
    """
733
    Create a list of the functions required by the program, starting with the
734
    required .main function and the optional .interrupt function.\n
735
    Record the length of each function, its body, and its start address and
736
    calculate the addresses of the labels within each function body.\n
737
    Finally, ensure the function address space does not exceed the absolute
738
    8192 address limit.
739
    """
740
    self.functionEvaluation = dict(list=list(), length=list(), body=list(), address=list());
741
    nextStart = 0;
742
    # ".main" is always required.
743
    self.functionEvaluation['list'].append('.main');
744
    self.functionEvaluation['length'].append(self.main['length']);
745
    self.functionEvaluation['body'].append(self.main['tokens']);
746
    self.functionEvaluation['address'].append(nextStart);
747
    nextStart = nextStart + self.functionEvaluation['length'][-1];
748
    # ".interrupt" is optionally required (and is sure to exist by this function
749
    # call if it is required).
750
    if self.interrupt:
751
      self.functionEvaluation['list'].append('.interrupt');
752
      self.functionEvaluation['length'].append(self.interrupt['length']);
753
      self.functionEvaluation['body'].append(self.interrupt['tokens']);
754
      self.functionEvaluation['address'].append(nextStart);
755
      nextStart = nextStart + self.functionEvaluation['length'][-1];
756
    # Loop through the required function bodies as they are identified.
757
    ix = 0;
758
    while ix < len(self.functionEvaluation['body']):
759
      for token in self.functionEvaluation['body'][ix]:
760
        if (token['type'] == 'macro') and (token['value'] in ('.call','.callc',)):
761
          callName = token['argument'][0]['value'];
762
          if callName not in self.functionEvaluation['list']:
763
            if not self.IsSymbol(callName):
764
              raise asmDef.AsmException('Function "%s" not defined for function "%s"' % (callName,self.functionEvaluation['list'][ix],));
765
            ixName = self.symbols['list'].index(callName);
766
            if self.symbols['type'][ixName] != 'function':
767 9 sinclairrf
              raise asmDef.AsmException('Function "%s" called by "%s" is not a function' % (callName, self.functionEvaluation['list'][ix],));
768 2 sinclairrf
            self.functionEvaluation['list'].append(callName);
769
            self.functionEvaluation['length'].append(self.symbols['body'][ixName]['length']);
770
            self.functionEvaluation['body'].append(self.symbols['body'][ixName]['tokens']);
771
            self.functionEvaluation['address'].append(nextStart);
772
            nextStart = nextStart + self.functionEvaluation['length'][-1];
773
      ix = ix + 1;
774
    # Within each function, compute the list of label addresses and then fill in
775
    # the address for all jumps and calls.
776
    for ix in range(len(self.functionEvaluation['list'])):
777
      startAddress = self.functionEvaluation['address'][ix];
778
      labelAddress = dict(list=list(), address=list());
779
      for token in self.functionEvaluation['body'][ix]:
780
        if token['type'] == 'label':
781
          labelAddress['list'].append(token['value']);
782
          labelAddress['address'].append(startAddress + token['offset']);
783
      for token in self.functionEvaluation['body'][ix]:
784
        if token['type'] != 'macro':
785
          continue;
786
        if token['value'] in ('.jump','.jumpc',):
787
          ix = labelAddress['list'].index(token['argument'][0]['value']);
788
          token['address'] = labelAddress['address'][ix];
789
        elif token['value'] in ('.call','.callc',):
790
          ix = self.functionEvaluation['list'].index(token['argument'][0]['value']);
791
          token['address'] = self.functionEvaluation['address'][ix];
792
    # Sanity checks for address range
793
    if self.functionEvaluation['address'][-1] + self.functionEvaluation['length'][-1] >= 2**13:
794
      raise asmDef.AsmException('Max address for program requires more than 13 bits');
795
 
796
  ################################################################################
797
  #
798
  # Emit the meta code for the memories.
799
  #
800
  ################################################################################
801
 
802
  def EmitMemories(self,fp):
803
    """
804
    Print the memories to the metacode file.\n
805
    The first line for each memory has the format
806
      :memory type mem_name bank length
807
    where
808
      type              is RAM or ROM
809
      mem_name          is the name of the memory
810
      bank              is the assigned bank address
811
      length            is the number of bytes used by the memory\n
812
    The subsequent lines are sequences of
813
      - variable_name
814
      value(s)
815
    where
816
      '-'               indicates a variable name is present
817
      variable_name     is the name of the variable
818
      values(s)         is one or more lines for the values with one byte per line
819
                        Note:  because the lines with variable names start with
820
                               '-', negative values are converted to unsigned
821
                               values\n
822
    """
823
    # Emit the individual memories.
824
    for ixMem in range(len(self.memories['list'])):
825
      fp.write(':memory %s %s %d %d\n' % (self.memories['type'][ixMem],self.memories['list'][ixMem],self.memories['bank'][ixMem],self.memories['length'][ixMem]));
826
      memName = self.memories['list'][ixMem];
827
      address = 0;
828
      for ixSymbol in range(len(self.symbols['list'])):
829
        if self.symbols['type'][ixSymbol] != 'variable':
830
          continue;
831
        vBody = self.symbols['body'][ixSymbol];
832
        if vBody['memory'] != memName:
833
          continue;
834
        fp.write('- %s\n' % self.symbols['list'][ixSymbol]);
835
        for v in vBody['value']:
836
          if not (-128 <=v < 256):
837
            raise Exception('Program Bug -- value not representable by a byte');
838
          fp.write('%02X\n' % (v % 0x100,));
839
      fp.write('\n');
840
 
841
  ################################################################################
842
  #
843
  # Emit the metacode for the program.
844
  #
845
  ################################################################################
846
 
847
  #
848
  # Utilities for building opcodes or the associated description strings.
849
  #
850
  # Note:  These utilities do not write to the metacode file.
851
  #
852
 
853
  def Emit_AddLabel(self,name):
854
    """
855
    Append the label to the labels associated with the current program address.
856
    """
857
    self.emitLabelList += ':' + name + ' ';
858
 
859
  def Emit_EvalSingleValue(self,token):
860
    """
861
    Evaluate the optional single-byte value for a macro.
862
    """
863
    if token['type'] == 'symbol':
864
      token = self.ExpandSymbol(token,singleValue=True);
865
    if token['type'] == 'constant':
866
      name = token['value'];
867
      if not self.IsSymbol(name):
868
        raise Exception('Program Bug');
869
      ix = self.symbols['list'].index(name);
870
      if len(self.symbols['body'][ix]) != 1:
871
        raise asmDef.AsmException('Optional constant can only be one byte at %s' % token['loc']);
872
      return self.symbols['body'][ix][0]
873
    elif token['type'] == 'value':
874
      return token['value']
875
    else:
876
      raise asmDef.AsmException('Unrecognized optional argument "%s"' % token['value']);
877
 
878 5 sinclairrf
  def Emit_GetAddrAndBank(self,token):
879 2 sinclairrf
    """
880
    For the specified variable, return an ordered tuple of the memory address
881
    within its bank, the corresponding bank index, and the corresponding bank
882
    name.\n
883 3 sinclairrf
    Note:  This is used by several user-defined macros that fetch from or store
884
           to variables.
885 2 sinclairrf
    """
886 5 sinclairrf
    name = token['value'];
887 2 sinclairrf
    if not self.IsSymbol(name):
888 5 sinclairrf
      raise asmDef.AsmException('"%s" is not a recognized symbol at %s' % (name,token['loc'],));
889 2 sinclairrf
    ixName = self.symbols['list'].index(name);
890
    if self.symbols['type'][ixName] != 'variable':
891 5 sinclairrf
      raise asmDef.AsmException('"%s" is not a variable at %s' % (name,token['loc'],));
892 2 sinclairrf
    body = self.symbols['body'][ixName];
893
    bankName = body['memory'];
894
    ixMem = self.memories['list'].index(bankName);
895
    return (body['start'],self.memories['bank'][ixMem],bankName,);
896
 
897
  def Emit_GetBank(self,name):
898
    """
899
    For the specified variable, return the memory bank index.\n
900 3 sinclairrf
    Note:  This is used by the .fetch, .fetch+, .fetch-, .store, .store+, and
901 2 sinclairrf
           .store- macros.
902
    """
903
    if name not in self.memories['list']:
904
      raise asmDef.AsmException('"%s" not a memory' % name);
905
    ixMem = self.memories['list'].index(name);
906
    return self.memories['bank'][ixMem];
907
 
908
  def Emit_String(self,name=''):
909
    """
910
    Append the specified string to the list of labels for the current
911
    instruction, restart the list of labels, and return the composite string.
912
    """
913
    name = self.emitLabelList + name;
914
    self.emitLabelList = '';
915
    return name;
916
 
917 4 sinclairrf
  def Emit_IntegerValue(self,token):
918
    """
919
    Return the integer value associated with a constant or a numeric expression.
920
    """
921
    if token['type'] == 'value':
922
      v = token['value'];
923
    elif token['type'] == 'symbol':
924
      name = token['value'];
925
      if not self.IsSymbol(name):
926
        raise asmDef.AsmException('Symbol "%s" not recognized at %s' % (token['value'],token['loc'],));
927
      ix = self.symbols['list'].index(name);
928
      v = self.symbols['body'][ix];
929
      if len(v) != 1:
930
        raise asmDef.AsmException('Argument can only be one value at %s' % token['loc']);
931
      v = v[0];
932
    else:
933
      raise asmDef.AsmException('Argument "%s" of type "%s" not recognized at %s' % (token['value'],token['type'],token['loc'],));
934
    if type(v) != int:
935
      raise Exception('Program Bug -- value should be an "int"');
936
    return v;
937
 
938 2 sinclairrf
  #
939
  # Utilities to write single instructions to the metacode file.
940
  #
941
  # Note:  Other than the program header and the function names, these
942
  #        utilities write the function bodies.
943
  #
944
 
945
  def EmitOpcode(self,fp,opcode,name):
946
    """
947
    Write the specified opcode and the associated comment string.\n
948
    The leading bit for an opcode is always a '0'.
949
    """
950
    if not (0 <= opcode < 256):
951
      raise Exception('Program Bug -- opcode "0x%X" out of range');
952
    fp.write('0%02X %s\n' % (opcode,self.Emit_String(name)));
953
 
954
  def EmitParameter(self,fp,token):
955
    """
956
    Write the name (and range) of the specified parameter and the optional
957
    associated comment string.\n
958
    The string 'p' specifies that the parameter is to be inserted into the
959
    instruction body.\n
960
    Note:  The comment string may be the empty string if there were no labels
961
           immediately preceding the parameter.
962
    """
963
    name = token['value'];
964
    if not self.IsParameter(name):
965
      raise Exception('Program Bug');
966
    commentString = self.Emit_String();
967
    if commentString:
968
      fp.write('p %s%s %s\n' % (name,token['range'],commentString,));
969
    else:
970
      fp.write('p %s%s\n' % (name,token['range'],));
971
 
972
  def EmitPush(self,fp,value,name=None,tokenLoc=None):
973
    """
974
    Write the opcode to push a value onto the data stack.  Include the comment
975
    string including either the optionally provided symbol name or a printable
976
    representation of the value being pushed onto the stack.\n
977
    Note:  The printable value is included when a name is not provided so that
978
           the contents of single characters or of strings being pushed onto
979
           the stack can be read.\n
980
    Note:  The token location is an optional input required when the range of
981
           the provided value may not have been previously ensured to fit in
982
           one byte.
983
    """
984
    if not (-128 <= value < 256):
985
      if tokenLoc == None:
986
        raise Exception('Program Bug -- untrapped out-of-range token "%s"' % value);
987
      else:
988
        raise asmDef.AsmException('Value not representable by a byte at "%s"' % tokenLoc);
989
    if value < 0:
990
      value = value + 256;
991
    if type(name) == str:
992
      fp.write('1%02X %s\n' % ((value % 0x100),self.Emit_String(name)));
993
    elif (chr(value) in string.printable) and (chr(value) not in string.whitespace):
994
      fp.write('1%02X %s\n' % ((value % 0x100),self.Emit_String('%02X \'%c\'' % (value,value,))));
995
    else:
996
      fp.write('1%02X %s\n' % ((value % 0x100),self.Emit_String('0x%02X' % value)));
997
 
998
  def EmitVariable(self,fp,name):
999
    """
1000
    Use the EmitPush method to push the address of a variable onto the data
1001
    stack.
1002
    """
1003
    if not self.IsSymbol(name):
1004
      raise asmDef.AsmException('Variable "%s" not recognized' % name);
1005
    ixName = self.symbols['list'].index(name);
1006
    if self.symbols['type'][ixName] != 'variable':
1007
      raise asmDef.AsmException('"%s" is not a variable' % name);
1008
    self.EmitPush(fp,self.symbols['body'][ixName]['start'],name);
1009
 
1010
  #
1011
  # EmitOpcode, EmitMacro, and EmitProgram emit composite or more complicated
1012
  # bodies.
1013
  #
1014
 
1015
  def EmitOptArg(self,fp,token):
1016
    """
1017
    Write the metacode for optional arguments to macros.\n
1018
    These must be single-instruction arguments.
1019
    """
1020
    # Symbols encountered in macros are expanded here instead of the
1021
    # ExpandTokens method -- the code is much simpler this way even though the
1022
    # associated error detection was deferred in the processing.  The symbol
1023
    # must expand to a single value.
1024
    if token['type'] == 'symbol':
1025
      token = self.ExpandSymbol(token,singleValue=True);
1026
    if token['type'] == 'constant':
1027
      name = token['value'];
1028
      if not self.IsSymbol(name):
1029
        raise Exception('Program Bug');
1030
      ix = self.symbols['list'].index(name);
1031
      if len(self.symbols['body'][ix]) != 1:
1032
        raise asmDef.AsmException('Optional constant can only be one byte at %s' % token['loc']);
1033
      self.EmitPush(fp,self.symbols['body'][ix][0],self.Emit_String(name),tokenLoc=token['loc']);
1034
    elif token['type'] in ('inport','outport','outstrobe'):
1035
      name = token['value'];
1036
      if not self.IsSymbol(name):
1037
        raise Exception('Program Bug -- unrecognized inport/outport name "%s"');
1038
      ix = self.symbols['list'].index(name);
1039
      self.EmitPush(fp,self.symbols['body'][ix],self.Emit_String(name));
1040
    elif token['type'] == 'instruction':
1041
      self.EmitOpcode(fp,self.InstructionOpcode(token['value']),token['value']);
1042
    elif token['type'] == 'parameter':
1043
      self.EmitParameter(fp,token);
1044
    elif token['type'] == 'value':
1045
      self.EmitPush(fp,token['value'],tokenLoc=token['loc']);
1046
    elif token['type'] == 'variable':
1047
      self.EmitVariable(fp,token['value']);
1048
    elif token['type'] == 'macro':
1049
      self.EmitMacro(fp,token);
1050
    else:
1051
      raise asmDef.AsmException('Unrecognized optional argument "%s"' % token['value']);
1052
 
1053
  def EmitMacro(self,fp,token):
1054
    """
1055 3 sinclairrf
    Write the metacode for a macro.\n
1056
    The macros coded here are required to access intrinsics.
1057 2 sinclairrf
    """
1058
    # .call
1059
    if token['value'] == '.call':
1060
      self.EmitPush(fp,token['address'] & 0xFF,'');
1061
      self.EmitOpcode(fp,self.specialInstructions['call'] | (token['address'] >> 8),'call '+token['argument'][0]['value']);
1062
      self.EmitOptArg(fp,token['argument'][1]);
1063
    # .callc
1064
    elif token['value'] == '.callc':
1065
      self.EmitPush(fp,token['address'] & 0xFF,'');
1066
      self.EmitOpcode(fp,self.specialInstructions['callc'] | (token['address'] >> 8),'callc '+token['argument'][0]['value']);
1067
      self.EmitOptArg(fp,token['argument'][1]);
1068
    # .fetch
1069
    elif token['value'] == '.fetch':
1070
      name = token['argument'][0]['value'];
1071
      ixBank = self.Emit_GetBank(name);
1072
      self.EmitOpcode(fp,self.specialInstructions['fetch'] | ixBank,'fetch '+name);
1073
    # .fetch+
1074
    elif token['value'] == '.fetch+':
1075
      name = token['argument'][0]['value'];
1076
      ixBank = self.Emit_GetBank(name);
1077
      self.EmitOpcode(fp,self.specialInstructions['fetch+'] | ixBank,'fetch+('+name+')');
1078
    # .fetch-
1079
    elif token['value'] == '.fetch-':
1080
      name = token['argument'][0]['value'];
1081
      ixBank = self.Emit_GetBank(name);
1082
      self.EmitOpcode(fp,self.specialInstructions['fetch-'] | ixBank,'fetch-('+name+')');
1083
    # .jump
1084
    elif token['value'] == '.jump':
1085
      self.EmitPush(fp,token['address'] & 0xFF,'');
1086
      self.EmitOpcode(fp,self.specialInstructions['jump'] | (token['address'] >> 8),'jump '+token['argument'][0]['value']);
1087
      self.EmitOptArg(fp,token['argument'][1]);
1088
    # .jumpc
1089
    elif token['value'] == '.jumpc':
1090
      self.EmitPush(fp,token['address'] & 0xFF,'');
1091
      self.EmitOpcode(fp,self.specialInstructions['jumpc'] | (token['address'] >> 8),'jumpc '+token['argument'][0]['value']);
1092
      self.EmitOptArg(fp,token['argument'][1]);
1093
    # .return
1094
    elif token['value'] == '.return':
1095
      self.EmitOpcode(fp,self.specialInstructions['return'],'return');
1096
      self.EmitOptArg(fp,token['argument'][0]);
1097
    # .store
1098
    elif token['value'] == '.store':
1099
      name = token['argument'][0]['value'];
1100
      ixBank = self.Emit_GetBank(name);
1101
      self.EmitOpcode(fp,self.specialInstructions['store'] | ixBank,'store '+name);
1102
    # .store+
1103
    elif token['value'] == '.store+':
1104
      name = token['argument'][0]['value'];
1105
      ixBank = self.Emit_GetBank(name);
1106
      self.EmitOpcode(fp,self.specialInstructions['store+'] | ixBank,'store+ '+name);
1107
    # .store-
1108
    elif token['value'] == '.store-':
1109
      name = token['argument'][0]['value'];
1110
      ixBank = self.Emit_GetBank(name);
1111
      self.EmitOpcode(fp,self.specialInstructions['store-'] | ixBank,'store- '+name);
1112 3 sinclairrf
    # user-defined macro
1113
    elif token['value'] in self.EmitFunction:
1114
      self.EmitFunction[token['value']](self,fp,token['argument']);
1115 2 sinclairrf
    # error
1116
    else:
1117
      raise Exception('Program Bug -- Unrecognized macro "%s"' % token['value']);
1118
 
1119
  def EmitProgram(self,fp):
1120
    """
1121
    Write the program to the metacode file.\n
1122
    The frist line for the program has the format
1123
      :program address_main address_interrupt
1124
    where
1125
      address_main      is the address of the .main function (this should be 0)
1126
      address_interrupt is either the address of the optional interrupt
1127
                        function if it was defined or the 2-character string
1128
                        '[]'\n
1129
    The subsequent lines are sequences of
1130
      - function_name   indicates the start of a new function body and the name
1131
                        of the function
1132
      instructions      is multiple lines, one for each instruction in the
1133
                        function\n
1134
    The formats of the instruction lines are as follows:
1135
      value string      value is the next instruction to store and string is an
1136
                        optional string describing the instruction
1137
                        Note:  "value" must be a 3-digit hex string
1138
                               representing a 9-bit value
1139
                        Note:  The only place string should be empty is when
1140
                               pushing the 8 lsb of an address onto the start
1141
                               prior to a call, callc, jump, or jumpc
1142
                               instruction
1143
      p name            the single 'p' means that the name of a parameter and
1144
                        its range are to be converted into an instruction
1145
    """
1146
    # Write the program marker, address of .main, address or "[]" of .interrupt,
1147
    # and the total program length.
1148
    fp.write(':program');
1149
    fp.write(' %d' % self.functionEvaluation['address'][0]);
1150
    if self.interrupt:
1151
      fp.write(' %d' % self.functionEvaluation['address'][1]);
1152
    else:
1153
      fp.write(' []');
1154
    fp.write(' %d' % (self.functionEvaluation['address'][-1] + self.functionEvaluation['length'][-1]));
1155
    fp.write('\n');
1156
    # Emit the bodies
1157
    for ix in range(len(self.functionEvaluation['list'])):
1158
      fp.write('- %s\n' % self.functionEvaluation['list'][ix]);
1159
      self.emitLabelList = '';
1160
      for token in self.functionEvaluation['body'][ix]:
1161
        if token['type'] == 'value':
1162
          self.EmitPush(fp,token['value'],tokenLoc=token['loc']);
1163
        elif token['type'] == 'label':
1164
          self.Emit_AddLabel(token['value']);
1165
        elif token['type'] == 'constant':
1166
          if not self.IsSymbol(token['value']):
1167
            raise Exception('Program Bug');
1168
          ix = self.symbols['list'].index(token['value']);
1169
          body = self.symbols['body'][ix];
1170
          self.EmitPush(fp,body[-1],token['value'],tokenLoc=token['loc']);
1171
          for v in body[-2::-1]:
1172
            self.EmitPush(fp,v,tokenLoc=token['loc']);
1173
        elif token['type'] in ('inport','outport','outstrobe',):
1174
          if not self.IsSymbol(token['value']):
1175
            raise Exception('Program Bug');
1176
          ix = self.symbols['list'].index(token['value']);
1177
          self.EmitPush(fp,self.symbols['body'][ix],token['value'],tokenLoc=token['loc']);
1178
        elif token['type'] == 'instruction':
1179
          self.EmitOpcode(fp,self.InstructionOpcode(token['value']),token['value']);
1180
        elif token['type'] == 'macro':
1181
          self.EmitMacro(fp,token);
1182
        elif token['type'] == 'parameter':
1183
          self.EmitParameter(fp,token);
1184
        elif token['type'] == 'symbol':
1185
          self.EmitPush(fp,token['value'],token['name'],tokenLoc=token['loc']);
1186
        elif token['type'] == 'variable':
1187
          self.EmitVariable(fp,token['value']);
1188
        else:
1189
          raise Exception('Program Bug:  Unrecognized type "%s"' % token['type']);
1190
 
1191
  ################################################################################
1192
  #
1193
  # Initialize the object.
1194
  #
1195
  ################################################################################
1196
 
1197
  def __init__(self):
1198
    """
1199
    Initialize the tables definining the following:
1200
      directly invokable instruction mnemonics and the associated opcodes
1201
      indirectly inivoked instruction mnemonics and the associated opcodes
1202
        Note:  These are accessed through macros since they require an argument
1203
               or are part of multi-instruction sequences.
1204
      directives (other than ".include")
1205
      macros with type restrictions for required arguments and defaults and
1206
        restrictions for optional arguments\n
1207
    Initialize lists and members to record memory attributes, stack lengths,
1208
    body of the .main function, body of the optional .interrupt function,
1209
    current memory for variable definitions, etc.
1210
    """
1211
 
1212
    #
1213 3 sinclairrf
    # Enumerate the directives
1214
    # Note:  The ".include" directive is handled within asmDef.FileBodyIterator.
1215
    #
1216
 
1217
    self.directives = dict();
1218
 
1219
    self.directives['list']= list();
1220
    self.directives['list'].append('.constant');
1221 9 sinclairrf
    self.directives['list'].append('.define');
1222 3 sinclairrf
    self.directives['list'].append('.function');
1223
    self.directives['list'].append('.interrupt');
1224
    self.directives['list'].append('.macro');
1225
    self.directives['list'].append('.main');
1226
    self.directives['list'].append('.memory');
1227
    self.directives['list'].append('.variable');
1228
 
1229
    #
1230 2 sinclairrf
    # Configure the instructions.
1231
    #
1232
 
1233
    self.instructions = dict(list=list(), opcode=list());
1234
    self.AddInstruction('&',            0x050);
1235
    self.AddInstruction('+',            0x018);
1236 7 sinclairrf
    self.AddInstruction('+c',           0x00B);
1237 2 sinclairrf
    self.AddInstruction('-',            0x01C);
1238
    self.AddInstruction('-1<>',         0x023);
1239
    self.AddInstruction('-1=',          0x022);
1240 7 sinclairrf
    self.AddInstruction('-c',           0x00F);
1241 2 sinclairrf
    self.AddInstruction('0<>',          0x021);
1242
    self.AddInstruction('0=',           0x020);
1243
    self.AddInstruction('0>>',          0x004);
1244
    self.AddInstruction('1+',           0x058);
1245
    self.AddInstruction('1-',           0x05C);
1246
    self.AddInstruction('1>>',          0x005);
1247
    self.AddInstruction('<<0',          0x001);
1248
    self.AddInstruction('<<1',          0x002);
1249
    self.AddInstruction('<<msb',        0x003);
1250
    self.AddInstruction('>r',           0x040);
1251
    self.AddInstruction('^',            0x052);
1252
    #self.AddInstruction('dis',          0x01C);
1253
    self.AddInstruction('drop',         0x054);
1254
    self.AddInstruction('dup',          0x008);
1255
    #self.AddInstruction('ena',          0x019);
1256
    self.AddInstruction('inport',       0x030);
1257
    self.AddInstruction('lsb>>',        0x007);
1258
    self.AddInstruction('msb>>',        0x006);
1259
    self.AddInstruction('nip',          0x053);
1260
    self.AddInstruction('nop',          0x000);
1261
    self.AddInstruction('or',           0x051);
1262
    self.AddInstruction('outport',      0x038);
1263
    self.AddInstruction('over',         0x00A);
1264
    self.AddInstruction('r>',           0x049);
1265
    self.AddInstruction('r@',           0x009);
1266
    self.AddInstruction('swap',         0x012);
1267
 
1268
    self.specialInstructions = dict();
1269
    self.specialInstructions['call']    = 0x0C0;
1270
    self.specialInstructions['callc']   = 0x0E0;
1271
    self.specialInstructions['fetch']   = 0x068;
1272
    self.specialInstructions['fetch+']  = 0x078;
1273
    self.specialInstructions['fetch-']  = 0x07C;
1274
    self.specialInstructions['jump']    = 0x080;
1275
    self.specialInstructions['jumpc']   = 0x0A0;
1276
    self.specialInstructions['return']  = 0x028;
1277
    self.specialInstructions['store']   = 0x060;
1278
    self.specialInstructions['store+']  = 0x070;
1279
    self.specialInstructions['store-']  = 0x074;
1280
 
1281
    #
1282
    # Configure the pre-defined macros
1283
    # Note:  'symbol' is a catch-call for functions, labels, variables, etc.
1284
    #        These are restricted to the appropriate types when the macros are
1285
    #        expanded.
1286
    #
1287
 
1288 11 sinclairrf
    self.macros = dict(list=list(), length=list(), args=list(), nArgs=list(), builtIn = list(), doc = list());
1289 3 sinclairrf
    self.EmitFunction = dict();
1290
 
1291
    # Macros built in to the assembler (to access primitives).
1292 2 sinclairrf
    self.AddMacro('.call',              3, [
1293
                                             ['','symbol'],
1294
                                             ['nop','instruction','singlemacro','singlevalue','symbol']
1295
                                           ]);
1296
    self.AddMacro('.callc',             3, [
1297
                                             ['','symbol'],
1298
                                             ['drop','instruction','singlevalue','symbol']
1299
                                           ]);
1300
    self.AddMacro('.fetch',             1, [ ['','symbol'] ]);
1301
    self.AddMacro('.fetch+',            1, [ ['','symbol'] ]);
1302
    self.AddMacro('.fetch-',            1, [ ['','symbol'] ]);
1303
    self.AddMacro('.jump',              3, [
1304
                                             ['','symbol'],
1305
                                             ['nop','instruction','singlemacro','singlevalue','symbol']
1306
                                           ]);
1307
    self.AddMacro('.jumpc',             3, [
1308
                                             ['','symbol'],
1309
                                             ['drop','instruction','singlemacro','singlevalue','symbol']
1310
                                           ]);
1311 4 sinclairrf
    self.AddMacro('.return',            2, [ ['nop','instruction','singlemacro','singlevalue','symbol'] ]);
1312 2 sinclairrf
    self.AddMacro('.store',             1, [ ['','symbol'] ]);
1313
    self.AddMacro('.store+',            1, [ ['','symbol'] ]);
1314
    self.AddMacro('.store-',            1, [ ['','symbol'] ]);
1315
 
1316 3 sinclairrf
    # User-defined macros in ./macros that are "built in" to the assembler.
1317
    macroSearchPath = os.path.join(sys.path[0],'macros');
1318
    for macroName in os.listdir(macroSearchPath):
1319
      if not re.match(r'.*\.py$',macroName):
1320
        continue;
1321
      self.AddUserMacro(macroName[:-3],macroSearchPaths=[macroSearchPath]);
1322
    for macroName in self.macros['list']:
1323
      self.macros['builtIn'].append(macroName);
1324
 
1325 2 sinclairrf
    #
1326
    # List the macros that have special symbols for their first argument.
1327
    #
1328
 
1329
    self.MacrosWithSpecialFirstSymbol = ('.call','.callc','.jump','.jumpc',);
1330
 
1331
    #
1332
    # Externally defined parameters.
1333
    #
1334
 
1335
    self.memoryLength = dict();
1336
    self.stackLength = dict();
1337
 
1338
    #
1339
    # Configure the containers for the expanded main, interrupt, function,
1340
    # macro, etc. definitions.
1341
    #
1342
 
1343 3 sinclairrf
    self.currentMemory = None;
1344 2 sinclairrf
    self.interrupt = None;
1345
    self.main = None;
1346 3 sinclairrf
    self.macroSearchPaths = ['.','./macros'];
1347 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.