Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Mensajes - Meta

#41
Lo intenté hacer con remove al comoBox, se borra, pero cuando encuentra otra unidad sigue el problema.

Código (csharp) [Seleccionar]
private void Form1_Load(object sender, EventArgs e)

{
    // Nombre de la unidad.
    ConsigueComponentes("Win32_CDROMDrive", "Id");

    foreach (char item in datos)
    {
        comboBox_Unidad.Items.Add(item);
    }

    // Selecciona la primera unidad.
    comboBox_Unidad.SelectedIndex = 0;

    // Limpiar.
    datos = "";

}
#42
Hola:

Quiero añadir unidades detectadas de si hay lectores en un PC o ordenador.

Cada vez que me detecta dos lectores en el comboBox me lo rellena así:

F
:
G
:

Como cuatros elementos cuando en realidad son dos y debe mostrarlos así:
F:
G:

Me da igual si solo también se queda como abajo.
F
G

¿Hay alguna forma de corregir estas cosas?

Saludos.
#43
Buenas:

Me gustó el ejemplo que usaste máscaras a la hora de tratar bits en un Byte. Lo he hecho en Windows Form como ejemplo.

Ver zoom.

Código (csharp) [Seleccionar]
using System;
using System.Windows.Forms;

namespace Leer_bits_de_un_Byte_01
{
   public partial class Form1 : Form
   {
       public Form1()
       {
           InitializeComponent();
       }

       int ascii;
       int resultado;
       readonly int[] mascara = new int[8]
       {
           0b_0000_0001, // Posición [0].
           0b_0000_0010,
           0b_0000_0100,
           0b_0000_1000,
           0b_0001_0000,
           0b_0010_0000,
           0b_0100_0000,
           0b_1000_0000  // Posición [7].
       };

       private void textBox_ASCII_TextChanged(object sender, EventArgs e)
       {
           try
           {
               ascii = int.Parse(textBox_ASCII.Text);
               resultado = ascii & mascara[0];
               if (resultado == 0) { label_c0.Text = "0"; } else { label_c0.Text = "1"; }
               resultado = ascii & mascara[1];
               if (resultado == 0) { label_c1.Text = "0"; } else { label_c1.Text = "1"; }
               resultado = ascii & mascara[2];
               if (resultado == 0) { label_c2.Text = "0"; } else { label_c2.Text = "1"; }
               resultado = ascii & mascara[3];
               if (resultado == 0) { label_c3.Text = "0"; } else { label_c3.Text = "1"; }
               resultado = ascii & mascara[4];
               if (resultado == 0) { label_c4.Text = "0"; } else { label_c4.Text = "1"; }
               resultado = ascii & mascara[5];
               if (resultado == 0) { label_c5.Text = "0"; } else { label_c5.Text = "1"; }
               resultado = ascii & mascara[6];
               if (resultado == 0) { label_c6.Text = "0"; } else { label_c6.Text = "1"; }
               resultado = ascii & mascara[7];
               if (resultado == 0) { label_c7.Text = "0"; } else { label_c7.Text = "1"; }
           }
           catch(Exception error)
           {
               MessageBox.Show(error.Message, "Aviso:",
               MessageBoxButtons.OK, MessageBoxIcon.Warning);
           }
       }
   }
}


En cuanto a lo que quiero decir desde el principio, lo comento de otra manera para que se entienda mejor.

El progrma de ahora mismo está hecho en consola, solo está diseñado para recibir una respuesta de un comando.

Por ejemplo el comando B<cr>

Si envío el comando B<cr> recibo una trama de Bytes, que cada respuesta recibida, siempre empieza la cadena en # como inicio y en <cr> como fin. <cr> es Retorno de Carro. En código es así, /r.

¿Qué ocurre cuando envío otro comando diferente a un dispositivo?

Que me devuelve otro resultado diferente y este programa en C# no lo procesa como debería.

Dejo un ejemplo de un Terminal indicado abajo.
Envío comando B<cr> me da una respuesta larga. Luego envío otro comando llamado X87<cr> y me llega otra cadena más corta y no tiene nada que ver con la B, así con todos los comandos.


Ver zoom.

Aquí abajo dejo un PDF que me dio el fabricante de un SAI. Se muestra el protocolo de comunicación, los comandos y que respuesta recibes.

Descargar.

En resumen.
Quiero hacer un programa, que al enviar un comando, el que sea, me procese la cadena de caracteres recibidos correspondiente.

Espero que esta vez se entienda.

Muchas gracias.  :D

PD: Con tu ejemplo de las máscaras, voy hacer un ejemplo con enum como hicistes en un programa a parte para cotillear y aprender mejor.
#44
Muy buen explicación.

En mi caso, siguiendo lo que me dicen, más la locura que tengo en la cabeza, el programa que hice es muy chapucero pero...

¡¡¡FUNCIONA!!!

;-) ;-) ;-) ;-) ;-) ;-) ;-) ;-)


Ver imagen.

