unit HelpScrn;

{$O+,F+,S-,X+}

interface

uses Objects, HelpUtil;

type

  PHelpScreen = ^THelpScreen;
  THelpScreen = object(TObject)
    MaxIndex: Word;
    MaxRow: Word;
    HasExample: Boolean;
    Buffer: PChar;
    BufSize: Word;
    constructor Init(ABuffer: PChar; ABufSize: Word);
    function  Read(Index: Word): Boolean; virtual;
    procedure GetRow(Row: Word; var Text: PChar; var Index: Word;
      var Example: Boolean); virtual;
    function  Format(Width: Word; var P; N: Word): Boolean; virtual;
    procedure GetPos(Index: Word; var Row, Col: Integer; var Len: Word); virtual;
    function  GetIndex(var S: string; var Len: Word): Word; virtual;
    function  GetContext(Index: Word): Integer; virtual;
    procedure GetExample(var StartPos, EndPos: TPos); virtual;
    function  SearchRow(Row: Word): Integer;
    function  SearchIndex(Index: Word): Integer;
  end;

  PHelpIndex = ^THelpIndex;
  THelpIndex = object(THelpScreen)
    BufPtr: PChar;
    CurIndex: Word;
    ContextNumber: Word;
    IndexToken: string[38];
    CurLetter: Word;
    function  Read(Index: Word): Boolean; virtual;
    procedure GetRow(Row: Word; var Text: PChar; var Index: Word;
      var Example: Boolean); virtual;
    function  Format(Width: Word; var P; N: Word): Boolean; virtual;
    procedure GetPos(Index: Word; var Row, Col: Integer; var Len: Word); virtual;
    function  GetIndex(var S: string; var Len: Word): Word; virtual;
    function  GetContext(Index: Word): Integer; virtual;
    procedure GetExample(var StartPos, EndPos: TPos); virtual;
    function  GetToken: Word;
    function  TokenLength: Word;
    procedure ReadLetter(Letter: Word);
    procedure Track(Start, Target: Word);
    procedure PositionTo(Index: Word);
    procedure Parse;
  end;

  PHelpTopic = ^THelpTopic;
  THelpTopic = object(THelpScreen)
    TopicSize: Word;
    BufPtr: PChar;
    CurRow: Word;
    CurIndex: Word;
    InExample: Boolean;
    constructor Init(ABuffer: PChar; ABufSize: Word);
    function  Read(Index: Word): Boolean; virtual;
    procedure GetRow(Row: Word; var Text: PChar; var Index: Word;
      var Example: Boolean); virtual;
    function  Format(Width: Word; var P; N: Word): Boolean; virtual;
    procedure GetPos(Index: Word; var Row, Col: Integer; var Len: Word); virtual;
    function  GetIndex(var S: string; var Len: Word): Word; virtual;
    function  GetContext(Index: Word): Integer; virtual;
    procedure GetExample(var StartPos, EndPos: TPos); virtual;
    procedure CountRows;
    procedure FindExample;
    procedure Reset;
    procedure GoBack(Amount: Word);
    procedure GoForward(Amount: Word);
    procedure GoRow(Row: Word);
    procedure FindPos(Ofs: Word; var P: TPos);
    function  FindChar(Num: Integer; C: Char): Word;
  end;

implementation

uses HelpFile;

type
  PPosArray = ^TPosArray;
  TPosArray = array[1..MaxCollectionSize] of ^TPos;

const
  ColumnCount: Word = 0;
  ColumnWidth: Word = 0;
  Master: array[0..26] of record
    Offset, Size, StartIndex, StartRow: Word
  end = ((), (), (), (), (), (), (), (),
         (), (), (), (), (), (), (), (),
         (), (), (), (), (), (), (), (),
         (),(),());
  SaveMaxRow: Word = 0;

procedure SortPos(A: PPosArray; N: Word);
var
  I, J: Word;
  P: ^TPos;
begin
  if N > 1 then
    for I := 1 to N - 1 do
      for J := I + 1 to N do
        if (A^[I]^.Row > A^[J]^.Row) or
           (A^[I]^.Row = A^[J]^.Row) and (A^[I]^.Col > A^[J]^.Col) then
        begin
          P := A^[I];
          A^[I] := A^[J];
          A^[J] := P
        end;
