#!/bin/bash -xe
if [ "x$1" = "x" ]; then
    FIXIMAGE=0
    REVVIDEO=true
    { cat <<EOF
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
MPBrCWe3bRqXEttKKPJ4j/AXt2uC0AQAAA
EOF
}|base64 -d|zcat > screen.bin
else
    FIXIMAGE=1
    REVVIDEO=false
    convert "$1" -resize "640x200!" pgm:- \
	|pamditherbw|convert pam:- pbm:-|{
	x=0
	# skip header
	while read; do
	    if echo $REPLY|grep -q "^ *#"; then
		echo skip comment >&2
	    else
		x=$((x+1))
		if [ $x -ge 2 ]; then
		    break;
		fi
	    fi
	done
	cat
    } > screen.bin
fi
iDSK c.dsk -n -i screen.bin -t 1

# todo: many emulators hang on printer output!
{ cat <<EOF
print #8,"hello world from basic to the printer port"
mode 2
memory &4000-1
$(if ${REVVIDEO}; then
     echo "border 0:ink 0,0:ink 1,26";
  else
     echo "border 26:ink 0,26:ink 1,0"; fi)
load "c.bin",&8000
load "screen.bin",&c000
call &8000
while(1)
 call &bd19 ' sync to blank
 if inkey(67)=0 or (joy(0) and 1) then |scrollup
 if inkey(69)=0 or (joy(0) and 2) then |scrolldown
 if inkey(34)=0 or (joy(0) and 4) then |scrollleft
 if inkey(27)=0 or (joy(0) and 8) then |scrollright
wend
EOF
}|nl -w1|unix2dos>c.bas
iDSK c.dsk -i c.bas -t 0
cat > crt0_cpc.s <<EOF
;; FILE: crt0.s
;; Generic crt0.s for a Z80
;; From SDCC..
;; Modified to suit execution on the Amstrad CPC!
;; by H. Hansen 2003

.module crt0
	.globl	_main
	.globl  _rsx_workspace
	.globl  _rsx_jump_table

	.area _HEADER (ABS)
;; Reset vector
	.org 	0x8000
	jp	init

	.org	0x$(bc <<<"obase=16;ibase=16;8000+10")

init:
	;; Initialise global variables
	call    gsinit
	;; rsx init
	ld hl,#_rsx_workspace
	ld bc,#_rsx_jump_table
	call #0xbcd1
	call	_main
	jp	_exit

;; Ordering of segments for the linker.
	.area	_HOME
	.area	_CODE
  .area   _GSINIT
  .area   _GSFINAL

	.area	_DATA
  .area   _BSS
  .area   _HEAP

  .area   _CODE
__clock::
	ret

_exit::
	ret

	.area   _GSINIT
gsinit::

.area   _GSFINAL
    	ret

EOF
sdasz80 -o crt0_cpc.s
cat > util.s <<'EOF'
.area _CODE
.globl  _scroll_down
.globl  _scroll_up
.globl  _scroll_left
.globl  _scroll_right

_putchar::
	ld a,l
1$:
	; output to printer
	; (todo: many emulators hang on printer output!)
	call 0xbd2b
	jp nc,1$
	ret

_get_time::
	call #0xbd0d ;kl time please
	ex de,hl
	ret

_rsx_jump_table::
	.word name_table  ;address pointing to RSX commands
	jp _scroll_down
	jp _scroll_up
	jp _scroll_left
	jp _scroll_right

name_table:
	.str "SCROLLDOW"
	.byte "N"+0x80
	.str "SCROLLU"
	.byte "P"+0x80
	.str "SCROLLLEF"
	.byte "T"+0x80
	.str "SCROLLRIGH"
	.byte "T"+0x80
	.byte 0           ;end of name table marker

_rsx_workspace::          ;Space for kernel to use
.byte 0,0,0,0
EOF
sdasz80 -o util.s

cat > c.c <<EOF
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

inline void sync_to_blank(void)
{
__asm
	call #0xbd19 ; sync to blank
__endasm;
}
inline void wait_for_keypress(void)
{
__asm
	call #0xbb18
__endasm;
}
void scroll_down(void);
void scroll_up(void);
void scroll_left(void);
void scroll_right(void);
uint16_t get_time(void);

#if $FIXIMAGE
void fix_image(void);
#endif

/*
   scroff : screen offset [0,2046]
   x      : x coordinate  [0,639]
   y      : y coordinate  [0,199]
   returns pointer to screen byte
   credits go to lightforce6128
*/
inline
char* scr_dot_position(uint16_t screen_offset, uint16_t x, uint8_t y) {
       uint16_t x_offset    = x / 8; /* mode 2: 8, mode 1: 4, mode 0: 2 */
       uint16_t y_offset    = (uint16_t)(y / 8) * 80;
       uint16_t y_scanline  = (uint16_t)(y % 8) * 2048;
       return (char*)0xC000
       	      + ((screen_offset + x_offset + y_offset) % 2048)
	      + y_scanline;
}

