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

Subversion Repositories ssbcc

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

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
  def Emit_GetAddrAndBank(self,name):
868
    """
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
    if not self.IsSymbol(name):
876
      raise asmDef.AsmException('"%s" is not a recognized symbol' % name);
877
    ixName = self.symbols['list'].index(name);
878
    if self.symbols['type'][ixName] != 'variable':
879
      raise asmDef.AsmException('"%s" is not a variable' % name);
880
    body = self.symbols['body'][ixName];
881
    bankName = body['memory'];
882
    ixMem = self.memories['list'].index(bankName);
883
    return (body['start'],self.memories['bank'][ixMem],bankName,);
884
 
885
  def Emit_GetBank(self,name):
886
    """
887
    For the specified variable, return the memory bank index.\n
888 3 sinclairrf
    Note:  This is used by the .fetch, .fetch+, .fetch-, .store, .store+, and
889 2 sinclairrf
           .store- macros.
890
    """
891
    if name not in self.memories['list']:
892
      raise asmDef.AsmException('"%s" not a memory' % name);
893
    ixMem = self.memories['list'].index(name);
894
    return self.memories['bank'][ixMem];
895
 
896
  def Emit_String(self,name=''):
897
    """
898
    Append the specified string to the list of labels for the current
899
    instruction, restart the list of labels, and return the composite string.
900
    """
901
    name = self.emitLabelList + name;
902
    self.emitLabelList = '';
903
    return name;
904
 
905
  #
906
  # Utilities to write single instructions to the metacode file.
907
  #
908
  # Note:  Other than the program header and the function names, these
909
  #        utilities write the function bodies.
910
  #
911
 
912
  def EmitOpcode(self,fp,opcode,name):
913
    """
914
    Write the specified opcode and the associated comment string.\n
915
    The leading bit for an opcode is always a '0'.
916
    """
917
    if not (0 <= opcode < 256):
918
      raise Exception('Program Bug -- opcode "0x%X" out of range');
919
    fp.write('0%02X %s\n' % (opcode,self.Emit_String(name)));
920
 
921
  def EmitParameter(self,fp,token):
922
    """
923
    Write the name (and range) of the specified parameter and the optional
924
    associated comment string.\n
925
    The string 'p' specifies that the parameter is to be inserted into the
926
    instruction body.\n
927
    Note:  The comment string may be the empty string if there were no labels
928
           immediately preceding the parameter.
929
    """
930
    name = token['value'];
931
    if not self.IsParameter(name):
932
      raise Exception('Program Bug');
933
    commentString = self.Emit_String();
934
    if commentString:
935
      fp.write('p %s%s %s\n' % (name,token['range'],commentString,));
936
    else:
937
      fp.write('p %s%s\n' % (name,token['range'],));
938
 
939
  def EmitPush(self,fp,value,name=None,tokenLoc=None):
940
    """
941
    Write the opcode to push a value onto the data stack.  Include the comment
942
    string including either the optionally provided symbol name or a printable
943
    representation of the value being pushed onto the stack.\n
944
    Note:  The printable value is included when a name is not provided so that
945
           the contents of single characters or of strings being pushed onto
946
           the stack can be read.\n
947
    Note:  The token location is an optional input required when the range of
948
           the provided value may not have been previously ensured to fit in
949
           one byte.
950
    """
951
    if not (-128 <= value < 256):
952
      if tokenLoc == None:
953
        raise Exception('Program Bug -- untrapped out-of-range token "%s"' % value);
954
      else:
955
        raise asmDef.AsmException('Value not representable by a byte at "%s"' % tokenLoc);
956
    if value < 0:
957
      value = value + 256;
958
    if type(name) == str:
959
      fp.write('1%02X %s\n' % ((value % 0x100),self.Emit_String(name)));
960
    elif (chr(value) in string.printable) and (chr(value) not in string.whitespace):
961
      fp.write('1%02X %s\n' % ((value % 0x100),self.Emit_String('%02X \'%c\'' % (value,value,))));
962
    else:
963
      fp.write('1%02X %s\n' % ((value % 0x100),self.Emit_String('0x%02X' % value)));
964
 
965
  def EmitVariable(self,fp,name):
966
    """
967
    Use the EmitPush method to push the address of a variable onto the data
968
    stack.
