Vistas de página en total

sábado, 7 de enero de 2012

Menú Contextual

La gente copiaba sus álbumes desde el pc a su mp3, en el pc los temas aparecían ordenados alfabéticamente por el título, que normalmente iban precedidos por el ordinal correspondiente tal que alfabéticamente se reprodujesen en su mismo orden del álbum. Pero Windows en una selección múltiple copia los temas según el orden de la FAT del sistema de archivos que no suele corresponder precisamente. Entonces al reproducir los temas del álbum en su mp3 se encontraban con que no estaban en el orden que pretendían. Esta opción añadida al sistema proporcionaba la solución al problema   

ContExtCopyAlf.dll


 {
      Módulo MenuContextual

      Objeto COM que implementa las interfaces IShellExtInit e IContextMenu,
      para crear un gestor de extensión de la Shell - menú contextual -
      controla la selección de una única carpeta que contenga más de un archivo,
      o varios archivos (más de uno) que no sean carpetas.
      Se crea una copia en el portapapeles con los archivos
      ordenados alfabéticamente para pegar en el sistema de archivo.

      JMCP     (c)Febrero 2006
}    

Unit MenuContextual;

Interface
Uses
    Windows, ComObj, ShlObj, ComServ, ShellApi, ActiveX, SysUtils;

Const  // Este es el identificador único para nuestra extensión
   CLSID_MenuContextual: TGUID = '{39F382A0-55FA-11D1-9A28-004033CA9316}';

Type
    // Derivamos nuestra clase de TComObject, indicando que se
    // van a implementar las interfaces IShellExtInit e IContextMenu
    TMenuContextual = Class(TComObject, IShellExtInit, IContextMenu)
    Private
      sPath, sCarpeta: String;
      lstArchivos: Array[0..999] Of String;
      strData, strDirs: String;
      nFiles: Integer; // no. de archivos

      procedure SortFiles();
      function LeeCarpeta(const szCarpeta: PChar): Boolean;
      function CopiaXCarpeta(hWnd: HWND): Boolean;
      function LeeDirs(strCarpeta: String): Integer;

    Public
          // Este método de la interfaz IContextMenu permite añadir
          // la opción al menú contextual
      Function QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast,
        uFlags: UINT): HResult; Stdcall;
          // Este método, también de IContextMenu, es el usado para
          // ejecutar las opciones añadidas al menú
      Function InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; Stdcall;
          // Tercer método de la interfaz IContextMenu, su finalidad
          // es devolver una cadena de descripción de la opción indicada
      Function GetCommandString(idCmd, uType: UINT; pwReserved: PUINT;
        pszName: LPSTR; cchMax: UINT): HResult; Stdcall;

          // Único método de la interfaz IShellExtInit, cuya finalidad
          // es inicializar el objeto facilitándole información sobre
          // los archivos seleccionados al pulsar el botón derecho
      Function Initialize (pidlFolder: PItemIDList; lpdobj: IDataObject;
        hKeyProgID: HKEY): HResult; Stdcall;
    End;

Implementation

// Este método de la interfaz IContextMenu es llamado por
// la shell de Windows cuando se va a desplegar el menú -
// emergente, ofreciendo así la posibilidad de añadir opciones
Function TMenuContextual.QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst,
          idCmdLast, uFlags: UINT): HResult;
Begin
     // Añadimos nuestra opción en la posición indexMenu con el
     // identificador idCmdFirst
  InsertMenu (Menu, indexMenu, MF_STRING + MF_BYPOSITION, idCmdFirst, 'Copiar Al&fabéticamente');
     // Devolvemos el número de opciones añadidas
  Result := 1;
End;

    // Este método de la interfaz IContextMenu es llamado por
    // la shell de Windows para recuperar una descripción de
    // una de las opciones añadidas al menú contextual
Function TMenuContextual.GetCommandString(idCmd, uType: UINT; pwReserved: PUINT;
  pszName: LPSTR; cchMax: UINT): HRESULT;
Begin
     Result := NOERROR; // Asumimos que no hay problema

     Case idCmd Of // Según la opción que sea
          0: // copiamos un descripción u otra en pszName
            StrLCopy(pszName, PChar('Copia de (' + Format('%d',[nFiles])
             + ') archivos en orden alfabético.'
             + ' Utilice Pegar para ponerlos en una ubicación nueva.'), cchMax-1);
          Else // si no es ninguna de nuestras opciones
               Result := E_INVALIDARG; // devolvemos un error
     End;
