#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <malloc.h>

typedef      unsigned char   BYTE;
typedef      unsigned int    WORD;
typedef      unsigned long   DWORD;
typedef      int             BOOL;
typedef      DWORD           FOURCC;
typedef      char far *      LPSTR;
typedef      BYTE far *      LPBYTE;
typedef      WORD far *      LPWORD;
typedef      DWORD far *      LPDWORD;
typedef      BYTE huge *      HPBYTE;
typedef      WORD huge *      HPWORD;
typedef      DWORD huge *      HPDWORD;

#define      BSIZE            256
#define      FALSE            0x0000
#define      TRUE            (!FALSE)
#define      VIDERR_OK      0x0000

#define      HIWORD(a)      ((a) >> 16)
#define      LOWORD(a)      ((a) & 0xFFFF)
#define      HIBYTE(a)      ((a) >> 8)
#define      LOBYTE(a)      ((a) & 0xFF)

#define      makeFOURCC(a,b,c,d)            ((DWORD) (((DWORD) d)<<24|((DWORD) c)<<16|((DWORD) b)<<8|((DWORD) a)))

#define comptypeDIB      makeFOURCC(0,0,0,0)
#define comptypeMSVIDEO      makeFOURCC('M','S','V','C')

#define      ID_Unknown            0x00
#define      ID_ProMovieSpectrumNTSC      0x01
#define      ID_ProMovieSpectrumPAL      0x02
#define      MAXBUFFER 20

extern LPSTR      glpcBoardName;
extern DWORD      Buffer;
extern DWORD      FrameCount;
extern DWORD      GetBuffer();
extern WORD       JSeg,JOff;
extern WORD       wRow,wColumn,VPos,VSlice;
extern WORD       HBytes,HAdjust;
extern WORD       BUFoff,BUFseg;
extern WORD       VGAoff;
extern WORD       N2Move;
extern void       VGAput();


    static      BYTE      bPMS[48];
    static      LPBYTE    B2C;
    static      WORD      seg_B2C;
    static      WORD      off_B2C;
    static      WORD      cuts[200];
    static      WORD      FrameSize;

    static      WORD      gwBoardID;
    static      WORD      wPMSPort = 0x250;
    static      WORD      wPMSAddr = 0xC800;
    static      WORD      wPMSIRQ  = 10;
    static      WORD      wIntrNum;
    static      WORD      wVideoStandard;
    static      LPWORD    lpwVGA;
    static      DWORD     OldBuffer;

    static      BOOL      CANAL = FALSE;
    static      BOOL      CLEAN = FALSE;
    static      BOOL      GREY  = FALSE ;

    static      int            iPMSBoardID;
    static      int            iPMSID;

    static      int       lborder;
    static      int       rborder;
    static      int       startline;
    static      int       endline;
    static      int       topline;
    static      int       botline;
    static      int       minoffset;
    static      int       maxoffset;
    static      int       countdown;
    static      int       XORminsum = 200;

static void (cdecl interrupt far *pOldVector)();
//extern void cdecl interrupt far JJDeviceISR(void);
extern void       DeviceISR();


/************************************************************************/
void JJSetInterruptVector(
      WORD wIRQ
)
{
    if(wIRQ<8)
      wIntrNum = wIRQ + 0x08;
    else
      wIntrNum = (0x70 - 0x08) + wIRQ;

    if(!pOldVector)
        pOldVector = _dos_getvect(wIntrNum);
//    _dos_setvect(wIntrNum,JJDeviceISR);

}

WORD JJSetInterruptMask(
      WORD wIRQ,
      WORD wIntMask)
{
    BYTE      btmp;
    BYTE      bOldMask;
    WORD      wPICReg;

    if(wIRQ<8)
        wPICReg = 0x21;
    else
        wPICReg = 0xA1;

    btmp = 0x0001 << (wIRQ & 0x0007);
    wIntMask =
        (      ((wIntMask & 0x0001) << (wIRQ & 0x0007))) & btmp;


    _disable();
    bOldMask = inp(wPICReg);
    outp(wPICReg,(bOldMask & (      btmp)) | wIntMask);
    _enable();
    return(bOldMask);
}

void PMSWrite (nIndex, nValue)
int nIndex;
int nValue;
{
    WORD      i;

    if(wPMSPort) {
          i=0x1000;
          while((--i)&&((inp(wPMSPort)&0x03)!=0x03));
          if(!i) printf("PMSWrite failed to write index (0x%02X)\n",nIndex);
          outpw (wPMSPort, (nValue << 8) | nIndex);
          bPMS[nIndex] = nValue;

    }
}

unsigned int PMSRead(wIndex)
WORD wIndex;
{
    return(bPMS[wIndex]);
}