969
    """
970
    if not self.IsSymbol(name):
971
      raise asmDef.AsmException('Variable "%s" not recognized' % name);
972
    ixName = self.symbols['list'].index(name);
973
    if self.symbols['type'][ixName] != 'variable':
974
      raise asmDef.AsmException('"%s" is not a variable' % name);
975
    self.EmitPush(fp,self.symbols['body'][ixName]['start'],name);
976
 
977
  #
978
  # EmitOpcode, EmitMacro, and EmitProgram emit composite or more complicated
979
  # bodies.
980
  #
981
 
982
  def EmitOptArg(self,fp,token):
983
    """
984
    Write the metacode for optional arguments to macros.\n
985
    These must be single-instruction arguments.
986
    """
987
    # Symbols encountered in macros are expanded here instead of the
988
    # ExpandTokens method -- the code is much simpler this way even though the
989
    # associated error detection was deferred in the processing.  The symbol
990
    # must expand to a single value.
991
    if token['type'] == 'symbol':
992
      token = self.ExpandSymbol(token,singleValue=True);
993
    if token['type'] == 'constant':
994
      name = token['value'];
995
      if not self.IsSymbol(name):
996
        raise Exception('Program Bug');
997
      ix = self.symbols['list'].index(name);
998
      if len(self.symbols['body'][ix]) != 1:
999
        raise asmDef.AsmException('Optional constant can only be one byte at %s' % token['loc']);
1000
      self.EmitPush(fp,self.symbols['body'][ix][0],self.Emit_String(name),tokenLoc=token['loc']);
1001
    elif token['type'] in ('inport','outport','outstrobe'):
1002
      name = token['value'];
1003
      if not self.IsSymbol(name):
1004
        raise Exception('Program Bug -- unrecognized inport/outport name "%s"');
1005
      ix = self.symbols['list'].index(name);
1006
      self.EmitPush(fp,self.symbols['body'][ix],self.Emit_String(name));
1007
    elif token['type'] == 'instruction':
1008
      self.EmitOpcode(fp,self.InstructionOpcode(token['value']),token['value']);
1009
    elif token['type'] == 'parameter':
1010
      self.EmitParameter(fp,token);
1011
    elif token['type'] == 'value':
1012
      self.EmitPush(fp,token['value'],tokenLoc=token['loc']);
1013
    elif token['type'] == 'variable':
1014
      self.EmitVariable(fp,token['value']);
1015
    elif token['type'] == 'macro':
1016
      self.EmitMacro(fp,token);
1017
    else:
1018
      raise asmDef.AsmException('Unrecognized optional argument "%s"' % token['value']);
1019
 
1020
  def EmitMacro(self,fp,token):
1021
    """
1022 3 sinclairrf
    Write the metacode for a macro.\n
1023
    The macros coded here are required to access intrinsics.
