Patrick McNamara wrote:


After looking at the lines above, I would probably go for this as a general format:

CMD OP1 OP2 [flags] for two operand instructions
CMD OP1 [flags] for one operand instructions
CMD [flags] for zero operand instructions

Though really, this is a kind of academic discussion. For as simple a syntax as this will have, setting up a compiler that access a configurable definition for line syntax would not be terribly hard. Still, having a single syntax will make it much easier for people other than the author to read and understand programs.

Patrick M
_______________________________________________
Open-graphics mailing list
[email protected]
http://lists.duskglow.com/mailman/listinfo/open-graphics
List service provided by Duskglow Consulting, LLC (www.duskglow.com)

So I sat down this afternoon and flushed this out a bit. Here is Timothy's original program (bugs and all, plus my new ones :) ) in the new instruction syntax. This assumes a sync polarity register. Flags are asserted if specified.

; Simple 640x480 program
; hfp = 8
; hsync = 8
; hbp = 8
; vfp = 3
; vsync = 3
; vbp = 3
; vsync,hsync negative-assertion, data-enable positive assertion

; Entry point
.addr = 0

; Vertical program
ADDR 0 [vhd]                            ; vid addr = start of framebuffer
CALL vsync+1 1
CALL vsync 2
CALL vblank 2 [v]                       ; back porch
FETCH 640 [v]                           ; fetch first scanline
CALL vbp+1 1 [v]
CALL vbp 1 [v]
CALL vactive 479 [v]
NOOP [v]                                ; Already fetched last scanline
CALL vactive+1 1 [v]
CALL vblank 2 [v]                       ; front porch
CALL vblank_short 1 [v]
JUMP 0 [vh]

vsync:
       NOOP 1                          ; last 4 pixels of hsync
       NOOP 2 [h]                      ; back porch
       NOOP 640 [h]                    ; active period, but vblank
       NOOP 2 [h]                      ; front porch

vblank:
       NOOP 1 [v]                      ; last 4 pixels of hsync
       NOOP 2 [vh]                     ; back porch
       NOOP 640 [vh]                   ; active period, but vblank
       NOOP 2 [vhr]                    ; front porch

vblank_short:
       NOOP 1 [v]                      ; last 4 pixels of hsync
       NOOP 2 [vh]                     ; back porch
       NOOP 640 [vh]                   ; active period, but vblank
       NOOP 1 [vhr]                    ; front porch

vactive:
       FETCH 1 [v]                     ; last 4 pixels of hsync
       NOOP 2 [vh]                     ; back porch
       SEND 640 [vh]                   ; active period, but vblank
       NOOP 2 [vhr]                    ; front porch

.end

Now for the fun part. What follows is a compiler for the defined assembly. It will produce raw binary byte strings suitable for loading into the video controller program memory. You should be able to modify the memory module (RAMB16_S36_36.v) in SVN as follows to populate the memory at startup with the compiled program. Please pardon my sad attempt at Verilog. I am NOT a Verilog programmer by any stretch. The final argument is the offset, if you want to start somewhere other than 0, change accordingly.
reg [35:0] mem [0:511];
initial begin
        $readmemb("program.bin", mem, 0);
end


And now, here is the compiler code. It's all Perl and it's ugly. It has bugs, I just haven't bothered to find them yet. I have not done more than a cursory validation of the output byte strings to see that I got all the bit positions in the opcodes correct. If enough people like it, I'll commit it to SVN.

----BEGIN COMPILER----

#!/usr/bin/perl

###################################################################
#
#  Sample video controller assembly langyuage compiler
#
#  Copyright (c) 2006, Patrick McNamara
#
#  This code is public domain and may be freely copied, modified
#  and redistributed under this or other licenses.
#
#  usage: compiler.pl <input file> <output file>
#
###################################################################


#start with our opcode definitions.
#fields are:
#  1, opcode value
#  2, argument count
#  3, argument 1 word position.  A leading - means that the
#     argument will be subtracted from the max possible
#  4, argument 2 word position, same format as argument 1
%opcode=(
    "JUMP" => [0, 1, "8:0", ""],
    "CALL" => [1, 2, "8:0", "-20:9"],
    "NOOP" => [2, 1, "-20:9", ""],
    "SEND" => [3, 1, "-20:9", ""],
    #"UNDEF" => [4, 0, "", ""],
    "FETCH" => [5, 1, "20:9", ""],
    "ADDR" => [6, 1, "20:0", ""],
    "INC" => [7, 1, "20:0", ""],
    );