#define SCR_NEXT_LINE(p) do{p+=0x800;if (!((uint16_t)p&0x3800)) { \
	p-=0x4000;char a=((uint16_t)p)>>11;p+=0x50;char b=((uint16_t)p)>>11; \
	if (a!=b) p-=0x800;};}while(0)

/* todo: macro version? */
inline char* scr_prev_line(char* p) {
      uint16_t x=(uint16_t)p;
      x-=0x800;
      if (((x>>8)&0x38)==0x38) {
      	 x+=0x4000;
	 char a=x>>11;
	 x-=0x50;
	 char b=x>>11;
	 if (a!=b) x+=0x800;
      }
      return (char*)x;
}

#define SCR_NEXT_BYTE(x) do{ \
	++x;if (!((uint16_t)x&0x7ff)) ((char*)(&x))[1]-=0x8;}while(0)

/* todo: test pass by reference? */
inline char* scr_prev_byte(char* x) {
       if (!((uint16_t)x&0x07ff)) x+=0x800;
       return --x;
}
#define SCR_PREV_BYTE(x) do{ \
	if (!((uint16_t)x&0x07ff)) ((char*)(&x))[1]+=0x8; --x;}while(0)

void main(void)
{
  uint16_t f,dt,s;

  printf("hello world from C to the printer port\r\n");

#if $FIXIMAGE
  fix_image();
#endif

  s = get_time();
  for (f=0;f<50;++f) {
	sync_to_blank();
	scroll_down();
  }
  dt=(get_time()-s);
  printf("%2dframes/s\r\n",f*300/dt);

  s = get_time();
  for (f=0;f<50;++f) {
	sync_to_blank();
	scroll_up();
  }
  dt=(get_time()-s);
  printf("%2dframes/s\r\n",f*300/dt);

  s = get_time();
  for (f=0;f<40;++f) {
	sync_to_blank();
	scroll_right();
  }
  dt=(get_time()-s);
  printf("%2dframes/s\r\n",f*300/dt);

  s = get_time();
  for (f=0;f<40;++f) {
	sync_to_blank();
	scroll_left();
  }
  dt=(get_time()-s);
  printf("%2dframes/s\r\n",f*300/dt);
}

inline char* scr_line_position(uint16_t scroff, char y)
{
	uint16_t y_offset    = (uint16_t)(y / 8) * 80;
	uint16_t y_scanline  = (uint16_t)(y % 8) * 2048;
	return (char*)0xC000 + ((scroff + y_offset) % 2048) + y_scanline;
}

void set_offset(uint16_t newoffset) {
	__asm
	call #0xbc05
	__endasm;
}

/*
  The video memory wraps on every 2048(0x800) bytes
  boundary.  The idea is to calculate how many bytes we can
  copy without crossing a boundary.

  The from or the to pointer might cross the boundary first
  or we might not cross any boundary at all.

  A row is 80 Bytes (<2048) => we can cross a boundary only
  once.

  note: the memory areas might overlap
  todo: memcpy doesn't allow overlap => use memmove?
  (unfortunately the memmove doesn't inline
  for now stay with memcpy which maps to ldir
  note/todo:
  looks like ldir isn't the fastest method to copy bytes)
*/
void copy_row(char* f, char* t)
{
	uint16_t fc;
	uint16_t tc;
	char count;
	char c;
	fc=(uint16_t)0x800-((uint16_t)f&0x7ff);
	tc=(uint16_t)0x800-((uint16_t)t&0x7ff);
	count=80;
	if (fc<tc) {
	   if (count<fc) {
	      memcpy(t,f,count);
	   }else{
		c=fc;
	      	memcpy(t,f,c);
	      	f=f-0x800+c;
	      	t=t+c;
	      	tc=tc-c;
	      	count=count-c;
	      	if (count<tc) {
		   memcpy(t,f,count);
		}else{
			c=tc;
			memcpy(t,f,c);
			f=f+c;
			t=t-0x800+c;
			count=count-c;
			memcpy(t,f,count);
		}
	   }
	}else{
	   if (count<tc) {
	      memcpy(t,f,count);
	   }else{
		c=tc;
	      	memcpy(t,f,c);
	      	f=f+c;
	      	t=t-0x800+c;
	      	fc=fc-c;
	      	count=count-c;
	      	if (count<fc) {
		   memcpy(t,f,count);
		}else{
			c=fc;
			memcpy(t,f,c);
			f=f-0x800+c;
			t=t+c;
			count=count-c;
			memcpy(t,f,count);
		}
	   }
	}
}

void scroll_down(void)
{
	/* firmware keeps offset there */
	uint16_t oldoffset=*((uint16_t *)0xb7c4);
	uint16_t newoffset=(oldoffset+80)%2048;
	set_offset(newoffset);
	char *f;
	char *t;
	for (char y=0;y<8;++y) {
	    f=scr_line_position(oldoffset, y);
	    t=scr_line_position(newoffset, 200-8+y);
	    copy_row(f,t);
	}
}