end;

constructor THelpScreen.Init(ABuffer: PChar; ABufSize: Word);
begin
  TObject.Init;
  Buffer := ABuffer;
  BufSize := ABufSize;
  MaxIndex := 0;
end;

function THelpScreen.Read(Index: Word): Boolean;
begin
end;

procedure THelpScreen.GetRow(Row: Word; var Text: PChar; var Index: Word;
  var Example: Boolean);
const
  Empty: Char = #0;
begin
  if Row >= MaxRow then
  begin
    Text := @Empty;
    Index := MaxIndex;
    Example := False;
  end;
end;

function THelpScreen.Format(Width: Word; var P; N: Word): Boolean;
begin
  Abstract;
end;

procedure THelpScreen.GetPos(Index: Word; var Row, Col: Integer; var Len: Word);
begin
  Abstract;
end;

function THelpScreen.GetIndex(var S: string; var Len: Word): Word;
begin
  Abstract;
end;

function THelpScreen.GetContext(Index: Word): Integer;
begin
  Abstract;
end;

procedure THelpScreen.GetExample(var StartPos, EndPos: TPos);
begin
  StartPos.Clear;
  EndPos.Clear;
end;

function LetterNum(C: Char): Word;
var
  I: Integer;
begin
  I := Ord(UpCase(C)) - Ord('A') + 1;
  if (I < 1) or (I > 26) then
    I := 0;
  LetterNum := I;
end;

function THelpScreen.SearchRow(Row: Word): Integer;
var
  I, J, K: Integer;
  Found: Boolean;
begin
  if Row = 0 then
    SearchRow := -1
  else
  begin
    I := 0;
    J := 26;
    Found := False;
    repeat
      K := (J + I) shr 1;
      if Master[K].StartRow > Row then
        J := K - 1
      else if (K < 26) and (Master[K+1].StartRow <= Row) then
        I := K + 1
      else
        Found := True;
    until Found;
    SearchRow := K;
  end;
end;

function THelpScreen.SearchIndex(Index: Word): Integer;
var
  I, J, K: Integer;
  Found: Boolean;
begin
  I := 0;
  J := 26;
  Found := False;
  repeat
    K := (J + I) shr 1;
    if Master[K].StartIndex > Index then
      J := K - 1
    else if (K < 26) and (Master[K+1].StartIndex <= Index) then
      I := K + 1
    else
      Found := True;
  until Found;
  SearchIndex := K;
end;

function THelpIndex.GetToken: Word; assembler;
asm
        PUSH    DS
        CLD
        LDS     SI,Self
        MOV     AX,[SI].CurIndex
        CMP     AX,[SI].MaxIndex
        JB      @@1
        SUB     AX,AX
        JMP     @@2
@@1:    INC     [SI].CurIndex
        MOV     AX,DS
        MOV     ES,AX
        LEA     DI,[SI].IndexToken+1
        MOV     DX,DI
        LDS     SI,[SI].BufPtr
        SUB     AH,AH
        LODSB
        MOV     CX,AX
        AND     CL,1FH
        SHR     AL,1
        SHR     AL,1
        SHR     AL,1
        SHR     AL,1
        SHR     AL,1
        ADD     DI,AX
        REP     MOVSB
        LODSW
        LDS     BX,Self
        MOV     [BX].ContextNumber,AX
        MOV     [BX].BufPtr.Word[0],SI
        XCHG    AX,DI
        SUB     AX,DX
        MOV     [BX].IndexToken.Byte,AL
@@2:    POP     DS
end;

function THelpIndex.TokenLength: Word; assembler;
asm
        PUSH    DS
        CLD
        LDS     SI,Self
        LDS     SI,[SI].BufPtr
        SUB     AH,AH
        LODSB
        MOV     BX,AX
        AND     BL,1FH
        MOV     CL,5
        SHR     AL,CL
        ADD     AX,BX
        POP     DS
end;

