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

Subversion Repositories ssbcc

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

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