int PMSReadReg(wIndex)
WORD wIndex;
{
    WORD      i;

    i=0x1000;
    while((--i)&&((inp(wPMSPort)&0x01)!=0x01));
    if(i) {
    } else {
      printf("PMSReadReg failed to write index (0x%02X)\n",wIndex);
      return(0xFF);
    }
    outp(wPMSPort,0x80 | wIndex);

    i=0x1000;
    while((--i)&&(inp(wPMSPort)&0x04));
    if(i)
      return(inp(wPMSPort+1));
    else {
      printf("PMSReadReg failed to read register (0x%02X)\n",wIndex);
      return(0xFF);
    }
}



void PMSIRQEnable(fEnable)
BOOL fEnable;
{
   if(fEnable) {
       SetInterruptVector(wPMSIRQ,(&DeviceISR));
       SetInterruptMask(wPMSIRQ,0x01);
       PMSWrite(0x10,PMSReadReg(0x10) | 0x03);
       PMSWrite(0x01,PMSReadReg(0x01) | 0x01);
   } else {
       PMSWrite(0x01,PMSRead(0x01) | 0xFE);
       PMSWrite(0x10,PMSRead(0x10) & 0xFC);
       SetInterruptMask(wPMSIRQ,0x00);
   }
}

void PMSSetVertDeci(wDeciNum,wDeciDen)
WORD wDeciNum;
WORD wDeciDen;

{
    printf("VertDeci Num,Den %d %d\n",wDeciNum,wDeciDen);
    if((wDeciNum%5)==0) {
      wDeciDen /= 5;
      wDeciNum /= 5;
    }
    while(((wDeciNum%3)==0)&&((wDeciDen%3)==0)) {
      wDeciDen /= 3;
      wDeciNum /= 3;
    }
    while((((wDeciNum&0x0001)|(wDeciDen&0x0001))==0x0000)
      ) {
      wDeciDen >>= 1;
      wDeciNum >>= 1;
    }
    while((wDeciDen>32)) {
      wDeciDen >>= 1;
      wDeciNum = (wDeciNum+1)>>1;
    }
    if(wDeciDen==32) {
      wDeciDen--;
    }

    if(wDeciDen==2) {
      wDeciDen |= 0x80;
    }


    printf("VertDeci Num,Den %d %d\n",wDeciNum,wDeciDen);
    PMSWrite(0x1C,wDeciDen);
    PMSWrite(0x1D,wDeciNum);
}

void PMSSetHorzDeci(wDeciNum,wDeciDen)
WORD wDeciNum;
WORD wDeciDen;

{
    printf("HorzDeci Num,Den %d %d\n",wDeciNum,wDeciDen);
    if((wDeciNum%5)==0) {
      wDeciDen /= 5;
      wDeciNum /= 5;
    }

    while((((wDeciNum&0x0001)|(wDeciDen&0x0001))==0x0000)
      ) {
      wDeciDen >>= 1;
      wDeciNum >>= 1;
    }
    while((wDeciDen>32)) {
      wDeciDen >>= 1;
      wDeciNum = (wDeciNum+1)>>1;
    }
    if(wDeciDen==32) {
      wDeciDen--;
    }

    printf("HorzDeci Num,Den %d %d\n",wDeciNum,wDeciDen|0x80);
    PMSWrite(0x24,0x80 | wDeciDen);
    PMSWrite(0x25,wDeciNum);


}
unsigned int IdentifyPMSBoard()
{
    BYTE BType;
    PMSWrite(0x02,0);
    BType = PMSReadReg(0x02) & 0x80;
    printf("Board Type read %x\n",BType);
    if(BType==0x80) {
      wVideoStandard = 2;
      printf("This board is PAL\n");
      return(ID_ProMovieSpectrumPAL);
    } else {
      wVideoStandard = 1;  /* should be 1 ! */
      printf("This board is NTSC\n");
      return(ID_ProMovieSpectrumNTSC);
    }
}

BOOL WakeUpPMSpectrum(wPort)
WORD wPort;
{
    int            i;
    int            iTmp;

    printf("WakeUpPMS: wPMSPort=%04X\n",wPMSPort);

    outp(wPMSPort+2,0x01);
    inp(wPMSPort);
    inp(wPMSPort);
    inp(wPMSPort);
    inp(wPMSPort);
    inp(wPMSPort);
    outp(wPMSPort+2,0x00);

    i=0x1000;
    while((--i)&&((iTmp=inp(wPMSPort))&0x04));
    inp(wPMSPort+1);

    iTmp = PMSReadReg(0x03);

    if((iTmp&0xF0)!=0x10)
        return(FALSE);

    return(TRUE);
}



void PMSIRQClear ()
{
    PMSWrite(0x00,PMSRead(0x00) | 0x01);
}

void SetPMSVideoSource(wID,wData)
WORD wID;
WORD wData;
{
    PMSWrite(0x2E,(PMSRead(0x2E) & 0xFE) | (wData & 0x01));
}

void SetPMSHue(wID,wData)
WORD wID;
WORD wData;

{
    printf("Set Hue to %d\n",wData);
    PMSWrite(0x26,wData);
    printf(" Confirm %d \n",PMSRead(0x26));
}