1024 2 sinclairrf
    """
1025
    # .call
1026
    if token['value'] == '.call':
1027
      self.EmitPush(fp,token['address'] & 0xFF,'');
1028
      self.EmitOpcode(fp,self.specialInstructions['call'] | (token['address'] >> 8),'call '+token['argument'][0]['value']);
1029
      self.EmitOptArg(fp,token['argument'][1]);
1030
    # .callc
1031
    elif token['value'] == '.callc':
1032
      self.EmitPush(fp,token['address'] & 0xFF,'');
1033
      self.EmitOpcode(fp,self.specialInstructions['callc'] | (token['address'] >> 8),'callc '+token['argument'][0]['value']);
1034
      self.EmitOptArg(fp,token['argument'][1]);
1035
    # .fetch
1036
    elif token['value'] == '.fetch':
1037
      name = token['argument'][0]['value'];
1038
      ixBank = self.Emit_GetBank(name);
1039
      self.EmitOpcode(fp,self.specialInstructions['fetch'] | ixBank,'fetch '+name);
1040
    # .fetch+
1041
    elif token['value'] == '.fetch+':
1042
      name = token['argument'][0]['value'];
1043
      ixBank = self.Emit_GetBank(name);
1044
      self.EmitOpcode(fp,self.specialInstructions['fetch+'] | ixBank,'fetch+('+name+')');
1045
    # .fetch-
1046
    elif token['value'] == '.fetch-':
1047
      name = token['argument'][0]['value'];
1048
      ixBank = self.Emit_GetBank(name);
1049
      self.EmitOpcode(fp,self.specialInstructions['fetch-'] | ixBank,'fetch-('+name+')');
1050
    # .jump
1051
    elif token['value'] == '.jump':
1052
      self.EmitPush(fp,token['address'] & 0xFF,'');
1053
      self.EmitOpcode(fp,self.specialInstructions['jump'] | (token['address'] >> 8),'jump '+token['argument'][0]['value']);
1054
      self.EmitOptArg(fp,token['argument'][1]);
1055
    # .jumpc
1056
    elif token['value'] == '.jumpc':
1057
      self.EmitPush(fp,token['address'] & 0xFF,'');
1058
      self.EmitOpcode(fp,self.specialInstructions['jumpc'] | (token['address'] >> 8),'jumpc '+token['argument'][0]['value']);
1059
      self.EmitOptArg(fp,token['argument'][1]);
1060
    # .return
1061
    elif token['value'] == '.return':
1062
      self.EmitOpcode(fp,self.specialInstructions['return'],'return');
1063
      self.EmitOptArg(fp,token['argument'][0]);
1064
    # .store
1065
    elif token['value'] == '.store':
1066
      name = token['argument'][0]['value'];
1067
      ixBank = self.Emit_GetBank(name);
1068
      self.EmitOpcode(fp,self.specialInstructions['store'] | ixBank,'store '+name);
1069
    # .store+
1070
    elif token['value'] == '.store+':
1071
      name = token['argument'][0]['value'];
1072
      ixBank = self.Emit_GetBank(name);
1073
      self.EmitOpcode(fp,self.specialInstructions['store+'] | ixBank,'store+ '+name);
1074
    # .store-
1075
    elif token['value'] == '.store-':
1076
      name = token['argument'][0]['value'];
1077
      ixBank = self.Emit_GetBank(name);
1078
      self.EmitOpcode(fp,self.specialInstructions['store-'] | ixBank,'store- '+name);
1079 3 sinclairrf
    # user-defined macro
1080
    elif token['value'] in self.EmitFunction:
1081
      self.EmitFunction[token['value']](self,fp,token['argument']);
1082 2 sinclairrf
    # error
1083
    else:
1084
      raise Exception('Program Bug -- Unrecognized macro "%s"' % token['value']);
1085
 
1086
  def EmitProgram(self,fp):
1087
    """
1088
    Write the program to the metacode file.\n
1089
    The frist line for the program has the format
1090
      :program address_main address_interrupt
1091
    where
1092
      address_main      is the address of the .main function (this should be 0)
1093
      address_interrupt is either the address of the optional interrupt
1094
                        function if it was defined or the 2-character string
1095
                        '[]'\n
1096
    The subsequent lines are sequences of
1097
      - function_name   indicates the start of a new function body and the name
1098
                        of the function
1099
      instructions      is multiple lines, one for each instruction in the
1100
                        function\n
1101
    The formats of the instruction lines are as follows:
1102
      value string      value is the next instruction to store and string is an
1103
                        optional string describing the instruction
1104
                        Note:  "value" must be a 3-digit hex string
1105
                               representing a 9-bit value
1106
                        Note:  The only place string should be empty is when
1107
                               pushing the 8 lsb of an address onto the start
1108
                               prior to a call, callc, jump, or jumpc
1109
                               instruction
1110
      p name            the single 'p' means that the name of a parameter and
1111
                        its range are to be converted into an instruction