procedure THelpIndex.ReadLetter(Letter: Word);
begin
  if Letter <> CurLetter then
    with Master[Letter] do
    begin
      ReadIndex(Buffer, Offset, Size);
      CurIndex := StartIndex;
      BufPtr := Buffer;
      CurLetter := Letter;
    end;
end;

procedure THelpIndex.Track(Start, Target: Word);
begin
  if (CurIndex > Target) or (CurIndex < Start) then
  begin
    BufPtr := Buffer;
    CurIndex := Start
  end;
  while CurIndex < Target do
    GetToken;
end;

procedure THelpIndex.PositionTo(Index: Word);
var
  Letter: Integer;
begin
  Letter := SearchIndex(Index);
  ReadLetter(Letter);
  with Master[Letter] do
    Track(StartIndex, Index);
end;

procedure THelpIndex.Parse;
var
  MaxLength, CurOffset, BufOfs: Word;
  TokenLen, Letter, CurRow, TokenCount, Rest, OldOfs, NewOfs: Word;
begin
  TokenCount := 0;
  CurOffset := 2;
  MaxLength := 0;
  CurIndex := 1;
  ReadIndex(Buffer, CurOffset, 4096);
  NewOfs := CurOffset;
  BufPtr := Buffer;
  BufOfs := PtrRec(BufPtr).Ofs;
  for Letter := 0 to 26 do
    with Master[Letter] do
    begin
      Offset := CurOffset;
      StartIndex := TokenCount;
      OldOfs := BufOfs;
      Dec(CurIndex);
      PtrRec(BufPtr).Ofs := BufOfs;
      Rest := 0;
      TokenLen := GetToken;
      while (TokenLen > 0) and (LetterNum(IndexToken[1]) = Letter) do
      begin
        if MaxLength < TokenLen then
          MaxLength := TokenLen;
        Inc(TokenCount);
        BufOfs := PtrRec(BufPtr).Ofs;
        if BufOfs - PtrRec(Buffer).Ofs > 4016 then
        begin
          Rest := BufOfs - PtrRec(Buffer).Ofs;
          NewOfs := NewOfs + Rest;
          ReadIndex(Buffer, NewOfs, 4096);
          BufPtr := Buffer;
          BufOfs := PtrRec(Buffer).Ofs;
        end;
        TokenLen := GetToken;
      end;
      Size := BufOfs + Rest - OldOfs;
      CurOffset := Offset + Size;
    end;
  if MaxLength = 0 then
    MaxLength := 38;
  ColumnCount := 76 div MaxLength;
  if ColumnCount = 0 then
    ColumnCount := 1;
  if ColumnCount > 4 then
    ColumnCount := 4;
  ColumnWidth := 76 div ColumnCount;
  CurRow := 1;
  for Letter := 0 to 25 do
    with Master[Letter] do
    begin
      StartRow := CurRow;
      CurRow := CurRow + 3 + (Master[Letter+1].StartIndex - StartIndex +
        ColumnCount - 1) div ColumnCount;
    end;
  Master[26].StartRow := CurRow;
  MaxRow := CurRow + 3 + (MaxIndex - Master[26].StartIndex +
    ColumnCount - 1) div ColumnCount;
  SaveMaxRow := MaxRow;
end;

function THelpIndex.Read(Index: Word): Boolean;
begin
  Read := False;
  if ReadIndex(Buffer, 0, 2) <> 0 then
  begin
    Read := True;
    MaxIndex := PWordArray(Buffer)^[0];
    HasExample := False;
    if Master[0].Offset = 0 then
      Parse
    else
      MaxRow := SaveMaxRow;
    CurLetter := 255;
  end;
end;

procedure THelpIndex.GetRow(Row: Word; var Text: PChar; var Index: Word;
  var Example: Boolean);

procedure MakeBold(Dest, Src: string; Width: Word); assembler;
asm
        PUSH    DS
        CLD
        LES     DI,Dest
        LDS     SI,Src
        MOV     AL,ES:[DI]
        SUB     AH,AH
        ADD     DI,AX
        LODSB
        XCHG    AX,CX
        MOV     AX,Width
        SUB     AX,CX
        XCHG    AX,DX
        MOV     AL,2
        STOSB
        REP     MOVSB
        STOSB
        MOV     CX,DX
        MOV     AL,' '
        REP     STOSB
        SUB     AL,AL
        STOSB
        MOV     AX,Dest.Word[0]
        XCHG    AX,DI
        SUB     AX,DI
        DEC     AX
        MOV     ES:[DI],AL
        POP     DS