void SetPMSColor(wID,wData)
WORD wID;
WORD wData;

{
    printf("Set Colour to %d",wData);
    PMSWrite(0x2C,wData);
    printf(" Confirm %d \n",PMSRead(0x2C));
}

void SetPMSContrast(wID,wData)
WORD wID;
WORD wData;

{
//  printf("Set Contrast to %d",wData);
    PMSWrite(0x2A,wData);
//  printf(" Confirm %d \n",PMSRead(0x2A));
}

void SetPMSBrightness(wID,wData)
WORD wID;
WORD wData;

{
    printf("Set Brightness to %d\n",wData);
    PMSWrite(0x27,wData);
    PMSWrite(0x28,wData);
    PMSWrite(0x29,wData);
}

void SetPMSHStart(wID,wData)
WORD wID;
WORD wData;

{
    printf("Set Horizontal Start to %d  ",wData);
    PMSWrite(0x20,wData);
    PMSWrite(0x21,(wData & 0x0300)>>8);
    printf(" Confirm %d \n",PMSReadReg(0x20));
}

void SetPMSVStart(wID,wData)
WORD wID;
WORD wData;
{
    PMSWrite(0x16,wData);
    PMSWrite(0x17,(wData & 0x0300)>>8);
}

void CursorPos(y,x)
int x,y;
{
  wRow = x;
  wColumn = y;
  SetCursorPos();
}