1112
    """
1113
    # Write the program marker, address of .main, address or "[]" of .interrupt,
1114
    # and the total program length.
1115
    fp.write(':program');
1116
    fp.write(' %d' % self.functionEvaluation['address'][0]);
1117
    if self.interrupt:
1118
      fp.write(' %d' % self.functionEvaluation['address'][1]);
1119
    else:
1120
      fp.write(' []');
1121
    fp.write(' %d' % (self.functionEvaluation['address'][-1] + self.functionEvaluation['length'][-1]));
1122
    fp.write('\n');
1123
    # Emit the bodies
1124
    for ix in range(len(self.functionEvaluation['list'])):
1125
      fp.write('- %s\n' % self.functionEvaluation['list'][ix]);
1126
      self.emitLabelList = '';
1127
      for token in self.functionEvaluation['body'][ix]:
1128
        if token['type'] == 'value':
1129
          self.EmitPush(fp,token['value'],tokenLoc=token['loc']);
1130
        elif token['type'] == 'label':
1131
          self.Emit_AddLabel(token['value']);
1132
        elif token['type'] == 'constant':
1133
          if not self.IsSymbol(token['value']):
1134
            raise Exception('Program Bug');
1135
          ix = self.symbols['list'].index(token['value']);
1136
          body = self.symbols['body'][ix];
1137
          self.EmitPush(fp,body[-1],token['value'],tokenLoc=token['loc']);
1138
          for v in body[-2::-1]:
1139
            self.EmitPush(fp,v,tokenLoc=token['loc']);
1140
        elif token['type'] in ('inport','outport','outstrobe',):
1141
          if not self.IsSymbol(token['value']):
1142
            raise Exception('Program Bug');
1143
          ix = self.symbols['list'].index(token['value']);
1144
          self.EmitPush(fp,self.symbols['body'][ix],token['value'],tokenLoc=token['loc']);
1145
        elif token['type'] == 'instruction':
1146
          self.EmitOpcode(fp,self.InstructionOpcode(token['value']),token['value']);
1147
        elif token['type'] == 'macro':
1148
          self.EmitMacro(fp,token);
1149
        elif token['type'] == 'parameter':
1150
          self.EmitParameter(fp,token);
1151
        elif token['type'] == 'symbol':
1152
          self.EmitPush(fp,token['value'],token['name'],tokenLoc=token['loc']);
1153
        elif token['type'] == 'variable':
1154
          self.EmitVariable(fp,token['value']);
1155
        else:
1156
          raise Exception('Program Bug:  Unrecognized type "%s"' % token['type']);
1157
 
1158
  ################################################################################
1159
  #
1160
  # Initialize the object.
1161
  #
1162
  ################################################################################
1163
 
1164
  def __init__(self):
1165
    """
1166
    Initialize the tables definining the following:
1167
      directly invokable instruction mnemonics and the associated opcodes
1168
      indirectly inivoked instruction mnemonics and the associated opcodes
1169
        Note:  These are accessed through macros since they require an argument
1170
               or are part of multi-instruction sequences.
1171
      directives (other than ".include")
1172
      macros with type restrictions for required arguments and defaults and
1173
        restrictions for optional arguments\n
1174
    Initialize lists and members to record memory attributes, stack lengths,
1175
    body of the .main function, body of the optional .interrupt function,
1176
    current memory for variable definitions, etc.