Código C#:
Código (csharp) [Seleccionar]


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Delimitador_consola_03
{
    class Program
    {
        static void Main(string[] args)
        {
            #region Configuración ventana.
            // Título de la ventana.
            Console.Title = "Probando manipulación de cadena";

            // Tamaño de la ventana, x, y.
            Console.SetWindowSize(100, 35);

            // Color de fondo.
            Console.BackgroundColor = ConsoleColor.DarkBlue;

            // Color de las letras.
            Console.ForegroundColor = ConsoleColor.Yellow;

            // Limpiar pantalla y dejarlo todo en color de fondo.
            Console.Clear();

            // Visible el cursor.
            Console.CursorVisible = true;
            #endregion

            // Cree una codificación ISO-8859-1.
            Encoding ISO_8859_1 = Encoding.GetEncoding("ISO-8859-1");

            #region Variables.
            // Partir a trocitos estos caracteres.
            char[] delimitadorComandoB = { '#', ',', 'O', 'I', 'L', 'B', 'V', 'F', 'H', 'R', 'S', };

            // Es la cadena de caracteres que me llegó desde el puerto serie.
            // En este ejemplo lo dejo en la varible directamente.
            // Una cadena completa empieza con # y termina en <cr>, o lo que es lo mismo, /r.
            string respuestaB = "#I223.3O224.0L000B100V26.4F50.2H50.2R0080S,,€€,,À"; // Comando B.

            // Se guarga en este array tipo string los datos ya partidos a tozos.
            string[] palabrasB = respuestaB.Split(delimitadorComandoB);

            // Tabla S1 descripción.
            string[] DESCRIPCION_S1 =
            {
                "Indica que este byte está disponible y es válido. [Siempre 1]",             // Posición [0].
                "Indica falla de energía de la red pública; ver detalles en S2.0 y S2.1.",
                "Indica que la capacidad de la batería es menor que el umbral de apagado.",
                "Indica que el zumbador está en estado de pitido.",
                "Indica que la prueba de batería se está procesando.",
                "Indica que el apagado programado está pendiente.",
                "Indica que la restauración programada está pendiente.",
                "Indica falla de hardware; ver detalles sobre el comando X71."               // Posición [7].
            };

            string[] DESCRIPCION_S2 =
            {
                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
                "--",
                "XX",
                "Indica sobretemperatura del inversor.",
                "Indica que el inversor tiene una falla.",
                "Indica que el inversor está apagado.",
                "Indica que la frecuencia de la utilidad está fuera de rango.",
                "Indica que el voltaje de la red pública está fuera de rango."
            };

            string[] DESCRIPCION_S3 =
            {
                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
                "Indica que la batería está completamente cargada.",
                "Indica que la capacidad de la batería aún es menor que el umbral restaurado del UPS cuando la energía de la red pública restaurado.",
                "Indica que la batería se está cargando.",
                "Indica que la batería se está descargando.",
                "Indica que la capacidad de la batería es inferior al 80 por ciento.",
                "Reservado, debe ser 0.",
                "Indica que la batería no está presente."
            };

            string[] DESCRIPCION_S4 =
{
                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
                "Indica que el bypass es una sobrecarga.",
                "Indica que la derivación está activa.",
                "Indica que la salida sufre un cortocircuito.",
                "Indica que la salida tiene carga.",
                "Indica que la salida está sobrecargada.",
                "Indica que la frecuencia de salida está fuera de rango en bypass.",
                "Indica que el voltaje de salida está fuera de rango en derivación."
            };

            string[] DESCRIPCION_S5 =
{
                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
                "Indica que no hay salida.",
                "Para indicar que el tiempo de ejecución restante es inferior al umbral.",
                "Para indicar que el zumbador está silenciado (no permanente) en este momento.",
                "Para indicar falla de cableado.",
                "Para indicar SAI en modo ECO.",
                "Para indicar UPS en Bypass manual.",
                "Arreglar 0."
            };

            string[] DESCRIPCION_S6 =
{
                "Para indicar que este byte está disponible y es válido. [Siempre 1].",
                "Indica UPS encendido.",
                "Reservado, debe ser 0.",
                "Reservado, debe ser 0.",
                "Reservado, debe ser 0.",
                "Reservado, debe ser 0.",
                "Reservado, debe ser 0.",
                "Reservado, debe ser 0."
            };

            bool boolS1 = true;
            bool boolS2 = false;
            bool boolS3 = false;
            bool boolS4 = false;
            bool boolS5 = false;
            bool boolS6 = false;
            bool boolContador = true;
            bool boolContador2 = false;
            #endregion

            // Muestra los resultados en pantalla.
            Console.WriteLine();
            Console.WriteLine("El voltaje de la utilidad es de {0} voltios. ", palabrasB[2]); // I.
            Console.WriteLine("El voltaje de salida del UPS es de {0} voltios. ", palabrasB[3]); // O.
            Console.WriteLine("La carga actual de UPS es del {0} por ciento. ", palabrasB[4]); // L.
            Console.WriteLine("La capacidad de la batería es del {0} por ciento. ", palabrasB[5]); // B.
            Console.WriteLine("El voltaje de la batería es de {0} voltios. ", palabrasB[6]); // V.
            //Console.WriteLine("La temperatura del gabinete del UPS es de {0} grados centígrados. ", palabrasB[7]); // T. No hay T. en mi versión.
            Console.WriteLine("La frecuencia de salida del SAI es de {0} Hz. ", palabrasB[7]); // F.
            Console.WriteLine("La frecuencia de salida del SAI es de {0} Hz. ", palabrasB[8]); // H.
            Console.WriteLine("El tiempo de funcionamiento restante de la batería es de {0} minutos. ", palabrasB[9]);
            //Console.WriteLine(@"Estos son los bits de estados que no se entiende.S:
            //Aquí hay que leer cada bits como cuando se leyeron cada Byte arriba: ", (char)palabrasB[10]);

            char[] bits = palabrasB[10].ToCharArray();

            Tabla();
            int contador = 0;
            for (int b = 0; b < bits.Length; b++)
            {
                for (int a = 7; a >= 0; a--)
                {
                   
                    Console.Write((((byte)bits[b]) & (1 << a)) >> a);
                    Console.Write(" | ");

                    if ((contador <= 7) && (boolS1 == true))
                    {
                        Console.WriteLine(DESCRIPCION_S1[contador]);

                        if (contador == 7)
                        {
                            boolS1 = false;
                            boolS2 = true;
                            boolContador2 = true;
                            contador = 0;
                        }
                    }
                    else if ((contador <= 7) && (boolS2 == true))
                    {
                        Console.WriteLine(DESCRIPCION_S2[contador]);
                        boolContador2 = false;
                        if (contador == 7)
                        {
                            boolS2 = false;
                            boolS3 = true;
                            boolContador2 = true;
                            contador = 0;
                        }
                    }
                    else if ((contador <= 7) && (boolS3 == true))
                    {
                        Console.WriteLine(DESCRIPCION_S3[contador]);
                        boolContador2 = false;
                        if (contador == 7)
                        {
                            boolS3 = false;
                            boolS4 = true;
                            boolContador2 = true;
                            contador = 0;
                        }
                    }
                    else if ((contador <= 7) && (boolS4 == true))
                    {
                        Console.WriteLine(DESCRIPCION_S4[contador]);
                        boolContador2 = false;
                        if (contador == 7)
                        {
                            boolS4 = false;
                            boolS5 = true;
                            boolContador2 = true;
                            contador = 0;
                        }
                    }
                    else if ((contador <= 7) && (boolS5 == true))
                    {
                        Console.WriteLine(DESCRIPCION_S5[contador]);
                        boolContador2 = false;
                        if (contador == 7)
                        {
                            boolS5 = false;
                            boolS6 = true;
                            boolContador2 = true;
                            contador = 0;
                        }
                    }
                    else if ((contador <= 7) && (boolS6 == true))
                    {
                        Console.WriteLine(DESCRIPCION_S6[contador]);
                        boolContador2 = false;
                        if (contador == 7)
                        {
                            boolS6 = true;
                            boolContador = true;
                            boolContador2 = true;
                            contador = 0;
                        }
                    }

                    if (boolContador == true)
                    {
                        contador++;

                        if (boolContador2 == true)
                        {
                            contador = 0;
                        }
                    }
                    //Console.WriteLine();
                }
                Console.WriteLine();
            }
           

            #region Tabla.
            void Tabla()
            {
                Console.WriteLine("Byte | Bit | Estado | Descripción");
                Console.WriteLine("-----+-----+-----------------------------------------------------------------------");
            }
            #endregion

            // Pulse cualquier tecla para salir.
            Console.ReadKey();
        }
    }
}