void ConDitherRGB()
{
WORD ScanBytes = HBytes;
WORD Line = FrameSize;
WORD iDS = FP_SEG(OldBuffer);
WORD iSI = FP_OFF(OldBuffer);
_asm {
      pushf
      push     es
      push     ds
      push     di
      push     si

      mov       ax, iDS
      mov       ds, ax
      mov       es, ax
      mov       si, iSI
      mov       di, iSI

DoScanLine:
      cld
      xor       bx, bx          ; zero error field
      mov       cx, ScanBytes
DoPutBytes:
      lodsw
      test     ax,4210h

      jz       Dither

      ;Add in previous error in BX (carefully)
      and      ax,7BDFh     ;Clear lsb of pixel color fields
      and      bx,7BDFh     ;Clear lsb of error color fields
      add      ax,bx
      mov      bx,ax
      test     bh,80h           ;Test for R overflow
      jz       DitherG
      or       ah,7Ch
DitherG:
      test     bh,04h           ;Test for G overflow
      jz       DitherB
      or       ax,03E0h
DitherB:
      test     bl,20h           ;Test for B overflow
      jz       DitherDone
      or       ax,001Fh
DitherDone:
      xor      bx,bx

Dither:
      add      ax,bx

      mov      bx,ax

      and      bx,1C67h     ;Save discarded bits
      push     bx
      mov      bx,ax

      shr      al,3
      and      al,03h           ;Blue

      and      ah,60h
      or       al,ah            ;Red

      shl      bx,3
      and      bh,1Ch
      or       al,bh            ;Green

      pop bx
      stosb
      loop     DoPutBytes

      dec      Line
      jnz      DoScanLine

      pop      si
      pop      di
      pop      ds
      pop      es
      popf
     }
}
void ConDitherGrey()
{
WORD ScanBytes = HBytes;
WORD Line = FrameSize;
WORD iDS = FP_SEG(OldBuffer);
WORD iSI = FP_OFF(OldBuffer);
_asm {
      pushf
      push     es
      push     ds
      push     di
      push     si

      mov       ax, iDS
      mov       ds, ax
      mov       es, ax
      mov       si, iSI
      mov       di, iSI

DoScanLine:
      cld
      mov       cx, ScanBytes
DoPutBytes:
      lodsw
      shr       ax, 10
      stosb
      loop     DoPutBytes

      dec      Line
      jnz      DoScanLine

      pop      si
      pop      di
      pop      ds
      pop      es
      popf
     }
}
void GetRGBSwap()
{
WORD iDS = FP_SEG(OldBuffer);
WORD iSI = FP_OFF(OldBuffer);
WORD iES = seg_B2C;
WORD iDI = off_B2C;
WORD ScanBytes = HBytes;
WORD iST,FirstP,LastP,start,end;
WORD iSI0 = iSI;
int i,ic;
int lb = lborder;
int rb = rborder;
int line = startline;
int endl = endline;
int maxo = HBytes;
int split,bbest;

int mins[200],Sum,amin;



iSI += (line-1)*HBytes;


_asm {
      pushf
      pusha
      push     ds
      push     si
      push     es
      push     di

      mov      ax, iDS           ; data segment
      mov      ds, ax            ; in DS

line_loop:
      mov      si, iSI
      mov      ax, iSI           ; address of scan line
      mov      start, ax
      mov      bx, lb
      add      start, bx         ; first pixel address
      add      ax, ScanBytes     ; iSI + ScanBytes i.e. next line address
      dec      ax                ; last pixel this line
      sub      ax, rb            ; next line minus right hand border
      mov      end, ax           ; last pixel to consider on this line
      sub      ax, ScanBytes
      mov      LastP, ax         ; last considered pixel on previous
      mov      ax, start
      sub      ax, ScanBytes
      mov      FirstP, ax        ; first considered pixel on previous

      mov      amin, 0EFFFh      ; min summed pixel xor
      xor      cx, cx            ; split value to start with is 0
      mov      bbest, cx
      cld
offset_loop:
      mov      dx, 0             ; zero sum
      mov      si, start         ; set address of start pixel
      add      si, cx            ; this split value
      mov      bx, FirstP        ; set address of start pixel on previous
      mov      ax, iES
      mov      es, ax
      xor      ah, ah            ; set top byte to zero
compare_loop:
      mov      al, ds:[si]       ; this pixel
      xor      al, ds:[bx]       ; should be zero for perfect match
      mov      di, iDI           ; bit count table offset
      add      di, al            ; count offset
      mov      al, es:[di]       ; bit count for this al
      add      dx, ax            ; sum bits set
      cmp      dx, amin          ; test against best
      jae      test_ok           ; jump to next if sum already too big
      inc      si                ; move address this line on one
      inc      bx                ; move address prev line on one
      cmp      bx, LastP         ; check all prev line pixels compared
      ja       end_of_line
      cmp      si, end           ; end of this line ?
      jbe      compare_loop
      mov      si, start         ; continue at the beginning
      jmp      compare_loop
end_of_line:
      cmp      dx, amin
      jae      test_ok
      mov      amin, dx          ; this dx is the best
      mov      bbest, cx         ; set bbest to the good cut point
test_ok:
      inc      cx             ; next value for split
      cmp      cx, maxo       ; have we done the last possible split offset?
      jbe      offset_loop    ; if not, jump to it

      mov      bx, start
      add      bx, bbest
      mov      ds:[bx], 0FFh  ; mark eventual left border
      mov      bx, end
      mov      ds:[bx], 0FFh ; mark right border
do_swap:
      cmp      bbest, 0      ; don't need to swap if best is 0
      je       done_swap
; now swap the line at the bbest position. Use iSI0 as a line buffer
      mov      ax, iDS
      mov      es, ax
      mov      di, iSI0       ; destination address is offset 0
      mov      si, start      ; source address is first considered pixel
      cld
      mov      cx, bbest      ; need to save the first bbest bytes
rep   movsb                   ; copy bbest bytes from si to di
      mov      di, start      ; prepare destination address
      mov      si, start
      add      si, bbest      ; prepare source address
      mov      cx, end
      sub      cx, si         ; number of bytes to move to 'end'
rep   movsb
      mov      cx, bbest      ; now saved buffer
      mov      si, iSI0
rep   movsb                   ; add the saved bbest bytes at the end

done_swap:
}
   cuts[line] = bbest;
   mins[line] = amin;
_asm {

      inc      line
      mov      ax, line
      cmp      ax, endl       ; last scan line to do ?
      ja       Finished
      mov      ax, iSI
      add      ax, ScanBytes  ; move iSI to start of next scanline
      mov      iSI, ax
      jmp      line_loop
Finished:
      pop      di
      pop      es
      pop      si
      pop      ds
      popa
      popf
     }
//   CursorPos(25,1);
//   printf("Line Cut Val");
//   ic = 2;
//   for(i=startline;i<=endline;i++) {
//      CursorPos(25,ic++);
//      printf("%3d %3d %4d",i,cuts[i],mins[i]);
//   }
}
void GetGreySwap()
{
WORD iDS = FP_SEG(OldBuffer);
WORD iSI = FP_OFF(OldBuffer);
WORD iES = seg_B2C;
WORD iDI = off_B2C;
WORD ScanBytes = HBytes;
WORD iST,FirstP,LastP,start,end;
WORD iSI0 = iSI;
int i,ic;
int lb = lborder;
int rb = rborder;
int line = startline;
int endl = endline;
int maxo = HBytes-maxoffset;
int mino = minoffset;
int split,bbest;

int c[200],mins[200],Sum,amin;

iSI += (startline-1)*HBytes;


_asm {
      pushf
      pusha
      push     ds
      push     si
      push     es
      push     di

      mov      ax, iDS           ; data segment
      mov      ds, ax            ; in DS

line_loop:
      mov      si, iSI
      mov      ax, iSI           ; address of scan line
      mov      start, ax
      mov      bx, lb
      add      start, bx         ; first pixel address
      add      ax, ScanBytes     ; iSI + ScanBytes i.e. next line address
      dec      ax                ; last pixel this line
      sub      ax, rb            ; next line minus right hand border
      mov      end, ax           ; last pixel to consider on this line
      sub      ax, ScanBytes
      mov      LastP, ax         ; last considered pixel on previous
      mov      ax, start
      sub      ax, ScanBytes
      mov      FirstP, ax        ; first considered pixel on previous

      mov      amin, 0EFFFh      ; min summed pixel xor
      xor      cx, cx            ; split value to start with is 0
      mov      bbest, cx
      cld
offset_loop:
      mov      dx, 0             ; zero sum
      mov      si, start         ; set address of start pixel
      add      si, cx            ; this split value
      mov      bx, FirstP        ; set address of start pixel on previous
      mov      ax, iES
      mov      es, ax
      xor      ah, ah            ; set top byte to zero
compare_loop:
      mov      al, ds:[si]       ; this pixel
      xor      al, ds:[bx]       ; should be zero for perfect match
      cmp      al, 0
      je       L1
      mov      di, iDI           ; bit count table offset
      add      di, al            ; count offset
      mov      al, es:[di]       ; bit count for this al
      add      dx, ax            ; add to sum if the pixels are diff
      cmp      dx, amin          ; test against best
      jae      test_ok           ; jump to next if sum already too big
L1:
      inc      si                ; move address this line on one
      inc      bx                ; move address prev line on one
      cmp      bx, LastP         ; check all prev line pixels compared
      ja       end_of_line
      cmp      si, end           ; end of this line ?
      jbe      compare_loop
      mov      si, start         ; continue at the beginning
      jmp      compare_loop
end_of_line:
      cmp      dx, amin
      jae      test_ok
      mov      amin, dx          ; this dx is the best
      mov      bbest, cx         ; set bbest to the good cut point
      cmp      cx, 0
      ja       test_ok
      mov      cx, mino
      dec      cx
test_ok:
      inc      cx             ; next value for split
      cmp      cx, maxo       ; have we done the last possible split offset?
      jbe      offset_loop    ; if not, jump to it

      mov      bx, start
      add      bx, bbest
      mov      ds:[bx], 0FFh  ; mark eventual left border
      mov      bx, end
      mov      ds:[bx], 0FFh ; mark right border
do_swap:
      cmp      bbest, 0      ; don't need to swap if best is 0
      je       done_swap
; now swap the line at the bbest position. Use iSI0 as a line buffer
      mov      ax, iDS
      mov      es, ax
      mov      di, iSI0       ; destination address is offset 0
      mov      si, start      ; source address is first considered pixel
      cld
      mov      cx, bbest      ; need to save the first bbest bytes
rep   movsb                   ; copy bbest bytes from si to di
      mov      di, start      ; prepare destination address
      mov      si, start
      add      si, bbest      ; prepare source address
      mov      cx, end
      sub      cx, si         ; number of bytes to move to 'end'
rep   movsb
      mov      cx, bbest      ; now saved buffer
      mov      si, iSI0
rep   movsb                   ; add the saved bbest bytes at the end

done_swap:
}
   c[line] = bbest;
   mins[line] = amin;
_asm {

      inc      line
      mov      ax, line
      cmp      ax, endl       ; last scan line to do ?
      ja       Finished
      mov      ax, iSI
      add      ax, ScanBytes  ; move iSI to start of next scanline
      mov      iSI, ax
      jmp      line_loop
Finished:
      pop      di
      pop      es
      pop      si
      pop      ds
      popa
      popf
     }
   CursorPos(25,1);
   printf("Line Cut Val");
   ic = 2;
   for(i=startline;i<=endline;i++) {
      CursorPos(25,ic++);
      printf("%3d %3d %5d",i,c[i],mins[i]);
   }
}


