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

Subversion Repositories ssbcc

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

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