Código chapuza pero funciona.

Ahora la pregunta del millón.

Cada vez que envío un comando me da un resultado diferente.

¿Cómo procesaría cada resultado correspondiente a cada comando?

Está claro que el comando B<cr>, la cadena al recibir no tiene nada que ver con otros comandos.

¿Se entiende lo qué quiero decir?

Si no entiendes, lo pregunto de otra manera.

Gracias.

Gracias a todos.
#45
ASM / Juego Space Invader en asm
4 Abril 2021, 10:25 AM
Hola:

Quiero saber si este juego en asm se puede ejecutar en Visual studio bajo C++ nativo, ya que ejecutas asm también. Lo que no se hacerlo ya que no hago pruebas desde hace añazos.

¿Es posible ejecutarlo?



Código ASM.
Código (asm) [Seleccionar]
cXLimIzq       EQU  32                  ; X-Limite izquierda juego
  cXLimDer       EQU  320-cXLimIzq        ; X-Limite derecha juego
  cYLimSup       EQU  60                  ; Y-Limite superior juego
  cYLimInf       EQU  180                 ; Y-Limite inferior juego
  cIncYEDisp     EQU  1                   ; Y-incremento de los e-disparos
  cTamDefFig     EQU  7*2                 ; Los words que definen la figura
  cSepIniEDisp   EQU  4                   ; Separación inicial del disparo enemigo
  cColorEDisp    EQU  14                  ; Color del disparo enemigo
  cColorPDisp    EQU  7                   ; Color del disparo del jugador
  cFigAlto       EQU  7                   ; Altura de las figuras
  cFigAncho      EQU  16                  ; Anchura de las figuras
  cAncho         EQU  256                 ; Anchura de la pantalla de juego
  cAlto          EQU  cYLimInf-cYLimSup+1 ; Altura de la pantalla de juego
  cSepMargen     EQU  3                   ; Margen de separación con el borde
  cLinColor      EQU  6                   ; Color de la línea contenedora
  cFrecBomb      EQU  010h                ; Frecuencia con la que empiezan a caer bombas
  cNumEColumn    EQU  11                  ; Número de columnas de enemigos
  cNumEnemigos   EQU  5*cNumEColumn       ; Número de enemigos
  cDispEne       EQU  5                   ; Máximo num disparos x cada enemigo
  cNumDispEne    EQU  7;cDispEne*cNumEColumn; Máximo disparos activos enemigos
  cXESep         EQU  1                   ; X-separación entre enemigos
  cYESep         EQU  3                   ; Y-separación entre enemigos
  cXESup         EQU  (cAncho-((16+cXESep)*10+16))/2  ; X-inicial-superior enemigos
  cYESup         EQU  cYLimSup-cYLimSup   ; Y-superior enemigos
  cYPSup         EQU  cYLimInf-7-cYLimSup ; Y-superior player
  cXPIzq         EQU  0                   ; X-lim izquierda player
  cXPDcha        EQU  256-11              ; X-lim derecha player
  cTimePlayer    EQU  0                   ; Tiempo retardo movimiento player
  cTimeEnemigos  EQU  20                  ; Tiempo retardo movimiento enemigos
  cTimePDisp     EQU  0                   ; Tiempo retardo movimiento player-disparo
  cTimeEDisp     EQU  0                   ; Tiempo retardo movimiento enemigos-disparos
  cTimeESgteDisp EQU  10                  ; Tiempo retardo siguiente enemigos-disparos
  cChgTEnemigos  EQU  2                   ; Cada cuántos enemigos destruidos incrementan velocidad
  cFilasBaja     EQU  2                   ; Filas que bajan los enemigos cada vez
  cYTope         EQU  cYPSup-10           ; Tope superior que no pueden sobrepasar enemigos
  cSoyInmune     EQU  0                   ; 1-soy inmune, 0-no soy inmune
  cTAMTOT        EQU  1024*6              ; Este programa y su pila caben en 6Kb
  cnNotas        EQU  7+6+7+5

  TEnemigos   STRUC
    PosX    DW ?
    PosY    DW ?
    OldPosX DW ?
    Def     DW ?                          ; La dirección donde está definido
    Despl   DW ?                          ; El desplazamiento de offsets (+7?)
    Tipo    DB ?                          ; 1, 2, 3, 4, 5
    Color   DB ?
    Vida    DB ?
    Abajo   DB ?                          ; Sólo disparan los que estén abajo
  TEnemigos   ENDS

  TCabEDisp STRUC                         ; Estructura de la cabecera de los disparos enemigos
    DirDisp DW ?                          ; Dirección donde se encuentra el disparo
    Vida    DB ?                          ; ¿Está vivo?
  TCabEDisp ENDS

  TEDisparos   STRUC                      ; Disparos de los enemigos
    PosX    DW ?
    PosY    DW ?
    Def     DW ?                          ; Matriz de definición del disparo
    Despl   DW ?                          ;   junto con el desplazamiento de offsets (+7?)
    Color   DB ?
  TEDisparos   ENDS

  TPlayer   STRUC
    PosX    DW ?
    PosY    DW ?
    Def     DW ?                          ; La dirección donde está definido
    Color   DB ?
    Vida    DB ?
  TPlayer   ENDS

  TPDisparos   STRUC                      ; Disparos del jugador
    PosX    DW ?
    PosY    DW ?
    Def     DW ?
    Despl   DW ?                          ; El desplazamiento de offsets (+7?)
    Color   DB ?
    Vida    DB ?
  TPDisparos   ENDS

  TNota STRUC
    Frecuencia DW ?
    Duracion   DB ?
  TNota ENDS

  LTEnemigos   EQU SIZEOF TEnemigos
  LTEDisparos  EQU SIZEOF TEDisparos
  LTCabEDisp   EQU SIZEOF TCabEDisp
  LTPlayer     EQU SIZEOF TPlayer
  LTPDisparos  EQU SIZEOF TPDisparos
  LTNota       EQU SIZEOF TNota

  IF1
    INCLUDE ..\LIB\CtesGphM.inc           ; Constantes gráficas
    INCLUDE ..\LIB\GraphMM.inc            ; Incorporamos las macros gráficas
  ENDIF