void DoSort()
{
WORD iDS = FP_SEG(OldBuffer);
WORD iSI = FP_OFF(OldBuffer);
WORD iES = seg_B2C;
WORD iDI = off_B2C;
WORD ScanBytes = HBytes;
WORD FirstP,LastP,start,end,start2;
WORD iEN = iSI;
WORD iSI0 = iSI;
int i,ic;
int lb = lborder;
int rb = rborder;
int line;
int maxo = maxoffset;
int mino = minoffset-1;
int top = topline;

int bot = botline;
int NPix = HBytes-lborder-rborder-1;
int LDone,amin,bbest;
int NLines = botline-topline+1;
int Lframe = HBytes*NLines;
int mins[500];
int Minimum = XORminsum;
iEN += (botline-1)*HBytes+lb; // last considered start pixel


line = startline;
for (i=0;i<NLines;i++) {
   if(line>botline) line = line-botline+topline-1;
   start = (line-1)*HBytes + iSI + lb;
   end = start + NPix;
   _asm {
      pushf
      pusha
      push     ds
      push     si
      push     es
      push     di

      mov      ax, iDS           ; data segment
      mov      ds, ax            ; in DS
      mov      es, ax            ; and ES
      mov      cx, 1             ; First line to consider is 1 away
      mov      bbest, cx
      mov      amin, 0EFFFh      ; min summed pixel xor
      cld
offset_loop:
      mov      si, start         ; start parent line pixel address
      mov      ax, cx            ; line offset in ax
      mul      ScanBytes         ; number of bytes ahead
      add      ax, si            ; start address for compare
      cmp      ax, iEN           ; wrap back to top ?
      jbe      nowrap
      sub      ax, Lframe        ; wrap offset
nowrap:
      mov      FirstP, ax        ; i.e. first pixel for match
      add      ax, NPix          ; add number of pixels to match
      mov      LastP, ax         ; last pixel for match
      mov      dx, 0             ; zero sum for this pair
      mov      bx, FirstP        ; set address of start pixel
      mov      ax, iES           ; set segment for bit count array
      mov      es, ax
compare_loop:
      mov      al, ds:[si]       ; this pixel
      xor      al, ds:[bx]       ; should be zero for perfect match
      cmp      al, 0             ; is it ?
      je       Label1            ; skip the addition if so
      mov      di, iDI           ; bit count table offset
      add      di, al            ; count offset
      mov      al, es:[di]       ; bit count for these xor'ed pixels
      xor      ah, ah            ; set top byte to zero
      add      dx, ax            ; sum bits set
      cmp      dx, amin          ; test against best
      jae      test_ok           ; jump to next if sum already too big
Label1:
      inc      si                ; move address this line on one
      inc      bx                ; move address other line on one
      cmp      bx, LastP         ; check all pixels compared
      jbe      compare_loop      ; do another if not
      mov      amin, dx          ; this dx is the best
      mov      bbest, cx         ; set bbest to be the best line
      mov      ax, FirstP
      mov      start2, ax        ; save offset for eventual swap
test_ok:
      cmp      cx, 1             ; the first line ?
      ja       not_first
      mov      cx, mino          ; minimum offset allowed
not_first:
      inc      cx                ; next line offset
      cmp      cx, maxo          ; have we done the last line?
      jbe      offset_loop       ; do another if not
done_line:
      cmp      bbest, 1          ; skip if best match is next line
      je       Finished
      mov      ax, amin
      cmp      ax, Minimum       ; test against minimum allowed
      jae      Finished          ; skip if too big

      mov      si, start         ; start address in source line
      add      si, ScanBytes     ; line after's start address
      mov      bx, start2        ; target swap line's start address
      mov      cx, NPix          ; start,end difference
      inc      cx                ; number of pixels to move
move_byte:
      mov      al, ds:[si]       ; get source byte
      xchg     ds:[bx], al       ; move it into target byte
      mov      ds:[si], al       ; put target byte back at source
      inc      si
      inc      bx
      loop     move_byte

Finished:
      mov      bx, start
      mov      ax, line

      pop      di
      pop      es
      pop      si
      pop      ds
      popa
      popf
    }
  cuts[line] = bbest;
  mins[line] = amin;
  line++;
}

CursorPos(25,1);
printf("Line Cut Min");
ic = 2;
line = startline;
for(i=0;i<23;i++) {
   if(line>botline) line = line-botline+topline-1;
   CursorPos(25,ic++);
   printf("%3d %3d %3d",line,cuts[line],mins[line]);
   line++;
}
}