end;

const
  S: string[87] = '';
var
  Letter, R: Integer;
  I: Word;
begin
  THelpScreen.GetRow(Row, Text, Index, Example);
  if Row < MaxRow then
  begin
    Index := 0;
    Example := False;
    S := #0;
    Text := @S[1];
    Letter := SearchRow(Row);
    if Letter < 0 then
      S := 'Turbo Help Index'#0
    else
      with Master[Letter] do
      begin
        ReadLetter(Letter);
        R := Row - StartRow - 3;
        if R < 0 then
        begin
          Index := StartIndex;
          if (R = -2) and (Letter > 0) then
            S := Chr(Letter + Ord('A') - 1) + #0;
        end else
        begin
          I := StartIndex + R * ColumnCount;
          Index := I;
          Track(StartIndex, I);
          if Letter = 26 then
            I := MaxIndex - I
          else
            I := Master[Letter+1].StartIndex - I;
          if I > ColumnCount then
            I := ColumnCount;
          while I > 0 do
          begin
            GetToken;
            MakeBold(S, IndexToken, ColumnWidth);
            Dec(I);
          end;
        end;
      end;
  end;
end;

function THelpIndex.Format(Width: Word; var P; N: Word): Boolean;
begin
  Format := True;
end;

procedure THelpIndex.GetPos(Index: Word; var Row, Col: Integer; var Len: Word);
var
  Letter: Integer;
begin
  Letter := SearchIndex(Index);
  with Master[Letter] do
  begin
    Row := StartRow + 3 + (Index - StartIndex) div ColumnCount;
    Col := (Index - StartIndex) mod ColumnCount * ColumnWidth + 1;
    PositionTo(Index);
    Len := TokenLength;
  end;
end;

function THelpIndex.GetIndex(var S: string; var Len: Word): Word;

function StrCmp(S1, S2: string): Boolean; assembler;
asm
        PUSH    DS
        CLD
        LDS     SI,S2
        LES     DI,S1
        MOV     CL,ES:[DI]
        CMPSB
        JB      @@3
        SUB     CH,CH
@@1:    LODSB
        CMP     AL,'a'
        JB      @@2
        CMP     AL,'z'
        JA      @@2
        AND     AL,0DFH
@@2:    SCASB
        LOOPZ   @@1
        JNZ     @@3
        MOV     AX,1
        JMP     @@4
@@3:    SUB     AX,AX
@@4:    POP     DS
end;

var
  Letter: Integer;
  Max, Index: Word;
  Found: Boolean;
begin
  GetIndex := $FFFE;
  Len := 0;
  if Length(S) > 0 then
  begin
    Letter := LetterNum(S[1]);
    with Master[Letter] do
    begin
      ReadLetter(Letter);
      Track(StartIndex, StartIndex);
      if Letter = 26 then
        Max := MaxIndex
      else
        Max := Master[Letter+1].StartIndex;
      Index := StartIndex;
      Found := False;
      while (Index < Max) and not Found do
      begin
        GetToken;
        if StrCmp(S, IndexToken) then
          Found := True
        else
          Inc(Index);
      end;
      if Found then
      begin
        Len := Length(S);
        GetIndex := Index
      end;
    end;
  end else
    GetIndex := 0;
end;

function THelpIndex.GetContext(Index: Word): Integer;
begin
  PositionTo(Index);
  GetToken;
  GetContext := ContextNumber;
end;

procedure THelpIndex.GetExample(var StartPos, EndPos: TPos);
begin
  THelpScreen.GetExample(StartPos, EndPos);
end;

constructor THelpTopic.Init(ABuffer: PChar; ABufSize: Word);
begin
  THelpScreen.Init(ABuffer, ABufSize);
  TopicSize := 0;
end;

function THelpTopic.Read(Index: Word): Boolean;
begin
  Read := ReadTopic(Index, Buffer, BufSize, TopicSize, MaxIndex);
  Reset;
  CountRows;
  FindExample;
