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

Subversion Repositories ssbcc

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

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

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