void DoRGBSwap(int Line,int Cut)
{
WORD iDS = FP_SEG(OldBuffer);
WORD iSI = FP_OFF(OldBuffer);
WORD ScanBytes = HBytes;
WORD iST,FirstP,LastP,start,end;
WORD iSI0 = iSI;
int i,ic;
int lb = lborder;
int rb = rborder;
int line = Line;
int bbest = Cut;
unsigned int mins[500],Sum,amin;

if(Cut==0) return;

iSI += (line-1)*HBytes;

_asm {
      pushf
      pusha
      push     ds
      push     si
      push     es
      push     di

      mov      ax, iDS           ; data segment
      mov      ds, ax            ; in DS
      mov      es, ax            ; in ES
      mov      ax, iSI           ; address of scan line
      mov      start, ax
      mov      bx, lb
      add      start, bx         ; first pixel address
      add      ax, ScanBytes     ; iSI + ScanBytes i.e. next line address
      dec      ax                ; last pixel this line
      sub      ax, rb            ; next line minus right hand border
      mov      end, ax           ; last pixel to consider on this line
; now swap the line at the bbest position. Use iSI0 as a line buffer
      mov      ax, iDS
      mov      es, ax
      mov      di, iSI0       ; destination address is offset 0
      mov      si, start      ; source address is first considered pixel
      cld
      mov      cx, bbest      ; need to save the first bbest bytes
rep   movsb                   ; copy bbest bytes from si to di
      mov      di, start      ; prepare destination address
      mov      si, start
      add      si, bbest      ; prepare source address
      mov      cx, end
      sub      cx, si         ; number of bytes to move to 'end'
rep   movsb
      mov      cx, bbest      ; now saved buffer
      mov      si, iSI0
rep   movsb                   ; add the saved bbest bytes at the end

      pop      di
      pop      es
      pop      si
      pop      ds
      popa
      popf
     }
}