end;

procedure THelpTopic.CountRows; assembler;
asm
        PUSH    DS
        LDS     SI,Self
        MOV     CX,[SI].TopicSize
        LES     DI,[SI].Buffer
        SUB     AX,AX
        SUB     BX,BX
        CLD
@@1:    JCXZ    @@2
        REPNZ   SCASB
        INC     BX
        JMP     @@1
@@2:    MOV     [SI].MaxRow,BX
        POP     DS
end;

procedure THelpTopic.FindExample; assembler;
asm
        PUSH    DS
        LDS     SI,Self
        MOV     CX,[SI].TopicSize
        LES     DI,[SI].Buffer
        MOV     AL,5
        SUB     BX,BX
        CLD
        JCXZ    @@1
        REPNE   SCASB
        JNE     @@1
        INC     BX
@@1:    MOV     [SI].HasExample,bl
        POP     DS
end;

procedure THelpTopic.Reset;
begin
  BufPtr := Buffer;
  CurRow := 0;
  CurIndex := 0;
  InExample := False;
end;

procedure THelpTopic.GoBack(Amount: Word);
begin
  if CurRow <= Amount then
    Reset
  else
    asm
        PUSH    DS
        LDS     SI,Self
        LES     DI,[SI].BufPtr
        SUB     AH,AH
        SUB     DX,DX
        MOV     BX,Amount
        STD
        INC     BX
        DEC     DI
        DEC     DI
        JMP     @@7
@@1:    MOV     CX,0FFFFH
        SUB     AL,AL
        REPNZ   SCASB
        NOT     CX
        PUSH    DI
        ADD     DI,CX
        DEC     CX
        JCXZ    @@6
        MOV     AL,2
        PUSH    CX
        PUSH    DI
        JMP     @@3
@@2:    INC     DX
@@3:    REPNE   SCASB
        JE      @@2
        MOV     AL,5
        POP     DI
        POP     CX
        JMP     @@5
@@4:    INC     AH
@@5:    REPNE   SCASB
        JE      @@4
@@6:    POP     DI
@@7:    DEC     BX
        JNZ     @@1
        AND     AH,1
        XOR     [SI].InExample,AH
        SHR     DX,1
        SUB     [SI].CurIndex,DX
        INC     DI
        INC     DI
        MOV     [SI].BufPtr.Word[0],DI
        MOV     AX,Amount
        SUB     [SI].CurRow,AX
        POP     DS
        CLD
    end;
end;

procedure THelpTopic.GoForward(Amount: Word);
begin
  if CurRow + Amount >= MaxRow then
    Amount := MaxRow - CurRow - 1;
  asm
        PUSH    DS
        LDS     SI,Self
        LES     DI,[SI].BufPtr
        SUB     AH,AH
        SUB     DX,DX
        MOV     BX,Amount
        CLD
        INC     BX
        JMP     @@7
@@1:    MOV     CX,0FFFFH
        SUB     AL,AL
        REPNZ   SCASB
        NOT     CX
        PUSH    DI
        SUB     DI,CX
        DEC     CX
        JCXZ    @@6
        MOV     AL,2
        PUSH    CX
        PUSH    DI
        JMP     @@3
@@2:    INC     DX
@@3:    REPNE   SCASB
        JE      @@2
        MOV     AL,5
        POP     DI
        POP     CX
        JMP     @@5
@@4:    INC     AH
@@5:    REPNE   SCASB
        JE      @@4
@@6:    POP     DI
@@7:    DEC     BX
        JNZ     @@1
        AND     AH,1
        XOR     [SI].InExample,AH
        SHR     DX,1
        ADD     [SI].CurIndex,DX
        MOV     [SI].BufPtr.Word[0],DI
        MOV     AX,Amount
        ADD     [SI].CurRow,AX
        POP     DS
  end;
end;

procedure THelpTopic.GoRow(Row: Word);
begin
  if Row < CurRow then
    if Row < CurRow shr 1 then
    begin
      Reset;
      GoForward(Row)
    end else
      GoBack(CurRow - Row)
  else if Row > CurRow then
    GoForward(Row - CurRow);