void copy_row_reverse(char* f, char* t)
{
	uint16_t fc;
	uint16_t tc;
	char count;
	char c;
	fc=((uint16_t)f&0x7ff)+1;
	tc=((uint16_t)t&0x7ff)+1;
	count=80;
	if (fc<tc) {
	   if (count<fc) {
	      memmove(t-count+1,f-count+1,count);
	   }else{
		c=fc;
	      	memmove(t-c+1,f-c+1,c);
	      	f=f+0x800-c;
	      	t=t-c;
	      	tc=tc-c;
	      	count=count-c;
	      	if (count<tc) {
		   memmove(t-count+1,f-count+1,count);
		}else{
			c=tc;
			memmove(t-c+1,f-c+1,c);
			f=f-c;
			t=t+0x800-c;
			count=count-c;
			memmove(t-count+1,f-count+1,count);
		}
	   }
	}else{
	   if (count<tc) {
	      memmove(t-count+1,f-count+1,count);
	   }else{
		c=tc;
	      	memmove(t-c+1,f-c+1,c);
	      	f=f-c;
	      	t=t+0x800-c;
	      	fc=fc-c;
	      	count=count-c;
	      	if (count<fc) {
		   memmove(t-count+1,f-count+1,count);
		}else{
			c=fc;
			memmove(t-c+1,f-c+1,c);
			f=f+0x800-c;
			t=t-c;
			count=count-c;
			memmove(t-count+1,f-count+1,count);
		}
	   }
	}
}

void scroll_up(void)
{
	uint16_t oldoffset=*((uint16_t *)0xb7c4);
	uint16_t newoffset=(oldoffset-80)%2048;
	set_offset(newoffset);
	char *f;
	char *t;
	for (char y=0;y<8;++y) {
	    f=scr_dot_position(oldoffset, 639, (int)200-8+y);
	    t=scr_dot_position(newoffset, 639, y);
	    /* note:
	       we copy from right to left because of overlap */
	    copy_row_reverse(f,t);
	}
}

/* todo: slow */
void scroll_left(void)
{
	uint16_t oldoffset=*((uint16_t *)0xb7c4);
	uint16_t newoffset=(oldoffset-2)%2048;
	set_offset(newoffset);
	char *fl;
	char *tl;
	char *f;
	char *t;
	char y=0;
	fl=scr_dot_position(oldoffset, 640-16, y);
	tl=scr_dot_position(newoffset, 0, y);
	while(1) {
	    *tl=*fl;
	    t=tl;
	    f=fl;
	    SCR_NEXT_BYTE(f);
	    SCR_NEXT_BYTE(t);
	    *t=*f;
	    ++y;
	    if (y==200) break;
	    SCR_NEXT_LINE(fl);
	    SCR_NEXT_LINE(tl);
	}
}

/* todo: slow */
void scroll_right(void)
{
	uint16_t oldoffset=*((uint16_t *)0xb7c4);
	uint16_t newoffset=(oldoffset+2)%2048;
	set_offset(newoffset);
	char *fl;
	char *tl;
	char *f;
	char *t;
	/* note: copy in reverse because of overlap */
	char y=199;
	fl=scr_dot_position(oldoffset, 8, y);
	tl=scr_dot_position(newoffset, 640-8, y);
	while(1) {
	    *tl=*fl;
	    t=tl;
	    f=fl;
	    SCR_PREV_BYTE(f);
	    SCR_PREV_BYTE(t);
	    *t=*f;
	    --y;
	    if (y==255) break;
	    fl=scr_prev_line(fl);
	    tl=scr_prev_line(tl);
	}
}

#if $FIXIMAGE
void fix_image(void) {
     for (char y=0;y<200;++y) {
     	 char* src=(char*)0xc000+(uint16_t)y*80;
     	 char* dest=scr_line_position(0,y)-0x8000;
	 memcpy(dest,src,80);
     }
     // memset((char*)0xc000,0,0x4000);
     memcpy((char*)0xc000,(char*)0x4000,0x4000);
}
#endif
EOF
sdcc -mz80 \
     --code-loc 0x$(bc <<<"obase=16;ibase=16;8000+38") \
     --data-loc 0 \
     --no-std-crt0 crt0_cpc.rel util.rel c.c
makebin -yo A -p c.ihx|tail -c +$((0x8000+1))>c.bin
iDSK c.dsk -i c.bin -e 8000 -c 8000

rm -vf printer.txt
mame cpc664 -flop1 c.dsk -skip_gameinfo -ab '\n\nrun "c\n' \
     -prin printer.txt \
     -snapsize 768x544 -aviwrite /tmp/p.avi -str 25
AV_LOG_FORCE_NOCOLOR=1 ffmpeg -ss 0.5 -y \
		       -i /tmp/p.avi \
		       -r 25 -pix_fmt yuv420p \
		       -q:a 0.1 /tmp/p.mp4
cat printer.txt