#The bit positions of the flags in the upper byte
%flag_bit=(
      "z" => 0x01,
      "i" => 0x02,
      "r" => 0x04,
      "x" => 0x08,
      "y" => 0x10,
      "h" => 0x20,
      "v" => 0x40,
      "d" => 0x80,
      );

my %labels=();
my @binary=();
my @src=();

#slurp in the source file
open(SRC, $ARGV[0]);
@src=<SRC>;
chomp(@src);
close(SRC);

#pass 1, figure out what the labels, and their offsets are.
%labels=map_labels([EMAIL PROTECTED]);

#compile the code
@binary=compile([EMAIL PROTECTED], \%labels);

#and dump the results to disk
write_out($ARGV[1], [EMAIL PROTECTED]);
exit(0);

sub compile {
   local *src=$_[0];
   local *labels=$_[1];
   my $line="";
   my $flags;
   my @flag_chars=();
   my $char;
   my $op, $arg1, $arg2;
   my @op_info=();
   my $binary;
   my $bith, $bitl;
   my $line_num=0;
   my @output=();
   my $op_count;
   my $comp=0;
   for $line (@src) {
   #our running line count for errors and other human stuff
   $line_num++;
   print "$line_num: $line\n";
   #by default, if not specified, the flags will all be 0;
   $flags=0;
   #zero out our final compiled byte string
   $binary=0;
   #if the line contains a comment ';' take everything before it
   $line=(split(";", $line))[0] if($line=~/;/);
   #if the line begins with a lable ':' take everything after it
   $line=(split(":", $line))[1] if($line=~/:/);
   #if we got a blank line, skip it.
   next if($line=~/^\s*$/);
   #if the line if a compiler macro, skip it
   next if($line=~/^\s*\./);
   #strip any leading blanks space
   [EMAIL PROTECTED]@@g;
   #check for flags in the command
   if ($line=~/\[\w*\]/) {
       #match the flags and extract
       $line=~m/\[(\w*)\]/;
       $char=$1;
       #now remove them from the original line since we don't need
       #them there anymore
       [EMAIL PROTECTED]@@;
       #split them into single characters
       @flag_chars=split("", $char);
       #and set any flag specified
       for $char (@flag_chars) {
       $flags|=$flag_bit{$char};
} }
   #insert the flags into the compiled bytestring
   $binary |= 0xFF000000 & ($flags << 24);
   $opcount=0;
   #split the line into it's component pieces
   ($op, $arg1, $arg2)=split(" ", $line);
   #and figure out how many operands we have.
   $opcount++ if(defined($arg1));
   $opcount++ if(defined($arg2) && $arg2 ne "");
   #Make sure we have a valid operation and error if not.
   if(!defined($opcode{$op})) {
       print "ERROR: line $line_num - opcode not defined: $op\n";
       next;
   }
   #retrieve the opcode definition
   @[EMAIL PROTECTED];
   #validate the number of arguments against what we expect.  For the time
   #being we will accept too many and just throw a warning.
   if($opcount < $op_info[1]) {
print "ERROR: line $line_num - expected $op_info[1] arguments, found $opcount\n";
       next;
   }
   if($opcount > $op_info[1]) {
print "WARNING: line $line_num - expected $op_info[1] arguments, found $opcount. Ignoring extra arguments\n";
       next;
} #add the opcode to the compiled byte string
   $binary |= 0x00E00000 & ($op_info[0] << 21);
   #if we have any arguments we need to process them
   if ($op_info[1]>0) {
       $comp=0;
       #determine whether the argument will be complemented and get
       #rid of the flag if it exists
       if($op_info[2]=~/^-/) {
       $comp=1;
       $op_info[2]=~s/^-//;
       }
       #get the high a low bit positions
       ($bith, $bitl)=split(":", $op_info[2]);
       #if the argument is not all numeric then we assume it is a label
       #and translate it.  If it won't translate, move on to the next line
       #since the translation function was nice and printed the error
       #message already
       if($arg1=~/\D/) {
       $arg1=translate_label($arg1, \%labels, $line_num);
       next unless(defined($arg1));
       $arg1=$labels{$arg1};
       }
       #if the argument is to be complemented, do so.
       if($comp) {
       $arg1=(2**($bith-$bitl))+1 - $arg1;
       }
       #Add the argument into the compiled bit string
$binary |= ((0xFFFFFFFF >> (31-$bith)) & (0xFFFFFFFF << ($bitl))) & ($arg1 << $bitl);
   }
#if there are two args we need to process the second one. See comments for the first #argument for explanation. Yes this chunk of code could probably be made into a
   #function for better maintenance and readability.
   if ($op_info[1]>1) {
       $comp=0;
       if($op_info[3]=~/^-/) {
       $comp=1;
       $op_info[3]=~s/^-//;
       }
       if($arg2=~/\D/) {
       $arg2=translate_label($arg2, \%labels, $line_num);
       next unless(defined($arg2));
       $arg2=$labels{$arg2};
       }
       ($bith, $bitl)=split(":", $op_info[3]);
       if($comp) {
       $arg2=(2**($bith-$bitl))+1 - $arg2;
} $binary |= ((0xFFFFFFFF >> (31-$bith)) & (0xFFFFFFFF << ($bitl))) & ($arg2 << $bitl); }
   #print the compiled output for the user
   printf "bin=0x%08X\n", $binary;
   push(@output, $binary);
   }
   return(@output);
}