end;

procedure THelpTopic.GetRow(Row: Word; var Text: PChar;var Index: Word;
  var Example: Boolean);
begin
  THelpScreen.GetRow(Row, Text, Index, Example);
  if Row < MaxRow then
  begin
    GoRow(Row);
    Text := BufPtr;
    Index := CurIndex;
    Example := InExample;
  end;
end;

function THelpTopic.Format(Width: Word; var P; N: Word): Boolean; assembler;
var
  SaveSP, IndexStart, Row: Word;
  RowLen: byte;
  NewMaxRow: Word;
  HasControlChars: Boolean;
  WordBuf: string[79];
  WordLen: byte;
  RowEnd: Word;
  CurPosPtr: PPosArray;
  TargetRow: Word;
  TargetCol: Byte;
  InKeyword: byte;
asm
        PUSH    DS
        MOV     SaveSP,SP
        PUSH    P.Word[2]
        PUSH    P.Word[0]
        PUSH    N
        CALL    SortPos
        SUB     AX,AX
        MOV     Row,AX
        MOV     NewMaxRow,AX
        MOV     HasControlChars,AL
        SUB     Width,1
        LES     AX,P
        MOV     CurPosPtr.Word[2],ES
        SUB     AX,4
        MOV     CurPosPtr.Word[0],AX
        LDS     SI,Self
        LES     DI,[SI].Buffer
        MOV     AX,DI
        MOV     CX,[SI].TopicSize
        ADD     AX,CX
        DEC     AX
        ADD     DI,[SI].BufSize
        SUB     DI,[SI].MaxIndex
        SUB     DI,[SI].MaxIndex
        MOV     IndexStart,DI
        DEC     DI
        XCHG    AX,SI
        PUSH    ES
        POP     DS
        STD
        REP     MOVSB
        CLD
        XCHG    SI,DI
        INC     SI
        INC     DI
        CALL    @@19
@@1:    CMP     SI,IndexStart
        JAE     @@30
@@2:    MOV     AL,[SI]
        AND     AL,AL
        JZ      @@3
        CMP     AL,' '
        JNE     @@6
@@3:    CMP     HasControlChars,0
        JE      @@4
        CALL    @@18
@@4:    MOV     AX,Row
        CMP     AX,TargetRow
        JNE     @@5
        PUSH    DS
        PUSH    SI
        LDS     SI,CurPosPtr
        LDS     SI,[SI]
        MOV     AX,NewMaxRow
        MOV     [SI].TPos.Row,AX
        POP     SI
        POP     DS
        CALL    @@19
        JMP     @@4
@@5:    XCHG    SI,DI
        SUB     AX,AX
        MOV     CX,0FFFFh
        REPNZ   SCASB
        NOT     CX
        SUB     DI,CX
        XCHG    SI,DI
        REP     MOVSB
        INC     NewMaxRow
        JMP     @@16
@@6:    MOV     RowLen,1
@@7:    MOV     AX,Row
        CMP     AX,TargetRow
        JNE     @@13
@@8:    CMP     BYTE PTR [SI],0
        JE      @@11
        MOV     AL,RowLen
        CMP     AL,1
        JE      @@9
        CMP     AL,TargetCol
        JA      @@11
@@9:    CALL    @@21
        MOV     AL,HasControlChars
        AND     AL,AL
        JZ      @@10
        ADD     AL,WordLen
        CBW
        CMP     AX,Width
        JBE     @@10
        CALL    @@18
@@10:   CALL    @@28
        JMP     @@8
@@11:   PUSH    DS
        PUSH    SI
        LDS     SI,CurPosPtr
        LDS     SI,[SI]
        MOV     AX,NewMaxRow
        MOV     [SI].TPos.Row,AX
        MOV     AL,TargetCol
        CMP     AL,RowLen
        JBE     @@12
        MOV     RowLen,AL
@@12:   MOV     AL,HasControlChars
        ADD     AL,1
        SUB     AL,RowLen
        ADD     AL,TargetCol
        MOV     [SI].TPos.Col,AL
        POP     SI
        POP     DS
        CALL    @@19
        JMP     @@7