End;

    // Este método de la interfaz IContextMenu es llamado por
    // la shell de Windows cuando se el usuario elige una de
    // las opciones añadidas por nosotros, dándonos así opción
    // a ejecutar el proceso que corresponda
Function TMenuContextual.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult;
            var
              res: Boolean;
              i:   Integer;
              df:  DROPFILES;
              hgd, hge, hgf:  Cardinal;
              lpgd, lpge, lpgf:  Pointer;
              CF_PREFERREDDROPEFFECT, CF_FILENAME : TClipFormat;
Begin
     Result := E_FAIL; // error
     res := TRUE;
     Case LoWord(lpici.lpVerb) Of // Según la opción elegida
       0:
       Begin
         If Length(sCarpeta) > 0 Then
         Begin
           {
           MessageBox(lpici.hwnd,
              PChar('Archivos de ' + sPath + ' la carpeta ' + sCarpeta
               + Format(' (%d)', [nFiles])), 'Copiar Alfabéticamente', MB_OK);
           }
           res := CopiaXCarpeta(lpici.hwnd); // recopia (crea, copia y renombra) carpeta
         End;
         If OpenClipboard(0) And res Then
         Try
          Begin
            EmptyClipboard();
            // Reg. tipos
            CF_PREFERREDDROPEFFECT := RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
            CF_FILENAME := RegisterClipboardFormat(CFSTR_FILENAMEA);
            // mem para DROPEFFECT
            hge := GlobalAlloc(GHND, SizeOf(Cardinal));
            If hge <> 0 Then
            Begin
              lpge := GlobalLock(hge);
              Cardinal(lpge^) := DROPEFFECT_COPY;
              GlobalUnlock(hge);
              SortFiles();
              strData := '';
              For i := 0  to nFiles-1 do
                 strData := strData + sPath + lstArchivos[i] + #0;
              strData := strData + #0;
              // mem para DROP
              hgd := GlobalAlloc(GHND + GMEM_DDESHARE, Length(strData) + SizeOf(df));
              If hgd <> 0 Then
              Begin
               lpgd := GlobalLock(hgd);
               df.pFiles := SizeOf(df);
               CopyMemory(lpgd, @df, SizeOf(df));
               CopyMemory(PChar(lpgd)+SizeOf(df), PChar(strData), Length(strData));
               GlobalUnlock(hgd);
               // mem para CF_FILENAME (antiguo formato de Windows 3.1x)
               hgf := GlobalAlloc(GHND, Length(strData));
               If hgf <> 0 Then
               Begin
                lpgf := GlobalLock(hgf);
                CopyMemory(lpgf, PChar(strData), Length(strData));
                GlobalUnlock(hgf);
               End;
               // poner Clips
               If (SetClipboardData(CF_PREFERREDDROPEFFECT, hge) <> 0) And
                  (SetClipboardData(CF_HDROP, hgd) <> 0) And
                  (SetClipboardData(CF_FILENAME, hgf) <> 0) Then
                 Result := NOERROR;
              End;
            End;
          End;
         Finally
            // Terminar
            CloseClipboard();
         End;
       End; // case 0
     Else     // otras?
          Result := E_INVALIDARG;
     End; // case
End;

// .........


// Este método de la interfaz IShellExtInit es llamado por la
// shell de Windows para inicializar el objeto, entregándole
// información sobre los archivos seleccionados
Function TMenuContextual.Initialize(pidlFolder: PItemIDList; lpdobj: IDataObject;
  hKeyProgID: HKEY): HResult;
Var
   Medio:   TStgMedium;
   Formato: TFormatEtc;
   Datos:   TSHFILEINFO;
   strFile: string;
   szFile:  Array[0..MAX_PATH] Of Char; // Nombre con Path
   i, k, cnt: Integer;
