struct VMem
  char  Char;
  byte  Attr;
section
  word  Word;
end

struct PTR8086
  word  Ofs;
  word  Seg;
section
  void @Ptr;
end

define BuffSize 64000

char   Buff [BuffSize];
word   Size;
word   Pos;
word   Ofs;

word   X1,Y1;
word   X2,Y2;

word   Xofs;
word   Yofs;

word   X,Y;

byte   Ins;

VMem  @VMPtr;

void @Ptr(word Seg,Ofs)
  PTR8086 P;
  P.Seg=Seg;
  P.Ofs=Ofs;
  return @P.Ptr;
end

word GetPSP()
  asm mov     AH,62H
  asm int     21H
  asm mov     AX,BX
end

byte FCreate(word @F; char @Name)
  asm push    DS
  asm mov     AH,3CH
  asm mov     CX,00H
  asm mov     DX,SS:[BP+6]
  asm mov     DS,DX
  asm mov     DX,SS:[BP+4]
  asm int     21H
  asm les     DI,SS:[BP+8]
  asm mov     ES:[DI],AX
  asm lahf
  asm mov     AL,AH
  asm and     AL,1
  asm pop     DS
end

byte FOpen  (word @F; char @Name)
  asm push    DS
  asm mov     AH,3DH
  asm mov     AL,00H
  asm mov     DX,SS:[BP+6]
  asm mov     DS,DX
  asm mov     DX,SS:[BP+4]
  asm int     21H
  asm les     DI,SS:[BP+8]
  asm mov     ES:[DI],AX
  asm lahf
  asm mov     AL,AH
  asm and     AL,1
  asm pop     DS
end

byte FClose (word F)
  asm mov     AH,3EH
  asm mov     BX,SS:[BP+4]
  asm int     21H
  asm lahf
  asm mov     AL,AH
  asm and     AL,1
end

byte FRead  (word F; word @N; char @Buff)
  asm push    DS
  asm mov     AH,3FH
  asm mov     BX,SS:[BP+12]
  asm les     DI,SS:[BP+8]
  asm mov     CX,ES:[DI]
  asm mov     DX,SS:[BP+6]
  asm mov     DS,DX
  asm mov     DX,SS:[BP+4]
  asm int     21H
  asm les     DI,SS:[BP+8]
  asm mov     ES:[DI],AX
  asm lahf
  asm mov     AL,AH
  asm and     AL,1
  asm pop     DS
end

byte FWrite (word F; word  N; char @Buff)
  asm push    DS
  asm mov     AH,40H
  asm mov     BX,SS:[BP+10]
  asm mov     CX,SS:[BP+8]
  asm mov     DX,SS:[BP+6]
  asm mov     DS,DX
  asm mov     DX,SS:[BP+4]
  asm int     21H
  asm lahf
  asm mov     AL,AH
  asm and     AL,1
  asm pop     DS
end

void GetCursor(byte @X,@Y)
  asm mov     AH,03H
  asm mov     BH,00H
  asm int     10H
  asm les     DI,SS:[BP+8]
  asm mov     ES:[DI],DL
  asm les     DI,SS:[BP+4]
  asm mov     ES:[DI],DH
end

void SetCursor(byte X,Y)
  asm mov     AH,02H
  asm mov     BH,00H
  asm mov     DL,SS:[BP+6]
  asm mov     DH,SS:[BP+4]
  asm int     10H
end

char GetChar()
  asm mov     AH,07H
  asm int     21H
end

word Load(char @Name)
  word N=BuffSize;
  word F;

  if FOpen(@F,@Name)!=0 then
    return 0;
  end

  FRead (F,@N,@Buff);
  FClose(F);

  return N;
end

void Save(char @Name)
  word F;
  if FCreate(@F,@Name)=0 then
    FWrite(F,Size,@Buff);
    FClose(F);
  end
end

