[Back to MISC SWAG index] [Back to Main SWAG index] [Original]

    { Updated MISC.SWG on May 26, 1995 }

    {
    This unit has many many features, including, but not limited to:

     o InterNational upper and lower casing functions.
     o Easy reference to the keys, via consts
     o Easy access to direct screen writes (written in Assembler of course)
     o FAST wild card compare routine (thanks to Arne de Bruijn)
     o Access to miscellanous video commands, like blinkon and blinkoff, cursor
       on, and cursor off.
     o Much more to improve in overall performance of your code.

    {$A+,B-,D-,E+,F-,G-,I-,L+,N-,O-,R-,S-,V+,X+}
    {$M 16384,0,655360}
    Unit AsmMisc; { by John MD Stephenson }
    { Country specific case conversation and other info retrieval.
      Donated to the public domain by Bj”rn Felten @ 2:203/208. }
    { Arne de.Bruijn wrote the WildComp function }
    { The UnCrunch routine comes from TheDraw - public domain }
    { All other code is written by myself, or Public Domain }

    {ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿}
    {³                              } Interface {                             ³}
    {ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ}
    Uses Dos,Crt;

    { For easy reference to keys }
    Const
      _Home     = #71;
      _End      = #79;
      _Up       = #72;
      _Down     = #80;
      _Left     = #75;
      _Right    = #77;
      _PageUp   = #73;
      _PageDown = #81;
      _Insert   = #82;
      _Delete   = #83;
      _CtrlPageUp   = #132;
      _CtrlPageDown = #118;
      _CtrlHome     = #119;
      _CtrlEnd      = #117;

      _F1       = #59;
      _F2       = #60;
      _F3       = #61;
      _F4       = #62;
      _F5       = #63;
      _F6       = #64;
      _F7       = #65;
      _F8       = #66;
      _F9       = #67;
      _F10      = #68;

      { First row }
      _AltQ     = #16;
      _AltW     = #17;
      _AltE     = #18;
      _AltR     = #19;
      _AltT     = #20;
      _AltY     = #21;
      _AltU     = #22;
      _AltI     = #23;
      _AltO     = #24;
      _AltP     = #25;

      { Second row }
      _AltA     = #30;
      _AltS     = #31;
      _AltD     = #32;
      _AltF     = #33;
      _AltG     = #34;
      _AltH     = #35;
      _AltJ     = #36;
      _AltK     = #37;
      _AltL     = #38;

      { Forth row }
      _AltZ     = #44;
      _AltX     = #45;
      _AltC     = #46;
      _AltV     = #47;
      _AltB     = #48;
      _AltN     = #49;
      _AltM     = #50;

      { Number row }
      _Alt1     = #120;
      _Alt2     = #121;
      _Alt3     = #122;
      _Alt4     = #123;
      _Alt5     = #124;
      _Alt6     = #125;
      _Alt7     = #126;
      _Alt8     = #127;
      _Alt9     = #128;
      _Alt0     = #129;
      _Alt_Dash = #130;
      _Alt_Equal= #131;

      { Variations }

      _AltF1    = #104;
      _AltF2    = #105;
      _AltF3    = #106;
      _AltF4    = #107;
      _AltF5    = #108;
      _AltF6    = #109;
      _AltF7    = #110;
      _AltF8    = #111;
      _AltF10   = #112;

      _ShiftF1  = #84;
      _ShiftF2  = #85;
      _ShiftF3  = #86;
      _ShiftF4  = #87;
      _ShiftF5  = #88;
      _ShiftF6  = #89;
      _ShiftF7  = #90;
      _ShiftF8  = #91;
      _ShiftF10 = #92;

    type
      DelimType = record
        thousands,
        decimal,
        date,
        time: array[0..1] of Char;
      end;

      CurrType = (leads,             { symbol precedes value }
                  trails,            { value precedes symbol }
                  leads_,            { symbol, space, value }
                  _trails,           { value, space, symbol }
                  replace);          { replaced }

      datefmt = (USA,Europe,Japan);

      CountryType = record
        DateFormat     : Word;           { 0: USA, 1: Europe, 2: Japan }
        CurrSymbol     : array[0..4] of Char;
        Delimiter      : DelimType;      { Separators }
        CurrFormat     : CurrType;       { Way currency is formatted }
        CurrDigits     : Byte;           { Digits in currency }
        Clock24hrs     : Boolean;        { True if 24-hour clock }
        CaseMapCall    : procedure;      { Lookup table for ASCII > $80 }
        DataListSep    : array[0..1] of Char;
        CID            : word;
        Reserved       : array[0..7] of Char;
      end;

      CountryInfo =  record
        case InfoID: byte of
          1: (IDSize     : word;
              CountryID  : word;
              CodePage   : word;
              TheInfo    : CountryType);
          2: (UpCaseTable: pointer);
      end;

    var
      CountryOk          : Boolean;    { Could determine country code flag }
      CountryRec         : CountryInfo;
      Maxwidth,maxheight : Byte;
      ScreenSize         : Word;

    {ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ}
    Procedure BlinkOff;
    Procedure BlinkOn;
    Procedure CLI; Inline($FA);
    Procedure CursorOff;
    Procedure CursorOn;
    Procedure GetBorder(var color: byte);
    Procedure SetBorder(color: byte);
    Procedure PutAttrs(x,y: byte; times: word);
    Procedure PutChars(x,y: byte; chr: char; times: word);
    Procedure PutString(x,y: byte; s: string);
    Procedure ReallocateMemory(P: Pointer);
    Procedure Retrace;
    Procedure StuffChar(c: char);
    Procedure STI; Inline($FB);
    Procedure UnCrunch(var Addr1,Addr2; BlkLen: Word);
    {ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ}
    Function Execute(Name,tail: pathstr): Word;
    Function FileExists(filename: PathStr): Boolean;
    Function LoCase(c: Char) : Char;
    Function LoCaseStr(s: String): String;
    Function Upcase(c: Char) : Char;
    Function UpCaseStr(s: String): String;
    Function WildComp(NameStr,SearchStr: String): Boolean;
    {ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ}
    type
      screentype = array[0..7999] of byte;
    Var
      Segment   : word;
      Screenaddr: ^screentype;
      LoTable   : array[0..127] of byte;
      CRP, LTP  : pointer;

    {ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿}
    {³                            } Implementation {                          ³}
    {ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ}

    Procedure SetBorder(color : byte);  assembler;
    asm
      mov ax, $1001
      mov bh, color
      int $10
    End;

    Procedure GetBorder(var color : byte); assembler;
    asm
      mov ax, $1008
      int $10
      les DI, color
      mov [ES:DI], bh
    end;

    procedure CursorOff; assembler;
    asm
      mov ah, $01   { Cursor function }
      mov cx, $FFFF { Set new cursor value }
      int $10       { Video interrupt }
    end;

    procedure CursorOn; assembler;
    asm
      mov ah, $01   { Cursor function }
      mov cx, 1543  { Set new cursor value }
      int $10       { Video interrupt }
    end;

    procedure StuffChar(c : char); assembler;
    asm
      mov ah, $05
      mov cl, c   { cl = c }
      xor ch, ch  { ch = 0 }
      int $16
    end;

    Procedure BlinkOff; assembler;
    { Note that the BL is the actual register, but BH _should_ also be set to 0 }
    asm
      mov ax, $1003
      mov bx, $0000
      int $10
    end;

    Procedure BlinkOn; assembler;
    { Note that the BL is the actual register, but BH _should_ also be set to 0 }
    asm
      mov ax, $1003
      mov bx, $0001
      int $10
    end;

    Procedure Retrace; assembler;
    { waits for a vertical retrace }
      asm
        mov dx, $03DA
       @loop1:
        in al, dx
        test al, 8
        jz @loop1
       @loop2:
        in al, dx
        test al, 8
        jnz @loop2
      end;

    procedure UnCrunch(var Addr1,Addr2; BlkLen:Word); assembler;
    { From TheDraw, not my procedure }
    asm
      PUSH    DS             { Save data segment.}
      LDS     SI, Addr1      { Source Address}
      LES     DI, Addr2      { Destination Addr}
      MOV     CX, BlkLen     { Length of block}
      JCXZ    @Done
      MOV     DX,DI          { Save X coordinate for later.}
      XOR     AX,AX          { Set Current attributes.}
      CLD
     @LOOPA:
      LODSB                  { Get next character.}
      CMP     AL,32          { If a control character, jump.}
      JC      @ForeGround
      STOSW                  { Save letter on screen.}
     @Next:
      LOOP    @LOOPA
      JMP     @Done
     @ForeGround:
      CMP     AL,16          { If less than 16, then change the}
      JNC     @BackGround    { foreground color.  Otherwise jump.}
      AND     AH,0F0h        { Strip off old foreground.}
      OR      AH,AL
      JMP     @Next
     @BackGround:
      CMP     AL,24          { If less than 24, then change the}
      JZ      @NextLine      { background color.  If exactly 24,}
      JNC     @FlashBitToggle{ then jump down to next line.}
      SUB     AL,16          { Otherwise jump to multiple output}
      ADD     AL,AL          { routines.}
      ADD     AL,AL
      ADD     AL,AL
      ADD     AL,AL
      AND     AH,8Fh         { Strip off old background.}
      OR      AH,AL
      JMP     @Next
     @NextLine:
      ADD     DX,160         { If equal to 24,}
      MOV     DI,DX          { then jump down to}
      JMP     @Next          { the next line.}
     @FlashBitToggle:
      CMP     AL,27          { Does user want to toggle the blink}
      JC      @MultiOutput   { attribute?}
      JNZ     @Next
      XOR     AH,128         { Done.}
      JMP     @Next
     @MultiOutput:
      CMP     AL,25          { Set Z flag if multi-space output.}
      MOV     BX,CX          { Save main counter.}
      LODSB                  { Get count of number of times}
      MOV     CL,AL          { to display character.}
      MOV     AL,32
      JZ      @StartOutput   { Jump here if displaying spaces.}
      LODSB                  { Otherwise get character to use.}
      DEC     BX             { Adjust main counter.}
     @StartOutput:
      XOR     CH,CH
      INC     CX
      REP STOSW
      MOV     CX,BX
      DEC     CX             { Adjust main counter.}
      LOOPNZ  @LOOPA         { Loop if anything else to do...}
     @Done:
      POP     DS             { Restore data segment.}
    end;

    Procedure ReallocateMemory(P : Pointer); Assembler;
    Asm
      Mov  AX, PrefixSeg
      Mov  ES, AX
      Mov  BX, word ptr P+2
      Cmp  Word ptr P,0
      Je   @OK
      Inc  BX
     @OK:
      Sub  BX, AX
      Mov  AH, $4A
      Int  $21
      Jc   @Out
      Les  DI, P
      Mov  Word Ptr HeapEnd,DI
      Mov  Word Ptr HeapEnd+2,ES
     @Out:
    End;

    Function Execute(Name, tail : pathstr) : Word; Assembler;
    Asm
      Push Word Ptr HeapEnd+2
      Push Word Ptr HeapEnd
      Push Word Ptr Name+2
      Push Word Ptr Name
      Push Word Ptr Tail+2
      Push Word Ptr Tail
      Push Word Ptr HeapPtr+2
      Push Word Ptr HeapPtr
      Call ReallocateMemory
      Call SwapVectors
      Call Dos.Exec
      Call SwapVectors
      Call ReallocateMemory
      Mov  AX, DosError
      Or   AX, AX
      Jnz  @Done
      Mov  AH, $4D
      Int  $21 { Return error in will be in AX (if any) }
     @Done:
    End;

    Procedure Putchars(x, y : byte; chr : char; times : word);
    { Procedure to fill a count amount of characters from position x, y }
    var offst: word;
    begin
      offst := (pred(y)*maxwidth+pred(x))*2;
      asm
        mov es, segment   { Segment to start at       }
        mov di, offst     { Offset to start at        }
        mov al, chr       { Data to place             }
        mov ah, textattr  { Colour to use             }
        mov cx, times     { How many times            }
        cld               { Forward in direction      }
        rep stosw         { Store the word (cx times) }
      end;
    end;

    Procedure PutAttrs(x,y: byte; times: word);
    { This procedure is to fill a certain amount of spaces with a colour       }
    { (from cursor position) and doesn't move cursor position!                 }
    var offst: word;
    begin
      offst := succ((pred(y)*maxwidth+pred(x))*2);
      asm
        mov es, segment
        mov di, offst
        mov cx, times
        mov ah, 0
        mov al, textattr
        cld
       @s1:
        stosb
        inc di    { Increase another above what the stosb already loops }
        loop @s1  { Loop until cx = 0                                   }
      end;
    end;

    Procedure PutString(x, y: byte; s: string);
    Begin
      { Does a direct video write -- extremely fast. }
      asm
        mov dh, y         { move X and Y into DL and DH (DX) }
        mov dl, x

        xor al, al
        mov ah, textattr  { load color into AH }
        push ax           { PUSH color combo onto the stack }

        mov ax, segment
        push ax           { PUSH video segment onto stack }

        mov bx, 0040h     { check 0040h:0049h to get number of screen columns }
        mov es, bx
        mov bx, 004ah
        xor ch, ch
        mov cl, es:[bx]
        xor ah, ah        { move Y into AL; decrement to convert Pascal coords }
        mov al, dh
        dec al
        xor bh, bh        { shift X over into BL; decrement again }
        mov bl, dl
        dec bl
        cmp cl, $50       { see if we're in 80-column mode }
        je @eighty_column
        mul cx            { multiply Y by the number of columns }
        jmp @multiplied
       @eighty_column:    { 80-column mode: it may be faster to perform the }
        mov cl, 4         {   multiplication via shifts and adds: remember  }
        shl ax, cl        {   that 80d = 1010000b , so one can SHL 4, copy  }
        mov dx, ax        {   the result to DX, SHL 2, and add DX in.       }
        mov cl, 2
        shl ax, cl
        add ax, dx
       @multiplied:
        add ax, bx        { add X in }
        shl ax, 1         { multiply by 2 to get offset into video segment }
        mov di, ax        { video pointer is in DI }
        lea si, s         { string pointer is in SI }
        SEGSS lodsb
        cmp al, 00h       { if zero-length string, jump to end }
        je @done
        mov cl, al
        xor ch, ch        { string length is in CX }
        pop es            { get video segment back from stack; put in ES }
        pop ax            { get color back from stack; put in AX (AH = color) }
       @write_loop:
        SEGSS lodsb       { get character to write }
        mov es:[di], ax   { write AX to video memory }
        inc di            { increment video pointer }
        inc di
        loop @write_loop  { if CX > 0, go back to top of loop }
       @done:             { end }
      end;
    end;

    function WildComp(NameStr,SearchStr: String): Boolean; assembler;
    {
     Compare SearchStr with NameStr, and allow wildcards in SearchStr.
     The following wildcards are allowed:
     *ABC*        matches everything which contains ABC
     [A-C]*       matches everything that starts with either A,B or C
     [ADEF-JW-Z]  matches A,D,E,F,G,H,I,J,W,V,X,Y or Z
     ABC?         matches ABC, ABC1, ABC2, ABCA, ABCB etc.
     ABC[?]       matches ABC1, ABC2, ABCA, ABCB etc. (but not ABC)
     ABC*         matches everything starting with ABC
     (for using with DOS filenames like DOS (and 4DOS), you must split the
      filename in the extention and the filename, and compare them seperately)
    }
    var
      LastW: word;
    asm
      cld
      push ds
      lds si,SearchStr
      les di,NameStr
      xor ah,ah
      lodsb
      mov cx,ax
      mov al,es:[di]
      inc di
      mov bx,ax
      or cx,cx
      jnz @ChkChr
      or bx,bx
      jz @ChrAOk
      jmp @ChrNOk
      xor dh,dh
     @ChkChr:
      lodsb
      cmp al,'*'
      jne @ChkQues
      dec cx
      jz @ChrAOk
      mov dh,1
      mov LastW,cx
      jmp @ChkChr
     @ChkQues:
      cmp al,'?'
      jnz @NormChr
      inc di
      or bx,bx
      je @ChrOk
      dec bx
      jmp @ChrOk
     @NormChr:
      or bx,bx
      je @ChrNOk
     {From here to @No4DosChr is used for [0-9]/[?]/[!0-9] 4DOS wildcards...}
      cmp al,'['
      jne @No4DosChr
      cmp word ptr [si],']?'
      je @SkipRange
      mov ah,byte ptr es:[di]
      xor dl,dl
      cmp byte ptr [si],'!'
      jnz @ChkRange
      inc si
      dec cx
      jz @ChrNOk
      inc dx
     @ChkRange:
      lodsb
      dec cx
      jz @ChrNOk
      cmp al,']'
      je @NChrNOk
      cmp ah,al
      je @NChrOk
      cmp byte ptr [si],'-'
      jne @ChkRange
      inc si
      dec cx
      jz @ChrNOk
      cmp ah,al
      jae @ChkR2
      inc si              {Throw a-Z < away}
      dec cx
      jz @ChrNOk
      jmp @ChkRange
     @ChkR2:
      lodsb
      dec cx
      jz @ChrNOk
      cmp ah,al
      ja @ChkRange        {= jbe @NChrOk; jmp @ChkRange}
     @NChrOk:
      or dl,dl
      jnz @ChrNOk
      inc dx
     @NChrNOk:
      or dl,dl
      jz @ChrNOk
     @NNChrOk:
      cmp al,']'
      je @NNNChrOk
     @SkipRange:
      lodsb
      cmp al,']'
      loopne @SkipRange
      jne @ChrNOk
     @NNNChrOk:
      dec bx
      inc di
      jmp @ChrOk
     @No4DosChr:
      cmp es:[di],al
      jne @ChrNOk
      inc di
      dec bx
     @ChrOk:
      xor dh,dh
      dec cx
      jnz @ChkChr        { Can't use loop, distance >128 bytes }
      or bx,bx
      jnz @ChrNOk
     @ChrAOk:
      mov al,1
      jmp @EndR
     @ChrNOk:
      or dh,dh
      jz @IChrNOk
      jcxz @IChrNOk
      or bx,bx
      jz @IChrNOk
      inc di
      dec bx
      jz @IChrNOk
      mov ax,[LastW]
      sub ax,cx
      add cx,ax
      sub si,ax
      dec si
      jmp @ChkChr
     @IChrNOk:
      mov al,0
     @EndR:
      pop ds
    end;

    Function Upcasestr(S : String) : String; Assembler;
    Asm
      PUSH    DS
      LDS     SI,S
      LES     DI,@Result
      CLD
      LODSB
      STOSB
      xor     CH,CH
      MOV     CL,AL
      JCXZ    @OUT
     @LOOP:
      LODSB
      xor ah, ah
      push ax
      call upcase
      StoSb
      Loop    @Loop
     @OUT:
      POP   DS
    end;

    Function Locasestr(S : String) : String; Assembler;
    Asm
      PUSH    DS
      LDS     SI,S
      LES     DI,@Result
      CLD
      LODSB
      STOSB
      xor     CH,CH
      MOV     CL,AL
      JCXZ    @OUT
     @LOOP:
      LODSB
      xor ah, ah
      push ax
      call locase { So we're not duping a lot of instructions }
      STOSB
      LOOP    @LOOP
     @OUT:
      POP   DS
    end;

    { Convert a character to upper case }

    function UpCase; Assembler;
    asm
      mov     al, c
      cmp     al, 'a'
      jb      @2
      cmp     al, 'z'
      ja      @1
      sub     al, ' '
      jmp     @2
     @1:
      cmp     al, 80h
      jb      @2
      sub     al, 7eh
      push    ds
      lds     bx,CountryRec.UpCaseTable
      xlat
      pop     ds
     @2:
    end; { UpCase }

      { Convert a character to lower case }

    function LoCase; Assembler;
    asm
      mov     al, c
      cmp     al, 'A'
      jb      @2
      cmp     al, 'Z'
      ja      @1
      or      al, ' '
      jmp     @2
     @1:
      cmp     al, 80h
      jb      @2
      sub     al, 80h
      mov     bx,offset LoTable
      xlat
     @2:
    end;                                 { LoCase }

    Function FileExists(filename: PathStr): Boolean; Assembler;
    Asm
      Push Ds
      Lds  Si, [filename]      { Make ASCIIZ }
      Xor  Ah, Ah
      Lodsb
      XChg Ax, Bx
      Mov  Byte Ptr [Si+Bx], 0
      Mov  Dx, Si
      Mov  Ax, 4300h           { Get file attributes }
      Int  21h
      Mov  Al, False
      Jc   @1                  { Fail? }
      Inc  Ax
     @1:
      Pop  Ds
    end;  { FileExists }

    Begin
      { Init the video addresses }
      if lastmode = 7 then segment := $B000 else segment := $B800;
      screenaddr := ptr(segment,$0000);

      { Init the video }
      Maxwidth := succ(lo(windmax));  { Get maximum window positions, which are   }
      Maxheight := succ(hi(windmax)); { the maxwidth and maxheight to be precise! }
      ScreenSize := maxheight*maxwidth*2; { For easy references to move commands. }

      { Init the tables for Upcasing }
      Crp := @CountryRec;
      Ltp := @LoTable;
      asm
        { Exit if Dos version < 3.0 }
        mov     ah, 30h
        int     21h
        cmp     al, 3
        jb      @1
        { Call Dos 'Get extended country information' function }
        mov     ax, 6501h
        les     di, CRP
        mov     bx,-1
        mov     dx,bx
        mov     cx,41
        int     21h
        jc      @1
        { Call Dos 'Get country dependent information' function }
        mov     ax, 6502h
        mov     bx, CountryRec.CodePage
        mov     dx, CountryRec.CountryID
        mov     CountryRec.TheInfo.CID, dx
        mov     cx, 5
        int     21h
        jc      @1
        { Build LoCase table }
        les     di, LTP
        mov     cx, 80h
        mov     ax, cx
        cld
       @3:
        stosb
        inc     ax
        loop    @3
        mov     di, offset LoTable - 80h
        mov     cx, 80h
        mov     dx, cx
        push    ds
        lds     bx, CountryRec.UpCaseTable
        sub     bx, 7eh
       @4:
        mov     ax, dx
        xlat
        cmp     ax, 80h
        jl      @5
        cmp     dx, ax
        je      @5
        xchg    bx, ax
        mov     es:[bx+di], dl
        xchg    bx, ax
       @5:
        inc     dx
        loop    @4
        pop     ds

        mov     [CountryOk], True
        jmp     @2
       @1:
        mov     [CountryOk], False
       @2:
      end;
    end.

[Back to MISC SWAG index] [Back to Main SWAG index] [Original]