/***************************************************************************
                          parse.c  -  parses each line, stores in temp space
                             -------------------
    Date                 : May 23 2000
    Copyright            : (C) 2000 by Daniel Bertrand
    Email                : d.bertrand@ieee.ca
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include"global.h"
#include"parse.h"

void illegal (const char *);

/*
This function parses the asm file calling appropriate functions to blow up macros,
define constants, keep track of equates, and handling assembler directives.

This function is called on a line by line basis.
this is were the juice is :-)

normally returns value 0 except when "end" directive is encountered in which case it returns 1
*/


int parse( char line_string[max_line_length])
{
	int op_num;
	static int ds_addr=0x100;     // DS start at 0x100 ( start of the general purpose registers).
	char temp[max_line_length];
	char *tmp, *leading_symbol=NULL, *op_name_ptr;
	extern int dsp_addr;
	extern struct parsed_struct parsed[DSP_CODE_SIZE];
	
		
	strcpy(&temp[0],&line_string[0]);

	if( line_string[0]==';'||line_string[0]=='%'||line_string[0]=='*'||line_string[0]=='\n')
	    	return(0);
 	
	 //check for a leading symbols

	if( line_string[0] != ' ' && line_string[0] != '\t'){
	 	
		if(strlen(leading_symbol=strtok(line_string,": \t\n"))>max_length_symbol){
			fprintf(stderr,"Error line %d : symbol is more than %d characters long\n",source_line_num,max_length_symbol);
			illegal("Syntax error, unable to continue");
		}
		//ok we know we have a symbol, but is it an equ, a DS, a DC, a macro definition, or an address reference?
		  	
		op_name_ptr=strtok(NULL, " \t\n");
		  	
					
		if(strcasecmp("equ",op_name_ptr)==0){
			tmp = strtok(NULL, " \t\n");
			new_symbol(leading_symbol,operand_decode(tmp));
			return(0);
	
		}else if(strcasecmp("DS",op_name_ptr)==0){
			new_symbol(leading_symbol,ds_addr);
			ds_addr+=operand_decode(strtok(NULL, " \t\n"));//see assemble.c for this function
			return(0);
	
		}else if(strcasecmp("macro",op_name_ptr)==0){
			fprintf(stderr,"Error: macro not implemented yet\n");		
			exit(2);
	
		}else if(strcasecmp("dc",op_name_ptr)==0){
                        new_dc(leading_symbol,strtok(NULL, " \t\n"),ds_addr);
                        ds_addr++;
			return(0);
		}else{
			new_symbol(leading_symbol,dsp_addr);//else it's a reference to this address		
		}
	}else{
		//if it didn't have a symbol:
		op_name_ptr=strtok(line_string," \t\n");
	}

	if(op_name_ptr!=NULL){
	
		if((op_num=op_decode(op_name_ptr))<num_ops){
	
  			parsed[dsp_addr].op_number = op_num;
			parsed[dsp_addr].source_line=source_line_num;
	  			  	
			strcpy((char *)&parsed[dsp_addr].operand,strtok(NULL," \t\n"));
#ifdef DEBUG		
			printf( "0x%03x\t%s   \t%s\n",dsp_addr,op_name_ptr,&parsed[dsp_addr].operand);
#endif
			dsp_addr++;
		}else if(strcasecmp("end", op_name_ptr)==0){
			return(1);
	
		}else if(strcasecmp("include",op_name_ptr)==0){
			asm_open(strtok(NULL, " \t\n\'\""));
		
		}else if(strcasecmp("orgd",op_name_ptr)==0){
			//get the new data storage address:
			ds_addr=operand_decode(strtok(NULL, " \t\n"));
#ifdef DEBUG
                        printf("ds_addr is now 0x%x",ds_addr);
#endif
		}else{
			fprintf(stderr,"ERROR: Bad op code field \"%s\" on line number %d\n",op_name_ptr,source_line_num);
			illegal("Unrecoverable Error");
		}
	}
return(0);
}




void new_symbol( char *equ_name_ptr, int constant)
{
	extern int symbol_count;
	extern struct symbol sym_table[max_num_symbol];
	
	if(symbol_count==max_num_symbol){
		fprintf(stderr,"error: too many symbols used; max is %d",max_num_symbol);
		exit(1);
	}
	
	strcpy(sym_table[symbol_count].name,equ_name_ptr);
	sym_table[symbol_count].value=constant;
	
	symbol_count++;
#ifdef DEBUG
	printf("symbol: %s == %x \n", equ_name_ptr, constant);
#endif     
}



int op_decode(char *op_name_ptr)
{
	int op_num;

	for(op_num=0;op_num<num_ops;op_num++){
      		if( strcasecmp(&op_codes[op_num][0],op_name_ptr) == 0 )
			break;
    	}
	return(op_num);
}

void new_dc(char *symbol,char *constant, int addr)
{
        extern  FILE *dc_fp;
        unsigned long temp;
        new_symbol(symbol,addr);

        //note DC only works with hex values, this is temporary and should be fixed ASAP:        
        temp=strtoul((constant+1),NULL,16);


        if(dc_fp==NULL)
                illegal("DC used but no output constant file specified");
#ifdef DEBUG
        printf("DC value %x at address %x",temp,addr);
#endif        
        fprintf(dc_fp, "%c%c%c%c%c%c", (char)addr,(char) (addr>>8) , (char) temp,(char) (temp >> 8),(char)(temp >> 16),(char) (temp >>24));
        
        return;
}

        