codigo SEGMENT PARA PUBLIC 'CODE'         ; Abre el segmento de código
  ASSUME CS:codigo, DS:codigo, ES:codigo, SS:codigo
  ORG 100h                                ; COM -> comienza en 100h
  Entrada PROC                            ; Abre el procedimiento
    ; *** Reasigna memoria
    MOV         SP, cTAMTOT               ; La base de la pila
    MOV         AH, 4Ah
    MOV         BX, cTAMTOT/16            ; Lo pasamos a párrafos
    INT         21h                       ; Redimensionamos el bloque de memoria
    JNC         $_Bien1
      MOV        AH, 9
      MOV        DX, OFFSET msg1
      INT        21h
      JMP        $_Salimos
    $_Bien1:
    CALL        VGAColor                  ; Comprobamos si existe VGA en color
    ; Reserva memoria para el búfer pant1
    MOV         BX, (cXLimDer-cXLimIzq+1)*(cYLimInf-cYLimSup+1)/16+1 ; En párrafos
    CALL        ResMem
    MOV         WORD PTR [ScrSeg], AX
    SetMode     13h                       ; Ponemos el modo gráfico
    CALL        DibMarco
    CALL        SaveValSPK
    CALL        Randomize
    CALL        SaveOldInt9h
    CALL        SetNewInt9h
    CALL        SaveOldInt1Ch
    CALL        SetNewInt1Ch
    CALL        IniCabEDisp
    CALL        ResetNivel                ; Inicializamos el nivel del juego
    $_Nueva_Partida:
    CALL        IniElementos              ; Inicializa valores en función de variables y constantes
    $_Main:
      CALL        GetPShoot               ; ¿Hemos pulsado ctrl?
      CALL        GetEShoot               ; ¿Debe disparar un enemigo?
      CALL        SetMotion               ; Movemos las piezas si toca
      JNC         $_Next0
        ; Si STC = 1 significa que los enemigos han llegado al nivel inferior: ganaron
        CALL        ResetNivel            ; Inicializamos el nivel del juego
        CALL        WaitUntilCtrl         ; Esperamos hasta Ctrl pulsado
        JMP         $_Nueva_Partida
      $_Next0:
      CMP         BYTE PTR [NumEnemigos], 0
      JA          $_Next1
        CALL        incNivel              ; Incrementamos el nivel del juego
        JMP         $_Nueva_Partida
      $_Next1:
      CMP         BYTE PTR [Player.Vida], 0
      JA          $_Next2
        CALL        ResetNivel            ; Inicializamos el nivel del juego
        CALL        WaitUntilCtrl         ; Esperamos hasta Ctrl pulsado
        JMP         $_Nueva_Partida
      $_Next2:
      CALL        ClearBuffer             ; Limpiamos el búfer
      CALL        DibujaEnemigos          ; Dibujamos los enemigos en el búfer
      CALL        DibujaJugador           ; Dibujamos el jugador en el búfer
      CALL        DibujaPDisparo          ; Dibujamos el disparo del jugador en búfer
      CALL        DibujaEDisparo          ; Dibujamos los disparos de los enemigos en búfer
      CALL        CheckPDEDColision       ; ¿El disparo del jugador alcanzo al de algun enemigo?
      CALL        CheckMPDColision        ; ¿El disparo del jugador alcanzo algun enemigo?
      CALL        CheckMEDColision        ; ¿El disparo de los enemigos alcanzaron al jugador?
      WRetrace    8                       ; Esperamos al final del retrazo
      MOV         AX, WORD PTR [ScrSeg]   ; Segmento Origen
      MOV         BX, __VgaSeg            ; Segmento destino: mem vídeo
      CALL        Copy64K                 ; Vuelca búfer en mem vídeo
      CMP         BYTE PTR [Escape], 1
    JNZ         $_Main
    SetMode     3h                        ; Volvemos al modo texto
    CALL        RestoreOldInt1Ch
    CALL        RestoreOldInt9h
    CALL        TurnOffSpk
    MOV         AX, WORD PTR [ScrSeg]     ; Libera memoria pantalla
    CALL        LibMem
    $_Salimos:
    ; Salimos al DOS
    MOV      AX, 4C00h                    ; Servicio 4Ch, mensaje 0
    INT      21h                          ; volvemos AL DOS
  Entrada ENDP                            ; cierra el procedimiento

  IF1
    INCLUDE  ..\LIB\VGA_M.inc             ; Incorporamos procedimientos
    INCLUDE  ..\LIB\SpcM.inc              ; Incorporamos procedimientos
  ENDIF

  ; ****************

  TurnOffSpk PROC
    ;  Propósito: Apaga el altavoz
    ;  Entrada  : Ninguna
    ;  Salida   : Ninguna
    ;  Destruye : AX
    MOV      AL, BYTE PTR [vbSpeaker]
    AND      AL, 11111100b                ; Apagamos y descconectamos el altavoz del canal 2 del PIT
    OUT      61h, AL
    RET
  TurnOffSpk ENDP

  SaveValSPK PROC
    ;  Propósito: Guarda el valor original del puerto 61h
    ;  Entrada  : Ninguna
    ;  Salida   : Ninguna
    ;  Destruye : AX
    IN       AL, 61h
    AND      AL, 11111100b
    MOV      BYTE PTR [vbSpeaker], AL
    RET
  SaveValSPK ENDP
 
  Frec_Periodo PROC
    ;  Propósito: Convertimos la frecuencia en período
    ;  Entrada  : CX: frecuencia
    ;  Salida   : DX: período
    ;  Destruye : AX, DX
    MOV      DX, 12h
    MOV      AX, 34DCh
    DIV      CX
    MOV      DX, AX                       ; Lo guardamos en DX
    RET
  Frec_Periodo ENDP
 
  PIT_REG_COMM PROC
    ;  Propósito: Le indicamos al PIT lo que vamos a hacer
    ;  Entrada  : DX:periodo
    ;  Salida   : Ninguna
    ;  Destruye : AX
    MOV      AL, 0B6h
    OUT      43h, AL
    MOV      AL, DL                       ; Pasamos el byte bajo del contador
    OUT      42h, AL
    MOV      AL, DH                       ; Y ahora el alto
    OUT      42h, AL
    RET
  PIT_REG_COMM ENDP

  SpkOn PROC
    ;  Propósito: Encendemos el altavoz con duración determinada
    ;  Entrada  : Ninguna
    ;  Salida   : Ninguna
    ;  Destruye : AX
    MOV      AL, BYTE PTR CS:[vbSpeaker]
    OR       AL, 3
    OUT      61h, AL
    RET
  SpkOn ENDP

  SpkOff PROC
    ;  Propósito: Encendemos el altavoz con duración determinada
    ;  Entrada  : Ninguna
    ;  Salida   : Ninguna
    ;  Destruye : AX
    MOV      AL, BYTE PTR CS:[vbSpeaker]
    AND      AL, 11111100b
    OUT      61h, AL
    RET
  SpkOff ENDP

  Sonido PROC
    ;  Propósito: Toca una nota en el altavoz del PC a través del PIT
    ;  Entrada  : CX:frecuencia
    ;  Salida   : Ninguna
    ;  Destruye : Ninguna
    .IF      CX == 0
      CALL     SpkOff
    .ELSE
      CALL     Frec_Periodo
      CALL     PIT_REG_COMM
      CALL     SpkOn
    .ENDIF
    RET
  Sonido ENDP
 
  ResetNota PROC
    ; Propósito: Recogemos los valores de la nueva nota en MNota
    ; entrada  : KeyTable
    ; salida   : KeyTable
    ; Destruye : AX
    PUSH      AX
    PUSH      BX
    MOV       BX, WORD PTR CS:[vwActual]
    MOV       AX, WORD PTR CS:[Notas1+BX]
    MOV       WORD PTR CS:[MNota.Frecuencia], AX
    MOV       AX, WORD PTR CS:[Notas1+BX+2]
    MOV       BYTE PTR CS:[MNota.Duracion], AL
    POP       BX
    POP       AX
    RET
  ResetNota ENDP

  ; ****************

  SaveOldInt1Ch PROC NEAR
    ; Propósito: Guarda la dirección de la antigua ISR de Int 1Ch
    ; entrada  : OldInt1Ch
    ; salida   : Ninguna
    ; Destruye : AX
    MOV       AX, 351Ch                   ; Obtiene la dirección de la
    INT       21h                         ;     interrupción 1Ch
    MOV       WORD PTR OldInt1Ch[2],ES    ; Guarda segmento
    MOV       WORD PTR OldInt1Ch[0],BX    ;     y dirección
    RET
  SaveOldInt1Ch ENDP

  RestoreOldInt1Ch PROC NEAR
    ; Propósito: Restaura la dirección de la antigua ISR de Int 1Ch
    ; entrada  : OldInt1Ch
    ; salida   : Ninguna
    ; Destruye : AX, DX
    PUSH      DS                          ; Guardamos DS
    LDS       DX, OldInt1Ch               ; Carga la dirección original
    MOV       AX, 251Ch                   ; Lo restaura a la tabla de vectores
    INT       21h
    POP       DS
    RET
  RestoreOldInt1Ch ENDP

  SetNewInt1Ch PROC NEAR
    ; Propósito: Establece la dirección de la nueva ISR para Int 1Ch
    ; entrada  : NuevaInt1Ch
    ; salida   : Ninguna
    ; Destruye : AX, DX
    MOV       DX, OFFSET NuevaInt1Ch      ; Carga la dirección de nueva rutina
    MOV       AX, 251Ch                   ; Establece la nueva interrupción
    INT       21h
    RET
  SetNewInt1Ch ENDP

  NuevaInt1Ch PROC FAR
    ; Propósito: Nueva ISR para la INT 1Ch, crea un retardo
    ; entrada  : Ninguna
    ; salida   : Ninguna
    ; Destruye : Ninguna
    PUSHF
    PUSH      AX
    PUSH      BX
    PUSH      CX
    PUSH      DX
    PUSH      DS
    PUSH      ES
    .IF       BYTE PTR CS:[vbEjecutando] == 1
      ; Si está sonando la nota
      DEC       BYTE PTR CS:[MNota.Duracion]            ; Decrementamos la duración
      .IF       BYTE PTR CS:[MNota.Duracion] == 0
        ; Si ya ha terminado la duración de la nota
        ADD       WORD PTR CS:[vwActual], 4             ; Incrementamos puntero a notas
        CALL      ResetNota                             ; Recogemos nueva nota
        .IF       WORD PTR CS:[vwActual] >= cnNotas*4
          ; Si ya hemos terminado todas las notas
          MOV       BYTE PTR CS:[vbEjecutando], 0       ; Marcamos que no tocamos
          MOV       WORD PTR CS:[vwActual], 0           ; Reseteamos el puntero a notas
          CALL      TurnOffSpk
        .ELSE
          ; Si todavía no hemos terminado todas las notas
          MOV       CX, WORD PTR CS:[MNota.Frecuencia]
          CALL      Sonido
        .ENDIF
      .ENDIF
    .ELSE
      ; Si no está sonando ninguna nota
      .IF       BYTE PTR CS:[vbEjecutar] == 1
        ; Si debemos ejecutar las notas
        .IF       WORD PTR CS:[vwActual] == 0
          ; Si estamos al inicio, reseteamos
          CALL      ResetNota
        .ENDIF
        .IF       BYTE PTR CS:[MNota.Duracion] != 0
          ; Si todavía dura la nota, la tocamos
          MOV       BYTE PTR CS:[vbEjecutando], 1       ; Marcamos para tocar
          MOV       CX, WORD PTR CS:[MNota.Frecuencia]
          CALL      Sonido
        .ENDIF
      .ENDIF
    .ENDIF
    CMP       BYTE PTR CS:[TimePlayer], 0
    JZ        $_Next1
      DEC       BYTE PTR CS:[TimePlayer]      ; Decrementamos el contador de pulsos
    $_Next1:
    CMP       BYTE PTR CS:[TimeEnemigos], 0
    JZ        $_Next2
      DEC       BYTE PTR CS:[TimeEnemigos]      ; Decrementamos el contador de pulsos
    $_Next2:
    CMP       BYTE PTR CS:[TimePDisp], 0
    JZ        $_Next3
      DEC       BYTE PTR CS:[TimePDisp]      ; Decrementamos el contador de pulsos
    $_Next3:
    CMP       BYTE PTR CS:[TimeEDisp], 0
    JZ        $_Next4
      DEC       BYTE PTR CS:[TimeEDisp]      ; Decrementamos el contador de pulsos
    $_Next4:
      CMP       BYTE PTR CS:[TimeESgteDisp], 0
      JZ        $_GT_Next5
        DEC       BYTE PTR CS:[TimeESgteDisp]   ; Retardo para el siguiente disparo de los enemigos
      $_GT_Next5:
    POP       ES
    POP       DS
    POP       DX
    POP       CX
    POP       BX
    POP       AX
    POPF
    JMP       [CS:OldInt1Ch]              ; Saltamos a la vieja rutina
  NuevaInt1Ch  ENDP