Begin
      Result := E_FAIL; // inicialmente por defecto
      //
      If lpdobj = Nil Then // Si no hay elementos seleccionados
            Exit; // devolvemos un error y no continuamos

      With Formato Do // Preparamos Formato
       Begin
           cfFormat := CF_HDROP; // formato arrastrar-soltar
           ptd := Nil; // sin dispositivo de destino
           dwAspect := DVASPECT_CONTENT; // queremos el contenido
           lindex := -1; // de toda la información
           tymed := TYMED_HGLOBAL; // en memoria
       End;

      // Preparamos los datos apuntados por lpdobj, que es una interfaz IDataObject,
      // en un medio de almacenamiento HGLOBAL en formato CF_HDROP
      If lpdobj.GetData(Formato, Medio) < 0 Then // Si no ha sido posible
         Exit; // no continuamos
         // Si hay seleccionado más de un archivo (NO FOLDER) pueden ordenarse
         // y los copiamos en el array lstArchivos
      cnt := DragQueryFile(Medio.hGlobal, $FFFFFFFF, Nil, 0);
      If cnt > 1 then
        Begin
          k := 0;
          for i := 0 to cnt-1 do
          Begin
            DragQueryFile(Medio.hGlobal, i, szFile, MAX_PATH);
            SHGetFileInfo(szFile, 0, Datos, SizeOf(Datos), SHGFI_ATTRIBUTES);
            If ((Datos.dwAttributes And SFGAO_FILESYSTEM ) <> 0) And
               ((Datos.dwAttributes And SFGAO_FOLDER) = 0) then
            Begin
                SetString(strFile, szFile, StrLen(szFile));
                if k = 0 Then
                    sPath := ExtractFilePath(strFile);
                //
                lstArchivos[k] := ExtractFileName(strFile);
                Inc(k);
            End;
          End;
          nFiles := k;
          If k > 1 then
            Result := NOERROR;
        End
      Else
        If cnt = 1 Then // debe ser una carpeta
        Begin
           DragQueryFile(Medio.hGlobal, 0, szFile, MAX_PATH);
           SHGetFileInfo(szFile, 0, Datos, SizeOf(Datos), SHGFI_ATTRIBUTES);
           If ((Datos.dwAttributes And SFGAO_FOLDER) <> 0) And
              (LeeCarpeta(szFile))  then
               Result := NOERROR;
        End;
      // final
      ReleaseStgMedium(Medio); // Liberamos el medio
End;

// ..........


Initialization // Al inicializar el módulo
         // Creamos el objeto identificador por el CLSID_MenuContextual,
         // indicando que la clase es TMenuContextual y que el servidor COM es ComServer
    TComObjectFactory.Create(ComServer, TMenuContextual, CLSID_MenuContextual,
         '', 'Extensión Shell para copia alf. de archivos', ciMultiInstance);

End.

_______________________




REGEDIT4
[HKEY_CLASSES_ROOT\CLSID\{39F382A0-55FA-11D1-9A28-004033CA9316}] 
@= "Extension Context Menu para Copiar archivos Alfabéticamente"
[HKEY_CLASSES_ROOT\CLSID\{39F382A0-55FA-11D1-9A28-004033CA9316}\InProcServer32] 
@= "ContExtCopyAlf.dll"
"ThreadingModel" = "Apartment"
[HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\CopiarAlf] 
@= "{39F382A0-55FA-11D1-9A28-004033CA9316}"
[HKEY_CLASSES_ROOT\Folder\shellex\ContextMenuHandlers\CopiarAlf] 
@= "{39F382A0-55FA-11D1-9A28-004033CA9316}"






viernes, 6 de enero de 2012

CRTVIDEO.C y KO.COM

Esto es algo de un vicio superado.


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
 *                                                                     *
 *   v_DrawLine   Pintar linea de punto a punto con un valor de pixels *
 *                El modo de interactuar es el establecido SetWROpMode *
 *                                                       EGA/MCGA/VGA  *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