void VideoBufferCopy()
{
  if(GREY) {
     ConDitherGrey();
     GetGreySwap();
  } else {
     ConDitherRGB();
     GetRGBSwap();
  }

  BUFseg = FP_SEG(OldBuffer);
  BUFoff = FP_OFF(OldBuffer);
  N2Move = FrameSize;
  VGAoff = 0;
  VGAPut();
}




void CanalBufferCopy()
{
  int i,Nline,line;
  unsigned int ip;

  Nline = botline-topline+1;

  if(GREY) {
     ConDitherGrey();
  } else {
     ConDitherRGB();
  }
  DoSort();

  BUFseg = FP_SEG(OldBuffer);
  ip = FP_OFF(OldBuffer);
  N2Move = botline-startline+1;
  VGAoff = 0;
  BUFoff = (startline-1)*HBytes + ip;
  VGAPut();
  VGAoff = 640*N2Move;
  N2Move = startline-topline;
  BUFoff = (topline-1)*HBytes + ip;
  VGAPut();
}

void BitTable()
{
   int i,j,n;
   printf("Calculating Bit table ...\n");
   for (i=0;i<BSIZE;i++) {
      n = 0;
      for (j=0;j<8;j++) { n += ( 1 & (i>>j) ) ; }
      *(B2C+i) = n;
   }
}
void main()