TopInvader1             DW      0c00h,1e00h,2d00h,3f00h,1200h,2100h,1200h
;   0c00   0000110000000000
;   1e00   0001111000000000
;   2d00   0010110100000000
;   3f00   0011111100000000
;   1200   0001001000000000
;   2100   0010000100000000
;   1200   0001001000000000
TopInvader2             DW      0c00h,1e00h,2d00h,3f00h,1200h,2100h,4080h
;   0c00   0000110000000000
;   1e00   0001111000000000
;   2d00   0010110100000000
;   3f00   0011111100000000
;   1200   0001001000000000
;   2100   0010000100000000
;   4080   0100000010000000
MiddleInvader1          DW      2100h,9e40h,0ad40h,7f80h,3f00h,2100h,4080h
MiddleInvader2          DW      2100h,1e00h,2d00h,7f80h,0bf40h,0a140h,1200h
BottomInvader1          DW      01e00h,7f80h,0ccc0h,0ffc0h,2100h,4c80h,2100h
BottomInvader2          DW      01e00h,7f80h,0ccc0h,0ffc0h,2100h,4c80h,8040h
PlayersShip             DW      0400h,0e00h,7fc0h,0ffe0h,0ffe0h,0ffe0h,0000h
;   0400   0000010000000000
;   0e00   0000111000000000
;   7fc0   0111111111000000
;   ffe0   1111111111100000
;   ffe0   1111111111100000
;   ffe0   1111111111100000
;   0000   0000000000000000
TwistedMissile1         DW      0000h,0000h,0000h,0800h,0400h,0800h,0400h
;   0000   0000000000000000
;   0000   0000000000000000
;   0000   0000000000000000
;   0800   0000100000000000
;   0400   0000010000000000
;   0800   0000100000000000
;   0400   0000010000000000
TwistedMissile2         DW      0000h,0000h,0000h,0400h,0800h,0400h,0800h
StraightMissile         DW      0000h,0000h,0000h,0400h,0400h,0400h,0400h

  MEnemigos  TEnemigos  cNumEnemigos DUP (<>)    ; Enemigos sin inicializar
  MCabEDisp  TCabEDisp  cNumDispEne  DUP (<>)    ; Disparos de los enemigos