sub translate_label {
   my $label_str=$_[0];
   local *labels=$_[1];
   my $line_num=$_[2];
   my $label_name;
   my $offset;
   my $dir=1;
   #it is possible that a label may have an offset associated with it
   #which could be positive or negative.  If the label name has a -
   #in it we assume that it will have negative offset
   $dir=-1 if($label_str=~/-/);
   #Now, split off any offset that does exist
   ($label_name, $offset)=split("[+-]", $label_str);
   #if there wasn't an offset, we will define it as zero to make the
   #math simpler.
   $offset=0 unless (defined($offset));
   #make sure the label we have has a defined offset, otherwise print
   #an error
   unless (defined($labels{$label_name})) {
print "ERROR: line $line_num - label undefined: $label_name\n"; return(undef);
   }
   #return the address for the translated label and offset
   return($labels{$label_name}+($offset*$dir));
}

sub map_labels {
   local  *src=$_[0];
   my %labels={};
   my $start=-1;
   my $offset=0;
   my $label="";
   #parse throught the source looking for labels
   for $line (@src) {
   #skip empty lines
   next if($line=~/^\s*$/);
   #skip comment only lines
   next if($line=~/^\s*;/);
   #if we found the .addr macro, save the start address for
   #label relocation later
   if($line=~/^\s*\.addr\s+/) {
       [EMAIL PROTECTED](\d+)@;
       $start=$1;
       next;
   }
   #skip any other compiler macro line
   next if($line=~/^\s*\./);
   #if we found a line that has a label definition, we need to
   #process it.
   if($line=~/^\s*\w+\s*:/) {
       #match the label and extract it.
       [EMAIL PROTECTED]([\w_]+)\s*:.*@;
       $label=$1;
       #since we are working with a fixed length instruction set
       #we can simply count the executable lines we've parse and
       #use that count as the label offset.
       $labels{$label}=$offset;
   }
   $offset++;
   }
   #if the programmer didn't specify an entry point, chastise him, but
   #continue anyway.
   if($start==-1) {
   print "Warning: No define entry point, assuming 0.\n";
   $start=0;
   }
   #if we have a none 0 entry point, we need to relocat all our labels
   #based on that entry point.
   if($start) {
   print "Relocating program to offset: $start\n";
   for $label  (keys(%labels)) {
       $labels{$label}+=$start;
   }
   }
   return(%labels);
}

sub write_out {
   my $file=$_[0];
   local *binary=$_[1];
   my $out_string="";
   my $count;
   #output file
   open(FILE, "> $file");
   #process each compiled statement
   for ($count=0; $count<scalar(@binary); $count++) {
   #We are writing out BIG ENDIEN, mainly because it is easiest
   #to load into a hex editor and verify against what is expected.
   #This could easily be changed by changing the pack flag below.
   $out_string=pack("N", $binary[$count]);
   print FILE $out_string;
   }
   close(FILE);
}

_______________________________________________
Open-graphics mailing list
[email protected]
http://lists.duskglow.com/mailman/listinfo/open-graphics
List service provided by Duskglow Consulting, LLC (www.duskglow.com)

Reply via email to