@@13:   CMP     BYTE PTR [SI],0
        JE      @@15
        CALL    @@21
        MOV     AL,HasControlChars
        AND     AL,AL
        JZ      @@14
        ADD     AL,WordLen
        CBW
        CMP     AX,Width
        JBE     @@14
        CALL    @@18
@@14:   CALL    @@28
        JMP     @@13
@@15:   INC     SI
@@16:   INC     Row
        JMP     @@1
@@17:   MOV     SP,SaveSP
        SUB     AX,AX
        JMP     @@32
@@18:   SUB     AX,AX
        STOSB
        CMP     DI,SI
        JAE     @@17
        MOV     HasControlChars,AL
        INC     NewMaxRow
        RETN
@@19:   MOV     AX,N
        AND     AX,AX
        MOV     AX,-1
        JZ      @@20
        DEC     N
        ADD     CurPosPtr.Word[0],4
        PUSH    DS
        PUSH    SI
        LDS     SI,CurPosPtr
        LDS     SI,[SI]
        MOV     AL,[SI].TPos.Col
        MOV     TargetCol,AL
        MOV     AX,[SI].TPos.Row
        POP     SI
        POP     DS
@@20:   MOV     TargetRow,AX
        RETN
@@21:   PUSH    ES
        PUSH    DI
        SUB     AX,AX
        MOV     WordLen,AL
        MOV     InKeyword,AL
        MOV     AX,SS
        MOV     ES,AX
        LEA     DI,WordBuf
@@22:   MOV     AL,[SI]
        AND     AL,AL
        JZ      @@25
        CMP     AL,' '
        JNE     @@23
        TEST    InKeyword,1
        JZ      @@26
@@23:   INC     SI
        STOSB
        CMP     AL,7
        JB      @@24
        INC     WordLen
        INC     RowLen
        JMP     @@22
@@24:   CMP     AL,2
        JNE     @@22
        XOR     InKeyword,1
        JMP     @@22
@@25:   MOV     AL,' '
        STOSB
        INC     RowLen
        JMP     @@27
@@26:   MOV     AL,' '
        STOSB
        INC     SI
        INC     RowLen
        CMP     [SI],AL
        JE      @@26
@@27:   MOV     RowEnd,DI
        POP     DI
        POP     ES
        RETN
@@28:   PUSH    DS
        PUSH    SI
        MOV     AX,SS
        MOV     DS,AX
        LEA     SI,WordBuf
        MOV     CX,RowEnd
        SUB     CX,SI
@@29:   LODSB
        STOSB
        CMP     AL,7
        SBB     HasControlChars,-1
        LOOP    @@29
        POP     SI
        POP     DS
        RETN
@@30:   CMP     HasControlChars,0
        JE      @@31
        CALL    @@18
@@31:   LDS     SI,Self
        SUB     DI,[SI].Buffer.Word[0]
        MOV     [SI].TopicSize,DI
        MOV     AX,NewMaxRow
        MOV     [SI].MaxRow,AX
        POP     AX
        PUSH    DS
        PUSH    SI
        MOV     DS,AX
        CALL    Reset
        PUSH    DS
        MOV     AX,1
@@32:   POP     DS
end;

procedure THelpTopic.GetPos(Index: Word; var Row, Col: Integer; var Len: Word);
begin
  if Index >= MaxIndex then
    Index := MaxIndex - 1;
  while (CurIndex <= Index) and (CurRow < MaxRow - 1) do
    GoForward(1);
  while CurIndex > Index do
    GoBack(1);
  Row := CurRow;
  asm
        PUSH    DS
        LDS     SI,Self
        MOV     CX,Index
        SUB     CX,[SI].CurIndex
        SHL     CX,1
        INC     CX
        LDS     SI,[SI].BufPtr
        MOV     DX,1
@@1:    LODSB
        INC     DX
        CMP     AL,7
        JAE     @@1
        DEC     DX
        CMP     AL,2
        JNE     @@1
        LOOP    @@1
@@2:    LODSB
        INC     CX
        CMP     AL,2
        JNE     @@2
        DEC     CX
        LDS     SI,Col
        MOV     [SI],DX
        LDS     SI,Len
        MOV     [SI],CX
        POP     DS
  end;