void Show()
  word X,Y;
  word K,P;

  P=Pos;
  Y=Y1;
  while Y<=Y2 do
    X=X1;
    K=0;
    while K<Xofs & P<Size & Buff[P]!=#10 do
      inc K;
      inc P;
    end

    while (X<=X2)  & (P<Size) & (Buff[P]!=#10) do
      char Ch;
      Ch=Buff[P];
      if Ch=#13 then
        Ch=#32;
      end
      VMPtr[80*Y+X].Char=Ch;
      VMPtr[80*Y+X].Attr=31;
      inc P;
      inc X;
    end

    while X<=X2 do
      VMPtr[80*Y+X].Char=' ';
      VMPtr[80*Y+X].Attr= 31;
      inc X;
    end

    while P<Size & Buff[P]!=#10 do
      inc P;
    end

    inc P;
    inc Y;
  end
end

void Info()
  SetCursor(X1+X,Y1+Y);
end

word _PTR()
  word P=Pos;
  word N=0;
  while N<Y do
    while Buff[P]!=#10 do
      inc P;
    end
    inc P;
    inc N;
  end

  return P;
end

word _EOL(word P)
  while P<Size & Buff[P]!=#13 & Buff[P]!=#10 do
    inc P;
  end

  return P;
end

void _SHL(word P1; word N)
  while P1+N<Size do
    Buff[P1]=Buff[P1+N];
    inc  P1;
  end
  Size=Size-N;
end

void _SHR(word P1; word N)
  word P2=Size;
  while P2>P1 do
    dec  P2;
    Buff[P2+N]=Buff[P2];
  end
  Size=Size+N;
end

void _Up()
  if Yofs+Y>0 then
    if Y>0 then
      dec Y;
    else
      if Pos>0 then
        dec Pos;
        while Pos>0 & Buff[Pos-1]!=#10 do
          dec Pos;
        end
      end

      dec Yofs;

      Show();
    end

    Info();
  end
end

void _Dn()
  word P1=_PTR();
  while P1<Size & Buff[P1]!=#10 do
    inc P1;
  end

  if P1+1<Size then
    if Y1+Y<Y2 then
      inc Y;
    else
      while Buff[Pos]!=#10 do
        inc Pos;
      end
      inc Pos;
      inc Yofs;
      Show();
    end

    Info();
  end
end

void _PgUp()
  word  S=Yofs;
  word  K=Y1;
  while K<Y2 do
    if Yofs>0 then
      if Pos>0 then
        dec Pos;
        while Pos>0 & Buff[Pos-1]!=#10 do
          dec Pos;
        end
      end
      dec Yofs;
    else
      if Y>0 then
        dec Y;
      end
    end
    inc K;
  end
  if Yofs!=S then
    Show();
  end
  Info();
end

void _PgDn()
  word P=_PTR();
  word S= Yofs;
  word K= Y1;
  while K<Y2 do
    while P<Size & Buff[P]!=#10 do
      inc P;
    end
    inc P;
    if P<Size then
      while Buff[Pos]!=#10 do
        inc Pos;
      end
      inc Pos;
      inc Yofs;
    end
    inc K;
  end
  if Yofs!=S then
    Show();
    Info();
  end
end

void _Left()
  if Xofs+X>0 then
    if X>0 then
      dec X;
    else
      dec Xofs;
      Show();
    end
    Info();
  end
end

void _Right()
  if Xofs+X+1<BuffSize then
    if X1+X+1<=X2 then
      inc X;
    else
      inc Xofs;
      Show();
    end
    Info();
  end
end

void _Tab()
  word S=Xofs;
  word K=1;
  while K<8 do
    if Xofs+X+1<BuffSize then
      if X1+X+1<=X2 then
        inc X;
      else
        inc Xofs;
      end
    end

    inc K;
  end

  if Xofs!=S then
    Show();
  end

  Info();
end

void _Home()
  X=0;
  if Xofs>0 then
    Xofs=0;
    Show();
  end
  Info();
end

void _End()
  word S = Xofs;
  word P1=_PTR();
  word P2=_EOL(P1);

  Xofs=0;
  X   =0;
  while P1<P2 do
    if X1+X+1<=X2 then
      inc X;
    else
      inc Xofs;
    end
    inc P1;
  end

  if Xofs!=S then
    Show();
  end

  Info();
end

void _BkSp()
  word P1=_PTR();
  if Xofs+X>0 then
    word P2=_EOL(P1);
    word P3=(Xofs+X)-1;
    if P3>=P2-P1 then
      _Left();
      return
    end

    P3=P1+P3;
    while P3+1<P2 do
      Buff[P3]=Buff[P3+1];
      inc  P3;
    end

    word N=1;
    while P3>P1 & Buff[P3-1]=#32 do
      inc N;
      dec P3;
    end
    _SHL(P3,N);
    if X>0 then
      dec X;
    else
      dec Xofs;
    end
  else
    word N=0;
    word P2;
    if P1>0 then
      if Buff[P1-1]=#10 then
        inc N;
        dec P1;
      end
    end
    if P1>0 then
      if Buff[P1-1]=#13 then
        inc N;
        dec P1;
      end
    end
    if N=0 then
      return
    end
    _SHL(P1,N);
    P2  =P1;
    Xofs=0;
    X   =0;
    while P2>0 & Buff[P2-1]!=#10 do
      if X1+X+1<=X2 then
        inc X;
      else
        inc Xofs;
      end
      dec P2;
    end
    if Y>0 then
      dec Y;
    else
      if Pos>0 then
        dec Pos;
        while Pos>0 & Buff[Pos-1]!=#10 do
          dec Pos;
        end
      end
      if Yofs>0 then
        dec Yofs;
      end
    end
  end
  Show();
  Info();
end

void _Del()
  inc X;
  _BkSp();
end

void _Enter()
  word P1=_PTR();
  word P2=_EOL(P1);
  word P3=Xofs+X;
  if Size+2>BuffSize then
    return
  end
  if P3<P2-P1 then
    P3=P1+P3;
    _SHR(P3,2);
    Buff[P3]  =#13;
    Buff[P3+1]=#10;

    word N=0;
    while P3>P1 & Buff[P3-1]=#32 do
      inc N;
      dec P3;
    end
    if N>0 then
      _SHL(P3,N);
    end
  else
    _SHR(P2,2);
    Buff[P2]  =#13;
    Buff[P2+1]=#10;
  end
  Xofs=0;
  X   =0;
  if Y1+Y<Y2 then
    inc Y;
    Show();
    Info();
  else
    inc Yofs;
    _Dn();
  end
end

void _Type(char Ch)
  word P1=_PTR();
  word P2=_EOL(P1);
  word P3= Xofs+X;
  word N = 0;
  if P3<P2-P1 then
    P3=P1+P3;
    if Ins!=0 then
      if Size+1>BuffSize then
        return
      end
      _SHR(P3,1);
      Buff[P3]=Ch;
    else
      Buff[P3]=Ch;
      while P2>P1 & Buff[P2-1]=#32 do
        inc N;
        dec P2;
      end
      if N>0 then
        _SHL(P2,N);
      end
    end
  else
    if Ch=#32 then
      _Right();
      return
    end
    N=(P3-(P2-P1))+1;

   if N>BuffSize-Size then
      return
    end

    P3=P1+P3;
    _SHR(P2,N);
    while P2<P3 do
      Buff[P2]=#32;
      inc  P2;
    end
    Buff[P3]=Ch;
  end

  if X1+X+1<=X2 then
    inc X;
  else
    inc Xofs;
  end

  Show();
  Info();
end

begin
  byte @N   =@Ptr(GetPSP(),128);
  char @Line=@Ptr(GetPSP(),129);

  char  Name [256];

  word  I=0;
  word  K=0;
  while I<N & Line[I]=' ' do
    inc I;
  end

  while I<N & Line[I]!=' ' do
    Name[K]=Line[I];
    inc I;
    inc K;
  end
  Name[K]=#00;

  if K=0 then
    return
  end

  Size  = Load(@Name);
  if !(Size<BuffSize) then
    return
  end

  Ins   = 0;

  Xofs  = 0;
  Yofs  = 0;

  X     = 0;
  Y     = 0;

  Pos   = 0;
  Ofs   = 0;

  X1    = 0;
  Y1    = 0;
  X2    =79;
  Y2    =23;

  @VMPtr=@Ptr(47104,0);

  VMem Buff[2000];
  byte X,Y;

  K=0;
  while K<2000 do
    Buff [K]=VMPtr[K];
    VMPtr[K].Word=0;
    inc   K;
  end

  GetCursor(@X,@Y);

  K=0;
  while K<80 do
    VMPtr[1920+K].Char=' ';
    VMPtr[1920+K].Attr=112;
    inc K;
  end

  Show();
  Info();
  while TRUE do
    char Ch;
    Ch=GetChar();
    select
      case Ch=#00:
        Ch=GetChar();
        select
          case Ch=#72:
            _Up();
          case Ch=#80:
            _Dn();
          case Ch=#73:
            _PgUp();
          case Ch=#81:
            _PgDn();
          case Ch=#75:
            _Left();
          case Ch=#77:
            _Right();
          case Ch=#71:
            _Home();
          case Ch=#79:
            _End();
          case Ch=#82:
            Ins=!Ins;
            Info();
          case Ch=#83:
            _Del();
          case Ch=#60:
            Save(@Name);
        end
      case Ch=#08:
        _BkSp();
      case Ch=#09:
        _Tab();
      case Ch=#13:
        _Enter();
      case Ch=#27:
        exit
      default:
        _Type(Ch);
    end
  end

  K=0;
  while K<2000 do
    VMPtr[K]=Buff[K];
    inc   K;
  end

  SetCursor(X,Y);
end
