* OPGEN
* Generate BP OPCODES.H from CSRC equivalent.
* Copyright (c) 2004 Ladybridge Systems, All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
* 
* Ladybridge Systems can be contacted via the www.openqm.com web site.
* 
* START-HISTORY:
* 16 Sep 04  2.0-1 OpenQM launch. Earlier history details suppressed.
* END-HISTORY
*
* START-DESCRIPTION:
*
* END-DESCRIPTION
*
* START-CODE

program opgen
   openseq 'CSRC', 'opcodes.h' to in.f else stop 'Cannot open CSRC opcodes.h'
   openseq 'BP', 'OPCODES.H' to out.f else
      if status() then stop 'Cannot open OPCODES.H'
   end
   weofseq out.f

   out.rec = '* OPCODES.H'
   gosub emit

   out.rec = '* Generated by OPGEN at ' : oconv(time(), 'MTS')
   out.rec := ' on ' : oconv(date(), 'DDMY[,A3,4]')
   gosub emit

   out.rec = ''
   gosub emit


   * Opcode mode names from #defines in OPCODES.H
   * These are the defines for format modes at the start of OPCODES.H

   mode.names = ''    ;* Names and...
   mode.values = ''   ;* A = 0, B = 1, etc.

   * Simple single byte opcodes

   simple.opcodes = ''

   * Secondary opcodes

   secondary.opcodes = ''            ;* Prefix CF only

   * Other prefixed opcodes

   prefixed.opcodes = ''       ;* Names of all prefixed opcodes, including CF 
   prefixed.opcode.values = '' ;* Corresponding two byte opcode values

   last.prefix = 0

   loop
      readseq rec from in.f else exit

      convert '"' to '' in rec
      op.args = field(field(rec, '(', 2), ')', 1)

      begin case
         case rec[1,5] = '_opc_'
            if trim(field(op.args, ',', 4))[1,10] = 'op_illegal' then continue

            opcode.value = iconv(field(op.args, ',', 1)[3,4], 'MX')
            prefix = shift(opcode.value, 8)
            opcode.name = trim(field(op.args, ',', 3))
            mode.name = trim(field(op.args, ',', 5))
            locate mode.name in mode.names<1> setting mode.pos then
               mode = mode.values<mode.pos>
            end else
               stop 'Unrecognised mode name "' : mode.name : '"'
            end

            if prefix then
               locate opcode.value in prefixed.opcode.values<1> by 'AR' setting ppos else
                  ins opcode.value before prefixed.opcode.values<ppos>
                  ins opcode.name before prefixed.opcodes<ppos>
               end
               n = bitand(opcode.value, 255) + 1
               secondary.opcodes<n> = opcode.name
            end else            
               n = opcode.value + 1 
               simple.opcodes<n> = opcode.name
            end

         case rec[1,7] = '_extop_'
            prefix.opcode = trim(field(op.args, ',', 1))
            locate prefix.opcode in simple.opcodes<1> setting posn else
               stop 'Prefix opcode ' : prefix.opcode : ' not found'
            end
            prefix = posn - 1
            opcode.value = shift(prefix, -8)

            base.opcode = trim(field(op.args, ',', 2))
            locate base.opcode in simple.opcodes<1> setting posn else
               stop 'Base opcode ' : base.opcode : ' not found'
            end
            opcode.value += posn - 1
 
            opcode.name = trim(field(op.args, ',', 3))

            locate opcode.value in prefixed.opcode.values<1> by 'AR' setting ppos else
               ins opcode.value before prefixed.opcode.values<ppos>
               ins opcode.name before prefixed.opcodes<ppos>
            end

         case rec = '/* Format codes */'
            loop
               readseq rec from in.f else exit
            until rec[1,2] = '/*'
               rec = trim(rec)
               if rec[1,7] = '#define' then
                  mode.name = field(rec, ' ', 2)
                  mode = field(rec, ' ', 3)
                  mode.names<-1> = mode.name
                  mode.values<-1> = char(65 + mode)
                  out.rec = '$define MODE.' : fmt(change(mode.name, '_', '.'), '20L') : mode
                  gosub emit
               end
            repeat

            out.rec = ''
            gosub emit
            continue

         case 1
            continue
      end case
           
      if prefix # last.prefix then
         out.rec = ''
         gosub emit

         out.rec = '* Secondary opcodes, prefix ' : oconv(prefix, 'MX')
         out.rec := ' (' : simple.opcodes<prefix+1> : ')'

         gosub emit
         last.prefix = prefix
      end

      out.rec = fmt('$define OP.' : opcode.name, '24L')
      out.rec := fmt(opcode.value, '5R')
      out.rec := '  ;* ' : oconv(opcode.value, 'MX')
      gosub emit
   repeat

   * Now emit opcode name tables

   out.rec = ''
   gosub emit

   out.rec = '* Simple opcodes'
   gosub emit

   for j = 1 to 256 step 8
      out.rec = 'opcodes'
      out.rec := if j = 1 then ' = "'  else ' := "'
      for k = 0 to 7
         if k then out.rec := @vm
         out.rec := simple.opcodes<j+k>
      next k
      out.rec := '"'
      gosub emit
   next j

   out.rec = ''
   gosub emit

   out.rec = '* Secondary opcodes'
   gosub emit

   num.prefixed.opcodes = dcount(prefixed.opcode.values, @fm)
   for i = 1 to num.prefixed.opcodes step 8
      out.rec = 'prefixed.opcodes'
      out.rec := if i = 1 then ' = "'  else ' := "'
      for j = 0 to 7
         k = i + j
      while k <= num.prefixed.opcodes
         if j then out.rec := @vm
         out.rec := prefixed.opcodes<k>
      next j
      out.rec := '"'
      gosub emit
   next i

   for i = 1 to num.prefixed.opcodes step 8
      out.rec = 'prefixed.opcode.values'
      out.rec := if i = 1 then ' = "'  else ' := "'
      for j = 0 to 7
         k = i + j
      while k <= num.prefixed.opcodes
         if j then out.rec := @vm
         out.rec := prefixed.opcode.values<k>
      next j
      out.rec := '"'
      gosub emit
   next i


   return

* ======================================================================

emit:
   writeseq trimb(out.rec) to out.f else stop 'Write error on OPCODES.H'
   return
end

* END-CODE