;   align 16
;   db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1             ;16
  MEDisparos TEDisparos cNumDispEne  DUP (<>)    ; Disparos de los enemigos
  Player     TPlayer    <>                       ; Jugador sin inicializar
  MPDisparos TPDisparos <>                       ; Disparos de los enemigos

  vbSpeaker    DB   ?
  vbEjecutar   DB   1                     ; Lo definimos indicando que suenen las notas
  vbEjecutando DB   0
  vwActual     DW   0
  Notas1       DW   262, 3, 330, 3, 349, 3, 392, 3, 349, 3, 294, 3, 0, 8     ; 7
               DW   262, 3, 330, 3, 349, 3, 392, 3, 349, 3,   0, 8           ; 6
               DW   262, 3, 330, 3, 349, 3, 392, 3, 349, 3, 294, 3, 0, 8     ; 7
               DW   330, 3, 349, 3, 330, 3, 262, 3, 262, 3                   ; 5
  MNota        TNota  <>

msg1           DB  "Error al REDIMENSIONAR memoria$"
msg2           DB  "Error al RESERVAR memoria$"
msg3           DB  "Error al LIBERAR memoria$"
msgAOE         DB  "(c)Abre los ojos al ensamblador", 13, 10
               DB  "Ejemplo de space invaders$"
msgExito       DB  "Enhorabuena, has salvado al planeta Tierra$"
msgFracaso     DB  "Por tu impericia la Tierra ha sido destruida$"
msgBlanco      DB  "                                            $"
NumEnemigos    DB  ?                      ; Número de enemigos que quedan
OldInt9h       DD  ?                      ; Dirección antigua de Int 9h
OldInt1Ch      DD  ?                      ; Dirección antigua de Int 1Ch
ScrSeg         DW  ?                      ; Segmento del búfer de pantalla
DirEnemigos    DW  ?                      ; Dirección del mvto de los enemigos
DesplDefFig    DW  ?                      ; Para pintar el mvto de la figura
TimePlayer     DB  ?                      ; Retardo para el mvto del jugador
TimeEnemigos   DB  ?                      ; Retardo para el mvto de los enemigos
TimePDisp      DB  ?                      ; Retardo para el mvto disparo jugador
TimeEDisp      DB  ?                      ; Retardo para el mvto disparo enemigos
TimeESgteDisp  DB  ?                      ; Retardo para el siguiente disparo enemigos
vNextTEnemigos DB  ?                      ; Siguiente retardo de los enemigos
TicksReloj     DD  ?                      ; Ticks de reloj
ActFrecBomb    DB  ?                      ; Frecuencia actual disparos enemigos (cambia)
NumEColumn     DW  ?                      ; Número de columnas de enemigos que quedan
ChgTEnemigos   DB  ?                      ; Cada cuantos enemigos muertos incr su velocidad
vSoyInmune     DB  cSoyInmune             ; Ser o no ser inmune
; Datos modificados en cada nivel
vYESup         DW  ?                      ; X-inicial-superior de los enemigos
; Datos que modifica la nueva ISR del teclado
PlayerLeft     DB  0
PlayerRight    DB  0
PlayerFire     DB  0
Escape         DB  0

codigo ENDS
END Entrada


Más códigos aquí.
http://abreojosensamblador.epizy.com/?Tarea=1&SubTarea=35&i=1

Gracias.
#46
Cita de: @XSStringManolo en  2 Abril 2021, 23:05 PM
Uhh, no sabía que sucedía ese error.

Suma de 1 en 1 en lugar de 8 en 8 y cuando i % 8 == 0 añades un espacio.

Si te refieres a esto.
Código (csharp) [Seleccionar]
for (int i = 0; i < recibidos.Length; i += 8)
           {
               richTextBox1.Text += recibidos.Substring(i, 1) + " ";
           }


O a esto ottro.
Código (csharp) [Seleccionar]
for (int i = 0; i < recibidos.Length; i += 1)
           {
               richTextBox1.Text += recibidos.Substring(i, 8) + " ";
           }