end;

function THelpTopic.GetIndex(var S: string; var Len: Word): Word; assembler;
asm
        PUSH    DS
        LDS     SI,Self
        LES     DI,[SI].Buffer
        MOV     CX,0FFFFh
        MOV     DX,[SI].MaxIndex
        MOV     AL,2
        MOV     DS,S.Word[2]
        INC     DX
        CLD
@@1:    DEC     DX
        JZ      @@7
        REPNE   SCASB
        JNZ     @@7
        MOV     SI,S.Word[0]
        MOV     BH,[SI]
        INC     SI
        SUB     BL,BL
@@2:    MOV     AH,ES:[DI]
        INC     DI
        INC     BX
        CMP     AH,' '
        JE      @@2
        DEC     BX
        INC     BH
        JMP     @@5
@@3:    CMP     AH,'a'
        JB      @@4
        CMP     AH,'z'
        JA      @@4
        AND     AH,0DFH
@@4:    CMP     AH,[SI]
        JNE     @@6
        INC     SI
        MOV     AH,ES:[DI]
        INC     DI
        INC     BX
@@5:    DEC     BH
        JNZ     @@3
        LDS     SI,Self
        MOV     AX,[SI].MaxIndex
        SUB     AX,DX
        LDS     SI,Len
        SUB     BH,BH
        MOV     [SI],BX
        JMP     @@8
@@6:    CMP     AH,AL
        JE      @@1
        REPNE   SCASB
        JZ      @@1
@@7:    MOV     AX,0FFFEH
@@8:    POP     DS
end;

function THelpTopic.GetContext(Index: Word): Integer; assembler;
asm
        PUSH    DS
        LDS     SI,Self
        MOV     AX,[SI].MaxIndex
        MOV     BX,Index
        CMP     BX,AX
        JB      @@1
        SUB     AX,AX
        JMP     @@2
@@1:    LES     DI,[SI].Buffer
        ADD     DI,[SI].BufSize
        SHL     AX,1
        SUB     DI,AX
        SHL     BX,1
        MOV     AX,ES:[BX+DI]
@@2:    POP     DS
end;

procedure THelpTopic.FindPos(Ofs: Word; var P: TPos);
begin
  if Ofs = $FFFF then
    with P do
    begin
      Row := 0;
      Col := 0
    end else
      asm
        PUSH    DS
        STD
        LDS     SI,Self
        LES     DI,[SI].Buffer
        MOV     AX,Ofs
        XCHG    AX,DI
        SUB     AX,DI
        NEG     AX
        XCHG    AX,CX
        SUB     BX,BX
        MOV     DL,1
        JCXZ    @@3
@@1:    DEC     DI
        MOV     AL,ES:[DI]
        CMP     AL,7
        SBB     DL,-1
        AND     AL,AL
        LOOPNZ  @@1
        JNZ     @@3
        INC     CX
        SUB     AL,AL
@@2:    JCXZ    @@3
        REPNZ   SCASB
        JNZ     @@3
        INC     BX
        JMP     @@2
@@3:    LDS     SI,P
        MOV     [SI].TPos.Col,DL
        MOV     [SI].TPos.Row,BX
        CLD
        POP     DS
  end;
end;

function THelpTopic.FindChar(Num: Integer; C: Char): Word; assembler;
asm
        PUSH    DS
        CLD
        LDS     SI,Self
        LES     DI,[SI].Buffer
        MOV     CX,[SI].TopicSize
        MOV     AL,C
        MOV     DX,Num
        AND     DX,DX
        JZ      @@2
@@1:    JCXZ    @@2
        REPNE   SCASB
        JZ      @@3
@@2:    MOV     AX,0FFFFH
        JMP     @@4
@@3:    DEC     DX
        JNZ     @@1
        XCHG    AX,DI
        DEC     AX
@@4:    POP     DS
end;

procedure THelpTopic.GetExample(var StartPos, EndPos: TPos);
begin
  FindPos(FindChar(1, #5), StartPos);
  FindPos(FindChar(2, #5), EndPos);
end;

end.
