
/* all this stuff is new for the 6502 version */

#include <stdio.h>
#include "cc65.h"

/* flags for enabling/disabling parts of the optimizer.  mostly so
   jrd can figure out the most effective combination... */

/* #define OPT12  */

/* return t if if line matches str */
#ifdef M6502
#asm
	.globl	_matchstr
_matchstr:
	jsr	popax		; get str
	sta	ptr1
	stx	ptr1+1
	jsr	popax		; get line
	sta	ptr2
	stx	ptr2+1
	ldy	#0
match1:
	lda	(ptr1),y	; get a char from str
	beq	match2
	cmp	(ptr2),y	; compare to char from line
	bne	match3
	iny
	bne	match1
match2:
	ldx	#0
	lda	#1
	rts
match3:
	lda	#0
	tax
	rts
#endasm
#else
matchstr(line, str)
char * line;
char * str;
{
  for (; *str; ++line, ++str)
    if (*line != *str)
      return (0);
  return (1);
}

#endif

/* return t if line looks like " jsr <template>" */
matchjsr(line, template)
char * line;
char * template;
{
  if (!matchstr(line, "\tjsr\t"))
    return (0);
  return (matchstr(line + 5, template));
}

/* handed a pointer to beginning of line, return pointer to beginning
   of next line, or NIL, if there isn't one */
#ifdef M6502
#asm
	.globl	_nextline
_nextline:
	jsr	popax		; get line
	sta	ptr1
	stx	ptr1+1
	ldy	#0
nextl1:
	lda	(ptr1),y
	beq	nextl2		; oops! off the end
	cmp	#$9B		; eol?
	beq	nextl3		; yup, done
	iny
	bne	nextl1
nextl2:
	lda	#0
	tax
	rts
nextl3:
	iny			; past the eol
	lda	(ptr1),y
	beq	nextl2		; oops! no more data
	tya
	clc
	adc	ptr1
	bcc	nextl4
	inc	ptr1+1
nextl4:
	ldx	ptr1+1
	rts			; done
#endasm
#else
nextline(line)
char * line;
{
  for (; *line; ++line)
    if (*line == '\n')
      if (line[1] == '\0')
	return (0);
      else
	return (line + 1);
  return (0);
}

#endif

/* return ptr to str1 prepended to str2 */
prepend(str1, str2)
char * str1;
char * str2;
{
char * r;

  r = str2 = str2 - strlen(str1);
  for (; *str1;)
    {
      *str2 = *str1;
      ++str2;
      ++str1;
    }
  return (r);
}

/* return ptr to terminator of line */
/* ...
#ifdef M6502
#asm
	.globl	_findeol
_findeol:
	jsr	popax		; get l
	sta	ptr1
	stx	ptr1+1
	ldy	#0
findl1:
	lda	(ptr1),y
	cmp	#$9B		; eol?
	beq	findl2
	iny
	bne	findl1
findl2:
	tya
	clc
	adc	ptr1
	bcc	findl3
	inc	ptr1+1
findl3:
	ldx	ptr1+1
	rts
#endasm
#else
findeol(l)
char * l;
{
  while (*l != '\n')
    ++l;
  return (l);
}

#endif
...* /

/* handy helper fun used by a lot of optimizers below.  if find
   jsr matches of the two passed values, substitute the third value.
   NB!! returns 0 if didn't change anything! */
char * optpair(ptr, rtn1, rtn2, rtn3)
char * ptr;
char * rtn1;
char * rtn2;
char * rtn3;
{
char * l;

  if ((l = nextline(ptr)) == 0)
    return (ptr);
  if (matchjsr(ptr, rtn1) && matchjsr(l, rtn2))
    {
/*
	printmsg("match '%s'", rtn1);
	printmsg(", '%s'", rtn2);
	printmsg("-> '%s'\n", rtn3);
*/
      return (prepend("\tjsr\t", prepend(rtn3, strchr(l, '\n'))));
    }
  return (0);
}


/* actual optimizer routines.
   The basic idea here is that we get a pointer, find some number of
   next-line pointers, and look for obvious patterns that can be
   replaced by simpler ones.  when we find one, prepend the simpler
   pattern, and return the pointer to new beginning of buf. */

extern char * triples[];

opt_trip(ptr)
char * ptr;
{
char * new_ptr;
#ifdef smallc
int * triple;
#else
char ** triple;
#endif

  for (triple = triples; triple[0]; triple += 3)
    {
/*
	printf("optimizing triple '%s' '%s' '%s'\n",
		triple[0], triple[1], triple[2]);
*/
      if (new_ptr = optpair(ptr, triple[0], triple[1], triple[2]))
	{
	  ptr = new_ptr;
	}
    }
  return (ptr);
}

/* optimize ldax #0 followed by pushax into push0 */

opt11(ptr)
char * ptr;
{
char * l;
int i;

  if ((l = nextline(ptr)) == 0)
    return (ptr);
  if (matchstr(ptr, "\tldax\t#0") && matchjsr(l, "pushax"))
    return (prepend("\tjsr\tpush0", strchr(l, '\n')));
  return (ptr);
}

#ifdef OPT12
/* optimize "axzerop" followed by lbeq, to just reverse the sense
   of the test */

opt12(ptr)
char * ptr;
{
char * l;
int i;

  if ((l = nextline(ptr)) == 0)
    return (ptr);
  if (matchstr(l, "\tlbeq") && matchjsr(ptr, "axzerop"))
    {
      l[3] = 'n'; /* make it lbne */
      l[4] = 'e';
      return (l);		/* return the lbne */
    }
  return (ptr);
}

#endif

/* the optimizer main loop */
char * opt(ptr)
char * ptr;
{
char * lastptr;

  lastptr = 0;

  while (lastptr != ptr)
    {
      lastptr = ptr;
/*
	ptr = opt1(ptr);
	ptr = opt2(ptr);
	ptr = opt3(ptr);
	ptr = opt4(ptr);
	ptr = opt4a(ptr);
	ptr = opt5(ptr);
	ptr = opt6(ptr);
*/
      ptr = opt_trip(ptr);

/*	ptr = opt9(ptr);
	ptr = opt10(ptr);	*/
      ptr = opt11(ptr);
#ifdef OPT12
      ptr = opt12(ptr);
#endif
      /* more later */
    }
  return (ptr);
}

static char xxc;
peephole(ptr)
char * ptr;
{
  if (optimize)
    while (xxc = *ptr)
      {
	if (xxc == '\t') /* only check obvious lines */
	  ptr = opt(ptr);
	while ((xxc = *ptr) && (xxc != '\n'))
	  {
	    cout(xxc);
	    ++ptr;
	  }
	if (xxc)
	  {
	    cout(xxc);
	    ++ptr;
	  }
      }
  else
    sout(ptr);
}