No funciona.

Cita de: WHK en  3 Abril 2021, 03:14 AM
Eso pasa porque el tipo String en .net tiene pre filtros como la eliminación de padding vacío, hay que procesarlo como un arreglo de bytes, sino pasarás haciendo parches y de una u otra manera tendrás problema con algún valor.

Los ejemplos de Microsoft, tiene la manía de recibir datos tipo sttring aquí y pòr aquí. Es verdad que en el puerto serie se transmite byte por byte. Aquí en SerialPort.ReadExisting es funciona con string.

Dicen muchos por Internet, que para estas cosas mejor aceptar las tramas de byte tal como vienen, en byte. Guardar los datos en Byte y luego procesarlo. Ya se usaría esto otro SerialPort.Read.

El problema si usas tipo arreglo con Byte[] tienes que saber el tamaño que te llega en ese arreglo. El programa completo cambiaría por todas partes, no es cambiar de tipo a string a Byte[] así sin más. Ahí está el problema.

Encuentro otro problema con el invoke al recibir datos por el puerto serie el como se comporta.
Código (csharp) [Seleccionar]
// Al recibir datos.
       private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
       {
           // Acumula los caracteres recibidos a nuestro 'buffer' (string).
           recibidos += serialPort1.ReadExisting();

           // Invocar o llamar al proceso de tramas.
           Invoke(new EventHandler(Actualizar));
       }


Si recibo este mensaje en ASCII, o lo que sea, por ejemplo. Debería mostrar el mensaje recibido tal como aparece en el Ejemplo 1. En el Ejemlo 2, a veces se corta, como si alguien le diera Enter en medio de la frase.

Ejemplo 1:
No hay mayor tesoro que un amigo.

Ejemplo 2:
No hay mayor tesoro q
ue un amigo.

No he conseguido ningún programa estable que funcione siempre como el Ejemplo 1.

Saludos.
#47
Buen truco en C++.

Me funcionó en modo consola en Windows.

Ver imagen.

Código (cpp) [Seleccionar]
#include <iostream>
#include <windows.h> // Para mostrar texto en el título de la ventana.
#include <stdio.h>   // Cambio color de fondo y letras.

using namespace std;
//using std::cout;
//using std::cin;

typedef unsigned char Byte;

enum
{
EST_VENTILADOR,
EST_PERSIANA,
EST_ALARMA,
EST_EXTRACTOR,
EST_LUZ_B,
EST_LUZ_A,
EST_MOTOR_B,
EST_MOTOR_A,

EST_TODOS_LOS_CONTROLADORES,
};

bool LeerBit(Byte elByte, Byte elBit)
{
return (elByte & (1 << elBit)) != 0;
}

int main(int argc, char* argv[])
{
// Mostrar caracteres correctamente en pantalla y título de la ventana.
SetConsoleOutputCP(65001);
wchar_t titulo[128];
MultiByteToWideChar(CP_UTF8, 0, "Título de la ventana. Win32 C++ 2019.", -1, titulo, 128);
SetConsoleTitle(titulo);

// Tamaño de la pantalla. Se cambia en los dos últimos dígitos.
SMALL_RECT r = { 0, 0, 49, 9 }; // X = 49, Y = 9.
SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), TRUE, &r);

// Cambio color de A (verde claro), color letras 0 (negro).
system("color A0");

// Ocultar cursor.
CONSOLE_CURSOR_INFO cci;
GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);
cci.bVisible = 0; // 0 oculta. 1 muestra cursor.
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci);

Byte unEstado = 0b11001000;

for (Byte i = 0; i < EST_TODOS_LOS_CONTROLADORES; i++)
{
printf("El bit: %i tiene valor %s\n", i, (LeerBit(unEstado, i) ? "Activo" : "Inactivo"));
}
cout << endl; // Nueva línea.
system("pause");
return 0;
}


Voy a hacerlo ahora mismo en C# en modo consola y luego a Windows Form.