{
    long          ii;
    int           svga;
    int           i,red,green,blue;
    int           contrast=137,brightness=65,colour=243,hue=188;
    int           mbuffer;
    int           nbuffer;
    int           ibuffer;
    WORD          j,k;
    int           VDeci = 8;
    int           HStart = 6;
    int           VStart = 22;
    int           VSkip = 1;
    DWORD         FrameBytes;
    BOOL          fExit = FALSE;
    DWORD         QueueBuffer[MAXBUFFER];
    char          ch;

    printf("Testing for Pro MovieSpectrum board.\n");

    wPMSPort = 0x250;

    if(!WakeUpPMSpectrum(wPMSPort)) {
      printf("Failed to find board\n");
      exit();
    }
    wPMSAddr = 0xC800;
    wPMSIRQ  = 10;

    gwBoardID = IdentifyPMSBoard();

    SetPMSContrast(0,contrast);
    SetPMSBrightness(0,brightness);
    SetPMSColor(0,colour);
    SetPMSHue(0,hue);
    printf("Hue        is %d\n",PMSReadReg(0x26));
    printf("Colour     is %d\n",PMSReadReg(0x2C));
    printf("Contrast   is %d\n",PMSReadReg(0x2A));
    printf("Brightness is %d\n",PMSReadReg(0x27));
    printf("Brightness is %d\n",PMSReadReg(0x28));
    printf("Brightness is %d\n",PMSReadReg(0x29));

    if(GREY) {
       PMSWrite(0x2E,0x00); /* decoder control */
    } else {
       PMSWrite(0x2E,0x02); /* set colour */
    }

    VSlice = 160;
    HBytes = 200;
    HStart = 48;
    lborder = 2;
    rborder = 6;
    startline = 20;
    endline = 70;
    minoffset = 10;
    maxoffset = 10;
    topline = 4;
    botline = VSlice-4;



    if(CANAL) {
       startline = VSlice*0.9;
       minoffset = 4;
       maxoffset = 16;
    }

       B2C = (LPBYTE) _fmalloc(BSIZE);
       if(B2C == NULL) {
          printf("Can't malloc Bit table ...\n");
          exit();
       } else {
          printf("B2C at %lp\n",B2C);
          seg_B2C = FP_SEG(B2C);
          off_B2C = FP_OFF(B2C);
       }
       BitTable(); /* Set Conversion for bit counts */
       printf("Bit Table is calculated ...\n");

    PMSWrite(0x14,VSkip); /* capture every ? frame */
    PMSWrite(0x15,0x01); /* out of 1                 */

    PMSWrite(0x16,VStart); /* vertical position to start */
    PMSWrite(0x17,(VStart&0x0100)>>8);

    PMSSetHorzDeci(HBytes,768);

    PMSWrite(0x18,VSlice); /* Number of lines in band*/
    PMSWrite(0x19,(VSlice&0x0100)>>8);

    PMSWrite(0x1A,0x01  ); /* interrupt at line      */
    PMSWrite(0x1B,(0x01   & 0x0100)>>8);

    PMSSetVertDeci(VSlice,300);

    PMSWrite(0x20,HStart); /* skip HStart pixels at line start */
    PMSWrite(0x21,(HStart&0x0100)>>8);

    PMSWrite(0x22,(HBytes)); /* capture HBytes pixels          */
    PMSWrite(0x23,(HBytes)>>8);

    PMSWrite(0x01,PMSRead(0x01) & 0xFD); /*field interrupt */

    FrameSize = (WORD) VSlice;

    FrameBytes = (DWORD) HBytes * FrameSize * 2;

    nbuffer = 0;
    for (i=1;i<=MAXBUFFER;i++) {
        Buffer = GetBuffer((DWORD) FrameBytes, 0x00);
        if(Buffer == -1) goto end_buff;
        nbuffer++;
        QueueBuffer[nbuffer] = Buffer;
    }
end_buff:
    printf("\n%d %dk Buffers are allocated for Frames\n",nbuffer,FrameBytes/1000);
    if (nbuffer==0) exit();

    printf("Any key to continue ...");
    getch();

    mode13(); /* sets mode 2e */


/* Set Colour or Grey Table */
    outp(0x3c8,0x00);
    for (i=0;i<256;i++) {
      if(GREY) {
        outp(0x3c9,i&0x3F);
        outp(0x3c9,i&0x3F);
        outp(0x3c9,i&0x3F);
      } else {
        outp(0x3c9,(i&0x60)>>1);  /* red */
        outp(0x3c9,(i&0x1C)<<1);  /* green */
        outp(0x3c9,(i&0x03)<<4);  /* blue */
      }
    }

    countdown = 0;
    FrameCount = 0;
    ibuffer = 1;
    Buffer = QueueBuffer[ibuffer];

    PMSIRQEnable(TRUE);

    do {
      if(kbhit()) {
          ch = getch();
            switch(ch) {
                case 0x1B :
                      fExit = TRUE;
                      break;
                case 0x71 :
                      lborder--;
                      if(lborder<0) lborder=0;
                      break;
                case 0x77 :
                      lborder++;
                      if(lborder>HBytes) lborder=HBytes;
                      break;
                case 0x72 :
                      rborder--;
                      if(rborder<0) rborder=0;
                      break;
                case 0x65 :
                      rborder++;
                      if(rborder>HBytes) rborder=HBytes;
                      break;
                case 'h'  :
                      topline--;
                      if(topline<0) topline = 0;
                      break;
                case 'j'  :
                      topline++;
                      if(topline>VSlice) topline = VSlice;
                      break;
                case 'k'  :
                      botline--;
                      if(botline<topline) botline = topline;
                      if(CLEAN) DoClean(OldBuffer,0x01);
                      break;
                case 'l'  :
                      botline++;
                      if(botline>VSlice) botline = VSlice;
                      break;
                case 0x61 :
                      startline--;
                      if(startline<2) startline=2;
                      break;
                case 0x73 :
                      startline++;
                      if(!CANAL) {
                         if(startline>endline) startline=endline;
                      } else {
                         if(startline>FrameSize) startline=FrameSize;
                      }
                      break;
                case 0x64 :
                      endline--;
                      if(endline<startline) endline=startline;
                      break;
                case 0x66 :
                      endline++;
                      if(endline>FrameSize) endline=FrameSize;
                      break;
                case 'o' :
                      minoffset--;
                      if(minoffset<0) minoffset=0;
                      if(CANAL && minoffset<2) minoffset=2;
                      break;
                case 'p' :
                      minoffset++;
                      if(minoffset>HBytes/2) minoffset=HBytes/2;
                      break;
                case 'O' :
                      maxoffset--;
                      if(maxoffset<minoffset) maxoffset=1;
                      break;
                case 'P' :
                      maxoffset++;
                      if(maxoffset>VSlice) maxoffset=VSlice;
                      break;
                case 'z' :
                      contrast++;
                      SetPMSContrast(0,contrast);
                      break;
                case 'x' :
                      contrast--;
                      SetPMSContrast(0,contrast);
                      break;
                case 'c' :
                      brightness++;
                      SetPMSBrightness(0,brightness);
                      break;
                case 'v' :
                      brightness--;
                      SetPMSBrightness(0,brightness);
                      break;
                case 'm' :
                      XORminsum++;
                      if(XORminsum>1000) XORminsum=1000;
                      break;
                case 'n' :
                      XORminsum--;
                      if(XORminsum<1) XORminsum=1;
                      break;
                default :
                    break;
            }
            CursorPos(1,21);
            printf("min=%1d max=%2d top=%2d bot=%3d",
            minoffset,maxoffset,topline,botline);
            CursorPos(1,22);
            printf("L=%2d R=%2d St=%3d En=%3d",
            lborder,rborder,startline,endline);
            CursorPos(1,23);
            printf("CON=%3d BRI=%3d",contrast,brightness);
            CursorPos(1,24);
            printf("XORminsum=%3d",XORminsum);
      }

      ibuffer++;
      if(ibuffer>nbuffer) ibuffer=1;
      OldBuffer = Buffer;
      Buffer = QueueBuffer[ibuffer];
      if(CANAL) {
         CanalBufferCopy();
      } else {
         VideoBufferCopy();
      }
//    countdown--;
//    if(countdown<0) countdown=10;


    } while(!fExit);



    PMSIRQEnable(FALSE);
    txtmode();
    printf("Capture disabled... ");
    Buffer = GetBuffer((DWORD)FrameBytes, 0x01);
    free(B2C);
    printf("Memory freed\n");
}