1177
    """
1178
 
1179
    #
1180 3 sinclairrf
    # Enumerate the directives
1181
    # Note:  The ".include" directive is handled within asmDef.FileBodyIterator.
1182
    #
1183
 
1184
    self.directives = dict();
1185
 
1186
    self.directives['list']= list();
1187
    self.directives['list'].append('.constant');
1188
    self.directives['list'].append('.function');
1189
    self.directives['list'].append('.interrupt');
1190
    self.directives['list'].append('.macro');
1191
    self.directives['list'].append('.main');
1192
    self.directives['list'].append('.memory');
1193
    self.directives['list'].append('.variable');
1194
 
1195
    #
1196 2 sinclairrf
    # Configure the instructions.
1197
    #
1198
 
1199
    self.instructions = dict(list=list(), opcode=list());
1200
    self.AddInstruction('&',            0x050);
1201
    self.AddInstruction('+',            0x018);
1202
    self.AddInstruction('-',            0x01C);
1203
    self.AddInstruction('-1<>',         0x023);
1204
    self.AddInstruction('-1=',          0x022);
1205
    self.AddInstruction('0<>',          0x021);
1206
    self.AddInstruction('0=',           0x020);
1207
    self.AddInstruction('0>>',          0x004);
1208
    self.AddInstruction('1+',           0x058);
1209
    self.AddInstruction('1-',           0x05C);
1210
    self.AddInstruction('1>>',          0x005);
1211
    self.AddInstruction('<<0',          0x001);
1212
    self.AddInstruction('<<1',          0x002);
1213
    self.AddInstruction('<<msb',        0x003);
1214
    self.AddInstruction('>r',           0x040);
1215
    self.AddInstruction('^',            0x052);
1216
    #self.AddInstruction('dis',          0x01C);
1217
    self.AddInstruction('drop',         0x054);
1218
    self.AddInstruction('dup',          0x008);
1219
    #self.AddInstruction('ena',          0x019);
1220
    self.AddInstruction('inport',       0x030);
1221
    self.AddInstruction('lsb>>',        0x007);
1222
    self.AddInstruction('msb>>',        0x006);
1223
    self.AddInstruction('nip',          0x053);
1224
    self.AddInstruction('nop',          0x000);
1225
    self.AddInstruction('or',           0x051);
1226
    self.AddInstruction('outport',      0x038);
1227
    self.AddInstruction('over',         0x00A);
1228
    self.AddInstruction('r>',           0x049);
1229
    self.AddInstruction('r@',           0x009);
1230
    self.AddInstruction('swap',         0x012);
1231
 
1232
    self.specialInstructions = dict();
1233
    self.specialInstructions['call']    = 0x0C0;
1234
    self.specialInstructions['callc']   = 0x0E0;
1235
    self.specialInstructions['fetch']   = 0x068;
1236
    self.specialInstructions['fetch+']  = 0x078;
1237
    self.specialInstructions['fetch-']  = 0x07C;
1238
    self.specialInstructions['jump']    = 0x080;
1239
    self.specialInstructions['jumpc']   = 0x0A0;
1240
    self.specialInstructions['return']  = 0x028;
1241
    self.specialInstructions['store']   = 0x060;
1242
    self.specialInstructions['store+']  = 0x070;
1243
    self.specialInstructions['store-']  = 0x074;
1244
 
1245
    #
1246
    # Configure the pre-defined macros
1247
    # Note:  'symbol' is a catch-call for functions, labels, variables, etc.
1248
    #        These are restricted to the appropriate types when the macros are
1249
    #        expanded.
1250
    #
1251
 
1252 3 sinclairrf
    self.macros = dict(list=list(), length=list(), args=list(), nArgs=list(), builtIn = list());
1253
    self.EmitFunction = dict();
1254
 
1255
    # Macros built in to the assembler (to access primitives).
1256 2 sinclairrf
    self.AddMacro('.call',              3, [
1257
                                             ['','symbol'],
1258
                                             ['nop','instruction','singlemacro','singlevalue','symbol']
1259
                                           ]);
1260
    self.AddMacro('.callc',             3, [
1261
                                             ['','symbol'],
1262
                                             ['drop','instruction','singlevalue','symbol']
1263
                                           ]);
1264
    self.AddMacro('.fetch',             1, [ ['','symbol'] ]);
1265
    self.AddMacro('.fetch+',            1, [ ['','symbol'] ]);
1266
    self.AddMacro('.fetch-',            1, [ ['','symbol'] ]);
1267
    self.AddMacro('.jump',              3, [
1268
                                             ['','symbol'],
1269
                                             ['nop','instruction','singlemacro','singlevalue','symbol']
1270
                                           ]);
1271
    self.AddMacro('.jumpc',             3, [
1272
                                             ['','symbol'],
1273
                                             ['drop','instruction','singlemacro','singlevalue','symbol']
1274
                                           ]);
1275
    self.AddMacro('.return',            2, [ ['nop','instruction','singlevalue','symbol'] ]);
1276
    self.AddMacro('.store',             1, [ ['','symbol'] ]);
1277
    self.AddMacro('.store+',            1, [ ['','symbol'] ]);
1278
    self.AddMacro('.store-',            1, [ ['','symbol'] ]);
1279
 
1280 3 sinclairrf
    # User-defined macros in ./macros that are "built in" to the assembler.
1281
    macroSearchPath = os.path.join(sys.path[0],'macros');
1282
    for macroName in os.listdir(macroSearchPath):
1283
      if not re.match(r'.*\.py$',macroName):
1284
        continue;
1285
      self.AddUserMacro(macroName[:-3],macroSearchPaths=[macroSearchPath]);
1286
    for macroName in self.macros['list']:
1287
      self.macros['builtIn'].append(macroName);
1288
 
1289 2 sinclairrf
    #
1290
    # List the macros that have special symbols for their first argument.
1291
    #
1292
 
1293
    self.MacrosWithSpecialFirstSymbol = ('.call','.callc','.jump','.jumpc',);
1294
 
1295
    #
1296
    # Externally defined parameters.
1297
    #
1298
 
1299
    self.memoryLength = dict();
1300
    self.stackLength = dict();
1301
 
1302
    #
1303
    # Configure the containers for the expanded main, interrupt, function,
1304
    # macro, etc. definitions.
1305
    #
1306
 
1307 3 sinclairrf
    self.currentMemory = None;
1308 2 sinclairrf
    self.interrupt = None;
1309
    self.main = None;
1310 3 sinclairrf
    self.macroSearchPaths = ['.','./macros'];
1311 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.