void v_DrawLine(int x1,     /* coordenada X inicio de linea          */
                int y1,     /* coordenada Y inicio de linea          */
                int x2,     /* coordenada X final de la linea        */
                int y2,     /* coordenada Y final de la linea        */
                int vp)     /* valor de los pixel de linea a pintar  */
{
unsigned char far *bvideo;
unsigned char uchx,bvp;
unsigned vramdes;   /* desplaz. RAM video   */
unsigned vexceso;   /* se compara con el banco anterior */
int bc,pw,bpl,ivp,inc_a,inc_b,inc_v,idx,idy,ixch;
int ordr; /* Rutina según pendiente */

/* Horizontales y Verticales en sentido positivo */
if(y1==y2){ if(x2<x1){ ixch=x1; x1=x2; x2=ixch; }
            v_DrawHLine(x1, y1, x2-x1+1, vp);
            return;
            }
if(x1==x2){ if(y2<y1){ ixch=y1; y1=y2; y2=ixch; }
            v_DrawVLine(x1, y1, y2-y1+1, vp);
            return;
            }

/* Datos del modo, etc */
vp%=256; /* para no liarse con los regs. del cont.gráf. */
bc=v_GetBitCount();
pw=v_GetMaxX() +1;    /* ancho de linea modo  */
bpl=pw;
if(bc!=8) bpl/=8;
bvp=vp;
ordr=0;

/* Sentido positivo en X */
/* if(x1>x2){ ixch=x1; x1=x2; x2=ixch;  */
/*            ixch=y1; y1=y2; y2=ixch; } */
asm { MOV  cx,x2
      SUB  cx,x1
      JNS  @seguir1
      NEG  cx
      MOV  bx,x2
      XCHG bx,x1
      MOV  x2,bx
      MOV  bx,y2
      XCHG bx,y1
      MOV  y2,bx

/* Distancias */
/* idx=x1-x2; */
/* inc_v=bpl; */
/* idy=y2-y1; */
/* Cuadrante  */
/* if(idy<0){ -idy; -inc_v; }  */

@seguir1:
      MOV  si,bpl
      MOV  bx,y2
      SUB  bx,y1
      JNS  @seguir2
      NEG  bx
      NEG  si

/* Rutina según pendiente < 1 <=  */
/* if(idx<idy){ ordr=1; ixch=idx; idx=idy; idy=ixch; }    pendiente */

@seguir2:
      MOV  inc_v,si
      CMP  bx,cx
      JLE  @seguir3
      MOV  ordr,1
      XCHG bx,cx

/* Incrementos y decisión */
/* inc_a=2*idy;         2dy       */
/* v_dec=inc_a - idx;   2dy - dx  */
/* inc_b=v_dec - idx;   2(dy-dx)  */

@seguir3:
      MOV  idx,cx
      SHL  bx,1
      MOV  inc_a,bx
      SUB  bx,cx
      MOV  si,bx       ; /* si variable decisión */
      SUB  bx,cx
      MOV  inc_b,bx

/* if(bc==8) vdesp=(unsigned long)pw * y1 + x1;    byte video ->pixel   */
/*    else   vdesp=(unsigned long)pw/8*y1 + x1/8;  byte que lo contiene */

      MOV  ax,pw
      MOV  bx,x1
      CMP  bc,8
      JZ   @l8bc
      MOV  cl,3
      SHR  ax,cl
      SHR  bx,cl
@l8bc:
      MOV  cx,y1
      MUL  cx
      ADD  ax,bx
      ADC  dx,0
      MOV  vramdes,ax
      MOV  vexceso,dx
      }

if(vexceso!=abanco) v_SetBankRAM(vexceso); // preparar reg. Secuenciador

/* Rutinas para 256 colores */
if(bc==8) {
        asm { PUSH ds
              PUSH es
              MOV  ax,0xA000
              MOV  es,ax
              MOV  di,vramdes
              MOV  bx,inc_v  ; /* incremento en y */
              MOV  cx,idx    ; /* no. de pixels   */
              INC  cx
              }
  if(!ordr){
        asm { MOV  al,bvp
      @lazo11:
              STOSB
              OR   di,di
              JNZ  @seg11
              PUSH ax
              PUSH bx
              PUSH cx
              }
              v_SetBankRAM(++vexceso);
        asm { POP cx
              POP bx
              POP ax
       @seg11:
              OR   si,si
              JNS  @lazo12
              ADD  si,inc_a
       @rep11:
              LOOP @lazo11
              JMP  @final1
      @lazo12:
              ADD  si,inc_b
              MOV  dx,di
              ADD  di,bx
              OR   bx,bx
              JNS  @siposi
              CMP  dx,di
              JA   @rep11
              JMP  @ponban1
      @siposi:
              CMP  dx,di
              JB   @rep11
     @ponban1:
              PUSH ax
              PUSH bx
              PUSH cx
              }
              if(inc_v<0) v_SetBankRAM(--vexceso);
                     else v_SetBankRAM(++vexceso);
        asm { POP cx
              POP bx
              POP ax
              JMP @rep11
      @final1:
              }
    } /* fin pendiente<1 */
  else {
        asm { MOV  al,bvp
      @lazo21:
              STOSB
              DEC  di
              MOV  dx,di
              ADD  di,bx
              OR   bx,bx
              JNS  @sipos2
              CMP  dx,di
              JA   @lazo22     // JNC
              JMP  @ponban2
       @sipos2:
              CMP  dx,di
              JB   @lazo22    // JC
     @ponban2:
              PUSH ax
              PUSH bx
              PUSH cx
              }
              if(inc_v<0) v_SetBankRAM(--vexceso);
                     else v_SetBankRAM(++vexceso);
        asm { POP cx
              POP bx
              POP ax
       @lazo22:
              OR  si,si
              JNS  @lazo23
              ADD  si,inc_a
        @ll21:
              LOOP @lazo21
              JMP  @salfin
       @lazo23:
              ADD  si,inc_b
              INC  di
              JNZ  @ll21
              PUSH cx
              PUSH bx
              PUSH ax
              }
              v_SetBankRAM(++vexceso);
        asm { POP ax
              POP bx
              POP cx
              JMP @ll21
      @salfin:
              }
    } /* fin pendiente>1 */
          asm POP es
          asm POP ds
  } /* fin if(bc==8) */

 /* NO 256 colores */
else {  /* preparar regs. control gráf */
        uchx=x1;
        asm { PUSH ds
              PUSH es
              MOV  dx,0x03CE
              MOV  ah,bvp
              XOR  al,al
              OUT  dx,ax
              MOV  ax,0x0F01
              OUT  dx,ax
              MOV  ax,0xA000
              MOV  es,ax
              MOV  di,vramdes
              MOV  ah,0x80    ; /* máscara inicial  */
              MOV  cl,uchx    ; /* byte bajo de X   */
              AND  cl,0x07    ; /* resto=mod 8      */
              SHR  ah,cl      ; /* rota bit masc. a posición pixel */
              MOV  bl,ah      ; /* máscara primer pixel */
              MOV  cx,idx     ; /* no. de pixels  */
              INC  cx
              MOV  al,0x08    ; /* reg. máscara */
              }
   if(!ordr){  /* Pendiente <=1 */
    if(pw!=1024){
         asm {
         @bu10:
              MOV ah,bl      ; /* másc. próximo pixel */
        @bu11:
              OR   ah,bl
              ROR  bl,1
              JC   @bu14       ; /* pixel de la máscara a la izq. */
              OR   si,si
              JNS  @bu12
              ADD  si,inc_a
              LOOP @bu11
              OUT  dx,ax
              OR   es:[di],al
              JMP  @acab
        @bu12:
              ADD  si,inc_b
              OUT  dx,ax
              OR   es:[di],al
              ADD  di,inc_v
      @lpbu10:
              LOOP @bu10
              JMP  @acab
        @bu14:
              OUT  dx,ax
              OR   es:[di],al
              INC  di
              OR   si,si
              JNS  @bu15
              ADD  si,inc_a
              JMP  @lpbu10
        @bu15:
              ADD  si,inc_b
              ADD  di,inc_v
              JMP  @lpbu10
        @acab:
              }
    } /* fin si pw!=1024 */
  else {  // EL PROBLEMATICO no 256col, pw=1024, pendiente<=1 -YA NO LO ES
          asm {
         @b10:
              MOV ah,bl      ; /* másc. próximo pixel */
         @b11:
              OR   ah,bl
              ROR  bl,1
              JC   @b14       ; /* pixel de la máscara a la izq. */
              OR   si,si
              JNS  @b12
              ADD  si,inc_a
              LOOP @b11
              OUT  dx,ax
              OR   es:[di],al
              JMP  @acaba1
       @lxb10:
              JMP @b10
         @b12:
              ADD  si,inc_b
              OUT  dx,ax
              OR   es:[di],al
              PUSH ax
              MOV  ax,inc_v
              OR   ax,ax
              POP  ax
              JNS  @pos12
              ADD  di,inc_v
              JNC  @ponb12
      @lopb10:
              LOOP @lxb10
              JMP  @acaba1
       @pos12:
              ADD  di,inc_v
              JC   @ponb12
              JMP  @lopb10
      @ponb12:
              PUSH dx
              PUSH bx
              PUSH ax
              PUSH cx
              }
              if(inc_v<0) v_SetBankRAM(--vexceso);
                     else v_SetBankRAM(++vexceso);
          asm {
              POP cx
              POP ax
              POP bx
              POP dx
              JMP @lopb10
         @b14:
              OUT  dx,ax
              OR   es:[di],al
              INC  di  ; /* NO necesario en Horiz. INC de Bank */
              OR   si,si
              JNS  @b15
              ADD  si,inc_a
              JMP  @lopb10
         @b15:
              ADD  si,inc_b
              PUSH ax
              MOV  ax,inc_v
              OR   ax,ax
              POP  ax
              JNS  @pos15
              ADD  di,inc_v
              JNC  @ponb15
              JMP  @lopb10
       @pos15:
              ADD  di,inc_v
              JC   @ponb15
              JMP  @lopb10
       @ponb15:
              PUSH dx
              PUSH bx
              PUSH ax
              PUSH cx
              }
              if(inc_v<0) v_SetBankRAM(--vexceso);
                     else v_SetBankRAM(++vexceso);
        asm {
              POP cx
              POP ax
              POP bx
              POP dx
              JMP @lopb10
        @acaba1:
              }
    } /* fin pw==1024 */
   } /* fin pendiente <= 1 */
  else {
    if(pw!=1024){
        asm { MOV  bx,inc_v
        @lb21:
              OUT  dx,ax
              OR   es:[di],al
              ADD  di,bx
        @lb22:
              OR   si,si
              JNS  @lb23
              ADD  si,inc_a
              LOOP @lb21
              JMP  @aca2
        @lb23:
              ADD  si,inc_b
              ROR  ah,1
              ADC  di,0
              LOOP @lb21
        @aca2:
              }
   }
   else {   // para 1024 x 768 aun con 16 colores hay más de un banco
        asm { MOV  bx,inc_v
         @b21:
              OUT  dx,ax
              OR   es:[di],al
              PUSH ax
              MOV  ax,di
              OR   bx,bx
              JNS  @sip21
              ADD  di,bx
              JC   @b22
              JMP  @ponb21
        @lxb21:
              JMP @b21
       @sip21:
              ADD  di,bx
              JNC  @b22
      @ponb21:
              PUSH bx
              PUSH cx
              PUSH dx
              }
              if(inc_v<0) v_SetBankRAM(--vexceso);
                     else v_SetBankRAM(++vexceso);
        asm { POP dx
              POP cx
              POP bx
         @b22:
              POP  ax
              OR   si,si
              JNS  @b23
              ADD  si,inc_a
       @lpb21:
              LOOP @lxb21
              JMP  @acaba2
         @b23:
              ADD  si,inc_b
              ROR  ah,1
              ADC  di,0
              JNZ  @lpb21
              PUSH dx
              PUSH cx
              PUSH bx
              PUSH ax
              }
              v_SetBankRAM(++vexceso);
        asm { POP ax
              POP bx
              POP cx
              POP dx
              JMP @lpb21
       @acaba2:
              }
    }
   } // fin else=pendiente > 1
        asm { MOV  ah,0xFF    ; // Máscara def.
              OUT  dx,ax
              XOR  ax,ax
              OUT  dx,ax      ; // pone por def.  Set / Reset 
              INC  dx
              OUT  dx,ax      ; // pone por def.  Reg. Activación
              POP  es
              POP  ds
              }
  } // fin else bc!=8
}  // fin func. DrawVLine


///////////////////////////
debug KO.DBG
============
N ko.com
a
jmp  114
db 'Sistema detenido!$'
mov  ah,0F
int  10
mov  ah,00
int  10
mov  ah,1
mov  ch,20
int  10
mov  dx,102
mov  ah,9
int  21
cli
jmp  12A

r cx
2C
w
q




behera
⇩
⇧
gora