Gracias por el detalle.  ;-)
#48
.NET (C#, VB.NET, ASP) / Leer bits de Byte
2 Abril 2021, 13:14 PM
Buenas camaradas:

Haciendo un programa hecho con Windows Form bajo .Net FrameWotk 4.7. Si envío un comando por el puerto serie que se llama X82, recibo un mensaje que en ASCII no se entiende porque usa caracteres raros.

Lo paso a hexadecimal y en binario.

Envío un comando, ocurre algún evento y en Windoes Form me aparece cógios en HEX y en BIN. Se puede cambiar varios bit en un único Byte.

Por ejemplo, si me llega este dato.

Hexadecimal: 23C797C0B00D

Binario: 00100011 11000111 10010111 11000000 10110000 00001101

Un ejemplo, es poner muchos labels en cada bits y una tabla de cada Byte recibido. Simpre son la misma cantidad de bytes ya que en realdiad hace de Flags.

Centrándonos en el binario, ya que hay 6 Bytes.

Código (csharp) [Seleccionar]

00100011 11000111 10010111 11000000 10110000 00001101
-------- -------- -------- -------- -------- --------
Byte I  Byte a    Byte b   Byte c   Byte d   Byte F


En el Byte c que corresponde al 11000000 quiero leer el bit 3 que corresponde al "Extractor" indicado en la tabla de abajo. Cada Byte tiene su tabla, ahora nos centramos en un Byte y ver los estados de los bits.

bit:c Dato        Función.
7 =   1            Motor A.
6 =   1            Motor B.
5 =   0            Luz A.
4 =   0            Luz B.
3 =   0            Extractor.
2 =   0            Alarma.
1 =   0            Persiana.
0 =   0            Ventilador

El Byte c que ahora contiene estos bits que son 11000000, me llega una nueva trama de Bytes y precisamente este, cambia de 11000000 a 11001000. Solo ha cambiado un bit que es el 3 en el Byte c.

Cada bit tiene su label para mostrarlo en el formulario de Windows. La tabla de abajo se actualiza.

bit:c Dato        Función.
7 =   1            Motor A.
6 =   1            Motor B.
5 =   0            Luz A.
4 =   0            Luz B.
3 =   1            Extractor.
2 =   0            Alarma.
1 =   0            Persiana.
0 =   0            Ventilador

Antes el Byte c del bit 3 que es el Extractor estaba a 0, ahora es 1.

En resumen. Quiero saber como se leen los bits que me llegan del puerto serie.

¿Existe la posibilidad de hacer un programa así?

Saludos.

PD: En esta clase de programas que no suelo usar, es como las aves, fáciles de ver, difíciles de alcanzar.
#49
Buenas:

Como veo la base ya tienes una variable con 0 y 1.
Hice el foreach y for que indicaste. Me da problemas. Lo hice así para que lo pase a binario y elfor tuyo para que lo separe.


Ver zoom.

Código (csharp) [Seleccionar]
           // Pasar a binario.
           string resultado = "";
           foreach (string leer in recibidos.Select(c => Convert.ToString(c, 2)))
           {
               resultado += leer.ToString();
           }    
           
           for (int i = 0; i < resultado.Length; i += 8)
           {
               richTextBox1.Text += resultado.Substring(i, 8) + " ";
           }


Si lo hago solo así que se muestre en richTextBox me da el  mismo error.
Código (csharp) [Seleccionar]
            for (int i = 0; i < recibidos.Length; i += 8)
            {
                richTextBox1.Text += recibidos.Substring(i, 8) + " ";
            }
#50
Buenas mi muy distinguido amigo:

El problema que el código y variable recibidos me viene en string.

Lo he intentado poner en Byte y me da más problemas por todas partes. El código es enorme.

Dejo un ejemplo para que lo veas y te hagas una gran idea.


Código (csharp) [Seleccionar]
using System;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace Ejemplo
{
    public partial class Form1 : Form
    {
        // Utilizaremos un string como buffer de recepción.
        string recibidos;

        public Form1()
        {
            InitializeComponent();
        }


        private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                // Codificación.
                //serialPort1.Encoding = Encoding.GetEncoding(437);
                //serialPort1.Encoding = Encoding.GetEncoding(28591); // 28591 es lo mismo que ISO-8859-1.
                serialPort1.Encoding = Encoding.GetEncoding("ISO-8859-1");
               
                // Añado los puertos disponible en el PC con SerialPort.GetPortNames() al comboBox_Puerto.
                comboBox_Puerto.DataSource = SerialPort.GetPortNames();

                // Añade puertos disponibles físicos  y virtuales.
                serialPort1.PortName = comboBox_Puerto.Text.ToString();

                // Añadir datos recibidos en el evento.
                serialPort1.DataReceived += new SerialDataReceivedEventHandler(serialPort1_DataReceived);
            }

            catch (Exception error)
            {
                MessageBox.Show(error.Message, "Aviso:",
                    MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        // Detecta USB o puerto serie virtual cuando lo conecta y desconecta del cable.
        protected override void WndProc(ref Message USB)
        {
            if (USB.Msg == 0x219)
            {
                comboBox_Puerto.DataSource = SerialPort.GetPortNames();
            }

            // Detecta si hay cambios en el usb y si los hay los refleja.
            base.WndProc(ref USB);
        }

        private void button_Conectar_Click(object sender, EventArgs e)
        {
            try
            {
                serialPort1.PortName = comboBox_Puerto.Text.ToString(); // Puerto seleccionado previamente.
                serialPort1.BaudRate = Convert.ToInt32(comboBox_Baudios.Text); // Baudios.
                serialPort1.Open(); // Abrir puerto.
                comboBox_Puerto.Enabled = false;
                comboBox_Baudios.Enabled = false;
                button_Conectar.Enabled = false;
                button_Desconectar.Enabled = true;
                groupBox_Control_Zumbador.Enabled = true;
            }
            catch (Exception error)
            {
                MessageBox.Show(error.Message, "Aviso:",
                MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        private void button_Desconectar_Click(object sender, EventArgs e)
        {
            try
            {
                serialPort1.Close(); // Cerrar puerto.
                comboBox_Puerto.Enabled = true;
                comboBox_Baudios.Enabled = true;
                button_Conectar.Enabled = true;
                button_Desconectar.Enabled = false;
                groupBox_Control_Zumbador.Enabled = false;
            }

            catch (Exception error)
            {
                MessageBox.Show(error.Message, "Aviso:",
                MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        // Al cerrar el formulario, cierra el puerto si está abierto.
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            try
            {
                serialPort1.Close(); // Cerrar puerto.
            }

            catch (Exception error)
            {
                MessageBox.Show(error.Message, "Aviso:",
                MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
        }

        // Al recibir datos.
        private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            // Acumula los caracteres recibidos a nuestro 'buffer' (string).
            recibidos += serialPort1.ReadExisting();

            // Invocar o llamar al proceso de tramas.
            Invoke(new EventHandler(Actualizar));
        }

        // Procesar los datos recibidos en el bufer y extraer tramas completas.
        private void Actualizar(object sender, EventArgs e)
        {

            // Asignar el valor de la trama al richTextBox.
            richTextBox1.Text += recibidos;

            // Pasar a hexadecimal.
            foreach (byte b in recibidos)
            {
                // x = minúscula, X = mayúscula.
                richTextBox1.Text += b.ToString("X2");
            }

            // Nueva línea.
            richTextBox1.Text += Environment.NewLine;

            // Pasar a binario.
            foreach (string leer in recibidos.Select(c => Convert.ToString(c, 2)))
            {
                richTextBox1.Text += leer.ToString();
            }

            // Nueva línea.
            richTextBox1.Text += Environment.NewLine;
            richTextBox1.Text += Environment.NewLine;

            // Selecciona la posición final para leer los mensajes entrantes.
            richTextBox1.SelectionStart = richTextBox1.Text.Length;

            // Mantiene el scroll en la entrada de cada mensaje.
            richTextBox1.ScrollToCaret();

            // Limpiar.
            recibidos = "";
        }

        private void button_Activar_Click(object sender, EventArgs e)
        {
            //byte[] mBuffer = Encoding.ASCII.GetBytes("K60:1\r"); // Comando K60:1 activar.
            byte[] mBuffer = Encoding.ASCII.GetBytes("X87\r"); // Comando X87 Flags.
            serialPort1.Write(mBuffer, 0, mBuffer.Length);
        }

        private void button_Desactivar_Click(object sender, EventArgs e)
        {
            byte[] mBuffer = Encoding.ASCII.GetBytes("K60:0\r"); // Comando K60:0 desactivar.
            serialPort1.Write(mBuffer, 0, mBuffer.Length);
        }

        private void button_Mute_temporal_Click(object sender, EventArgs e)
        {
            byte[] mBuffer = Encoding.ASCII.GetBytes("B\r"); // Comando K60:2 Mute temporal.
            serialPort1.Write(mBuffer, 0, mBuffer.Length);
        }

        private void button_Limpiar_Click(object sender, EventArgs e)
        {
            // Limpiar.
            richTextBox1.Clear();
        }
    }
}