Vistas de página en total

lunes, 16 de diciembre de 2013

EMULADOR 8051 (1/12) (INTERPRETE)

   Como parte fundamental de un emulador está el desensamblador.
    Ya tenemos resuelto el problema del Hardware, y conocemos como vamos a controlar las señales provenientes del microprocesador y la paca prototipo. Para ello he puesto los esquemas de las tarjetas y las funciones que darán el interface con las tarjetas.

    Ahora podemos ponernos al meollo de la cuestión. El entrono del depurador.
    Al menos debe de contener las siguientes características.
              - Debe de tener una ventana de datos de memoria, en la que se puedan modificar y visualizar estos.
              - Debe de ser capaz de mostrar el programa desensambaldo.
              - Debe mostrar los registros del microprocesador.
              - Debe de tener modo de ejecución continuo, paso a paso y puntos de ruptura.
              - Debe de se amigable, aunque lo estemos realizando en DOS y en Ensamblador.

    El hecho de usar ensamblador y no utilizar un entorno gráfico, fue debido en su momento a la necesidad de velocidad. Ahora, aunque los ordenadores son más rápidos, la velocidad nunca bien mal.
    ¿Por qué tanta necesidad de velocidad? Porque el traceo lo hace el ordenador directamente. Es el ordenador el que da los pulsos de reloj, para ir ejecutando las instrucciones, y además entre pulsos va a tomar datos de la placa a través de los buses. Por ello, si queremos tracear a una buena velocidad, necesitamos que el manejo del Hard sea lo más rápido posible. Necesitamos que la interpretación del código, el desensamblado también, y sobre todo necesitamos que la respuesta en pantalla sea instantanea.

    Es por ese motivo que la función Puts y Printf, que en su momento ya he puesto en el blog, no esté hecha mediante llamadas al DOS, sino mediante volcado directo a pantalla.

    Para  empezar el depurador, por lo tanto, voy  a comenzar por la rutina principal. La que desensambla.
    Para realizar el desensamblador de una forma muy rápida y con poco código, creo una tabla de desensamblado, en la cuál asignaremos un a serie de datos a cada código que encontremos en código máquina.
   
     Así mediante una tabla de indexado, llamada Tabla, accederemos a una tabla de interpretación de código. Cuando leamos un byte de código máquina, usaremos Tabla localizar el formato de la instrución que codifica.
   A través de ese puntero recorreremos la lista correspondiente de datos de la instrucción, que nos indicará:  

    1. Byte 0 =  tipo instrucción
      •  0  ----  inmediato            (1 byte)
      •  1  ----  texto + num 8 bits   (2 bytes)
      •  2  ----  texto + num 16 bits  (3 bytes)
      •  3  ----  texto + num 8bits + texto
      •  4  ----  texto + num 16 bits + texto
      •  5  ----  texto + num 8 bits relativo + texto + num 8 bits
      •  6  ----  texto + num 8 bits relativo
      •  7  ----  texto + num 8 bits relativo + texto
      •  8  ----  texto + num 8bits + texto + num 8 bits
      •  9  ----  texto + num 8 bits + texto + num 8 bits relativo
      • 10  ----  texto + num 11bits  (2 bytes)
      • 11  ----  texto + num 8 bits ref_bit
      • 12  ----  texto + num 8 bits ref_bit + texto
      • 13  ----  texto + num 8 bits ref_bit + texto + num 8 bits relativo
      • 0ffh  ----  nueva tabla INSTRUCCION COMPUESTA
    1. byte 1 = Ciclos de reloj de la instrución
    2. byte 2= numero de bytes de la instrucción
    3. byte 3 en adelante =  Nemónico  
La tabla de indexación Tabla, se enlaza a nivel de compilación con la lista de instruciiones mediante una macro que crea todas las etiquetas de forma automática.
     Estas macros son INSTRUC y TABLA_INST. La primera crea la etiqueta con los valores del contador de TABLA_INST y esta última lleva la cuenta del bucle, en este caso 100h, colocando delante un nemónico inicial "I", que es enviado en todas las llamadas posteriores a INSTRUC, para que lo coloque delante del número del contador.
     Esta función interpretará el código de insgtrucción indicando el formato que debe usarse en la lectura de los siguientes bytes. Para ello, interpreta el código de instrucción y devuelve un puntero a la cadena de interpretación. Esta cadena contiene la estructura de textos que han de colocarse, insertando entre medias los datos que correspondan.
      Por lo tanto esta función es subrutina de una de mayor nivel que desensamblará la instrucción completa.
        Este interprete es un proceso muy sencillo que he utilizado en otros lenguajes para desensamblar otros códigos máquina, resultando muy eficaz y sencillo de programar.
        Esta función no solo servirá para reconocer la instrucción al presentaral en pantalla o listarla. Sino mucho más importante será usada por las rutinas de ejecución para conocer el numero de pulsos de reloj de cada instrucción de ensamblador, y poder así ejecutar la instrucción por completo.

INTERPRETA.ASM
; Copyright (C) 2013  José Ángel Moneo Fernández

;    This program is free software: you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation, either version 3 of the License, or
;   (at your option) any later version.

;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.

;    You should have received a copy of the GNU General Public License
;    along with this program.  If not, see <http://www.gnu.org/licenses/


include main.mac
include stdio.mac
include window.mac
include 8051.mac
include 48iocent.mac

 public _interpreta

;definición de instrución en la tabla
INSTRUC MACRO ETI,X
IFDEF ETI&X
        DB X
        DW ETI&X
ENDIF
endm
;macro para definir tabla de instrucciones
TABLA_INST MACRO ETI
        LB=0
          REPT 100h
          INSTRUC ETI,%LB
          LB=LB+1
          ENDM
 ENDM

DIR_RESET EQU 0  
 
.model compact,pascal


.data




;CONTROL
;Lista de instrucciones

; estructura de la entrada en la tabla
     ;byte 0 =tipo instruciión
               ;0 inmediato            (1 byte)
               ;1 texto + num 8 bits   (2 bytes)
               ;2 texto + num 16 bits  (3 bytes)
               ;3 texto + num 8bits + texto
               ;4 texto + num 16 bits + texto
                ;5 texto + num 8 bits relativo + texto + num 8 bits
               ;6 texto + num 8 bits relativo
               ;7 texto + num 8 bits relativo + texto
               ;8 texto + num 8bits + texto + num 8 bits
               ;9 texto + num 8 bits + texto + num 8 bits relativo
               ;10 texto + num 11bits  (2 bytes)
               ;11 texto + num 8 bits ref_bit
               ;12 texto + num 8 bits ref_bit + texto
               ;13 texto + num 8 bits ref_bit + texto + num 8 bits relativo
               ;0ffh nueva tabla INSTRUCCION COMPUESTA
     ;byte 1 = Ciclos de reloj de la instrución
     ;byte 2= numero de bytes de la instruciión
     ;Resto hasta "0" Nemónico
   
I0 DB 0,12,1,"NOP",0
I1 DB 10,24,2,"AJMP ",0
I2 DB 2,24,3,"LJMP ",0
I3 DB 0,12,1,"RR A",0
I4 DB 0,12,1,"INC A",0
I5 DB 1,12,2,"INC ",0
I6 DB 0,12,1,"INC @R0",0
I7 DB 0,12,1,"INC @R1",0
I8 DB 0,12,1,"INC R0",0
I9 DB 0,12,1,"INC R1",0
I10 DB 0,12,1,"INC R2",0
I11 DB 0,12,1,"INC R3",0
I12 DB 0,12,1,"INC R4",0
I13 DB 0,12,1,"INC R5",0
I14 DB 0,12,1,"INC R6",0
I15 DB 0,12,1,"INC R7",0
I16 DB 13,24,3,"JBC ",0,",",0
I17 db 10,24,2,"ACALL ",0
I18 DB 2,24,3,"LCALL ",0
I19 DB 0,12,1,"RRC A",0
I20 DB 0,12,1,"DEC A",0
I21 DB 1,12,2,"DEC ",0
I22 DB 0,12,1,"DEC @R0",0
I23 DB 0,12,1,"DEC @R1",0
I24 DB 0,12,1,"DEC R0",0
I25 DB 0,12,1,"DEC R1",0
I26 DB 0,12,1,"DEC R2",0
I27 DB 0,12,1,"DEC R3",0
I28 DB 0,12,1,"DEC R4",0
I29 DB 0,12,1,"DEC R5",0
I30 DB 0,12,1,"DEC R6",0
I31 DB 0,12,1,"DEC R7",0
I32 DB 13,24,3,"JB ",0,",",0
I33 DB 10,24,2,"AJMP ",0
I34 DB 0,24,1,"RET",0
I35 DB 0,12,1,"RL A",0
I36 DB 1,12,2,"ADD A,#",0
I37 DB 1,12,2,"ADD A,",0
I38 DB 0,12,1,"ADD A,@R0",0
I39 DB 0,12,1,"ADD A,@R1",0
I40 DB 0,12,1,"ADD A,R0",0
I41 DB 0,12,1,"ADD A,R1",0
I42 DB 0,12,1,"ADD A,R2",0
I43 DB 0,12,1,"ADD A,R3",0
I44 DB 0,12,1,"ADD A,R4",0
I45 DB 0,12,1,"ADD A,R5",0
I46 DB 0,12,1,"ADD A,R6",0
I47 DB 0,12,1,"ADD A,R7",0
I48 DB 13,24,3,"JNB ",0,",",0
I49 db 10,24,2,"ACALL ",0
I50 DB 0,24,1,"RETI",0
I51 DB 0,12,1,"RLC A",0
I52 DB 1,12,2,"ADDC A,#",0
I53 DB 1,12,2,"ADDC A,",0
I54 DB 0,12,1,"ADDC A,@R0",0
I55 DB 0,12,1,"ADDC A,@R1",0
I56 DB 0,12,1,"ADDC A,R0",0
I57 DB 0,12,1,"ADDC A,R1",0
I58 DB 0,12,1,"ADDC A,R2",0
I59 DB 0,12,1,"ADDC A,R3",0
I60 DB 0,12,1,"ADDC A,R4",0
I61 DB 0,12,1,"ADDC A,R5",0
I62 DB 0,12,1,"ADDC A,R6",0
I63 DB 0,12,1,"ADDC A,R7",0
I64 DB 13,24,3,"JC ",0,",",0
I65 db 10,24,2,"AJMP ",0
I66 DB 3,12,2,"ORL ",0,",A",0
I67 DB 8,24,3,"ORL ",0,",#",0
I68 DB 1,12,2,"ORL A,#",0
I69 DB 1,12,2,"ORL A,",0
I70 DB 0,12,1,"ORL A,@R0",0
I71 DB 0,12,1,"ORL A,@R1",0
I72 DB 0,12,1,"ORL A,R0",0
I73 DB 0,12,1,"ORL A,R1",0
I74 DB 0,12,1,"ORL A,R2",0
I75 DB 0,12,1,"ORL A,R3",0
I76 DB 0,12,1,"ORL A,R4",0
I77 DB 0,12,1,"ORL A,R5",0
I78 DB 0,12,1,"ORL A,R6",0
I79 DB 0,12,1,"ORL A,R7",0
I80 DB 13,24,3,"JNC ",0,",",0
I81 db 10,24,2,"ACALL ",0
I82 DB 3,12,2,"ANL ",0,",A",0
I83 DB 8,24,3,"ANL ",0,",#",0
I84 DB 1,12,2,"ANL A,#",0
I85 DB 1,12,2,"ANL A,",0
I86 DB 0,12,1,"ANL A,@R0",0
I87 DB 0,12,1,"ANL A,@R1",0
I88 DB 0,12,1,"ANL A,R0",0
I89 DB 0,12,1,"ANL A,R1",0
I90 DB 0,12,1,"ANL A,R2",0
I91 DB 0,12,1,"ANL A,R3",0
I92 DB 0,12,1,"ANL A,R4",0
I93 DB 0,12,1,"ANL A,R5",0
I94 DB 0,12,1,"ANL A,R6",0
I95 DB 0,12,1,"ANL A,R7",0
I96 DB 13,24,3,"JZ ",0,",",0
I97 db 10,24,2,"AJMP ",0
I98 DB 3,12,2,"XRL ",0,",A",0
I99 DB 8,24,3,"XRL ",0,",#",0
I100 DB 1,12,2,"XRL A,#",0
I101 DB 1,12,2,"XRL A,",0
I102 DB 0,12,1,"XRL A,@R0",0
I103 DB 0,12,1,"XRL A,@R1",0
I104 DB 0,12,1,"XRL A,R0",0
I105 DB 0,12,1,"XRL A,R1",0
I106 DB 0,12,1,"XRL A,R2",0
I107 DB 0,12,1,"XRL A,R3",0
I108 DB 0,12,1,"XRL A,R4",0
I109 DB 0,12,1,"XRL A,R5",0
I110 DB 0,12,1,"XRL A,R6",0
I111 DB 0,12,1,"XRL A,R7",0
I112 DB 13,24,3,"JNZ ",0,",",0
I113 db 10,24,2,"ACALL ",0
I114 DB 11,24,2,"ORL C,",0
I115 DB 0,24,1,"JMP @A+DPTR",0
I116 DB 1,12,2,"MOV A,#",0
I117 DB 8,24,3,"MOV ",0,",#",0
I118 DB 1,12,2,"MOV @R0,#",0
I119 DB 1,12,2,"MOV @R1,#",0
I120 DB 1,12,2,"MOV R0,#",0
I121 DB 1,12,2,"MOV R1,#",0
I122 DB 1,12,2,"MOV R2,#",0
I123 DB 1,12,2,"MOV R3,#",0
I124 DB 1,12,2,"MOV R4,#",0
I125 DB 1,12,2,"MOV R5,#",0
I126 DB 1,12,2,"MOV R6,#",0
I127 DB 1,12,2,"MOV R7,#",0
I128 DB 6,24,2,"SJMP ",0
I129 db 10,24,2,"AJMP ",0
I130 DB 11,24,2,"ANL C,",0
I131 DB 0,24,1,"MOVC A,@A+PC",0
I132 DB 0,48,1,"DIV AB",0
I133 DB 8,24,3,"MOV ",0,",",0
I134 DB 3,24,3,"MOV ",0,",@R0",0
I135 DB 3,24,3,"MOV ",0,",@R1",0
I136 DB 3,24,3,"MOV ",0,",R0",0
I137 DB 3,24,3,"MOV ",0,",R1",0
I138 DB 3,24,3,"MOV ",0,",R2",0
I139 DB 3,24,3,"MOV ",0,",R3",0
I140 DB 3,24,3,"MOV ",0,",R4",0
I141 DB 3,24,3,"MOV ",0,",R5",0
I142 DB 3,24,3,"MOV ",0,",R6",0
I143 DB 3,24,3,"MOV ",0,",R7",0
I144 DB 2,24,3,"MOV DPTR,#",0
I145 db 10,24,0,"ACALL ",0
I146 DB 12,24,0,"MOV ",0,",C",0
I147 DB 0,24,0,"MOVC A,@A+PC",0
I148 DB 1,12,0,"SUBB A,#",0
I149 DB 1,12,0,"SUBB A,",0
I150 DB 0,12,0,"SUBB A,@R0",0
I151 DB 0,12,0,"SUBB A,@R1",0
I152 DB 0,12,0,"SUBB A,R0",0
I153 DB 0,12,0,"SUBB A,R1",0
I154 DB 0,12,0,"SUBB A,R2",0
I155 DB 0,12,0,"SUBB A,R3",0
I156 DB 0,12,0,"SUBB A,R4",0
I157 DB 0,12,0,"SUBB A,R5",0
I158 DB 0,12,0,"SUBB A,R6",0
I159 DB 0,12,0,"SUBB A,R7",0
I160 DB 11,24,0,"ORL C,/",0
I161 db 10,24,0,"AJMP ",0
I162 DB 11,24,0,"MOV C,",0
I163 DB 0,24,0,"INC DPTR",0
I164 DB 0,48,0,"MUL AB",0
I165 DB 0,12,0,"????",0
I166 DB 1,12,0,"MOV @R0,",0
I167 DB 1,12,0,"MOV @R1,",0
I168 DB 1,12,0,"MOV R0,",0
I169 DB 1,12,0,"MOV R1,",0
I170 DB 1,12,0,"MOV R2,",0
I171 DB 1,12,0,"MOV R3,",0
I172 DB 1,12,0,"MOV R4,",0
I173 DB 1,12,0,"MOV R5,",0
I174 DB 1,12,0,"MOV R6,",0
I175 DB 1,12,0,"MOV R7,",0
I176 DB 11,24,0,"ANL C,/",0
I177 db 10,24,0,"ACALL ",0
I178 DB 11,12,0,"CPL ",0
I179 DB 0,12,0,"CPL C",0
I180 DB 9,24,0,"CJNE A,#",0,",",0
I181 DB 9,24,0,"CJNE A,",0,",",0
I182 DB 9,24,0,"CJNE @R0,#",0,",",0
I183 DB 9,24,0,"CJNE @R1,#",0,",",0
I184 DB 9,24,0,"CJNE R0,#",0,",",0
I185 DB 9,24,0,"CJNE R1,#",0,",",0
I186 DB 9,24,0,"CJNE R2,#",0,",",0
I187 DB 9,24,0,"CJNE R3,#",0,",",0
I188 DB 9,24,0,"CJNE R4,#",0,",",0
I189 DB 9,24,0,"CJNE R5,#",0,",",0
I190 DB 9,24,0,"CJNE R6,#",0,",",0
I191 DB 9,24,0,"CJNE R7,#",0,",",0
I192 DB 1,24,0,"PUSH ",0
I193 db 10,24,0,"AJMP ",0
I194 DB 11,12,0,"CLR ",0
I195 DB 0,12,0,"CLR C",0
I196 DB 0,12,0,"SWAP A",0
I197 DB 1,12,0,"XCH A,",0
I198 DB 0,12,0,"XCH A,@R0",0
I199 DB 0,12,0,"XCH A,@R1",0
I200 DB 0,12,0,"XCH A,R0",0
I201 DB 0,12,0,"XCH A,R1",0
I202 DB 0,12,0,"XCH A,R2",0
I203 DB 0,12,0,"XCH A,R3",0
I204 DB 0,12,0,"XCH A,R4",0
I205 DB 0,12,0,"XCH A,R5",0
I206 DB 0,12,0,"XCH A,R6",0
I207 DB 0,12,0,"XCH A,R7",0
I208 DB 1,24,0,"POP ",0
I209 db 10,24,0,"ACALL ",0
I210 DB 11,12,0,"SETB ",0
I211 DB 0,12,0,"SETB C",0
I212 DB 0,12,0,"DA A",0
I213 DB 9,24,0,"DJNZ ",0,",",0
I214 DB 0,12,0,"XCHD A,@R0",0
I215 DB 0,12,0,"XCHD A,@R1",0
I216 DB 6,24,0,"DJNE R0,",0,",",0
I217 DB 6,24,0,"DJNE R1,",0,",",0
I218 DB 6,24,0,"DJNE R2,",0,",",0
I219 DB 6,24,0,"DJNE R3,",0,",",0
I220 DB 6,24,0,"DJNE R4,",0,",",0
I221 DB 6,24,0,"DJNE R5,",0,",",0
I222 DB 6,24,0,"DJNE R6,",0,",",0
I223 DB 6,24,0,"DJNE R7,",0,",",0
I224 DB 0,24,0,"MOVX A,@DPTR",0
I225 db 10,24,0,"AJMP ",0
I226 DB 0,24,0,"MOVX A,@R0",0
I227 DB 0,24,0,"MOVX A,@R1",0
I228 DB 0,12,0,"CLR A",0
I229 DB 1,12,0,"MOV A,",0
I230 DB 0,12,0,"MOV A,@R0",0
I231 DB 0,12,0,"MOV A,@R1",0
I232 DB 0,12,0,"MOV A,R0",0
I233 DB 0,12,0,"MOV A,R1",0
I234 DB 0,12,0,"MOV A,R2",0
I235 DB 0,12,0,"MOV A,R3",0
I236 DB 0,12,0,"MOV A,R4",0
I237 DB 0,12,0,"MOV A,R5",0
I238 DB 0,12,0,"MOV A,R6",0
I239 DB 0,12,0,"MOV A,R7",0
I240 DB 0,24,0,"MOV @DPTR,A",0
I241 db 10,24,0,"ACALL ",0
I242 DB 0,24,0,"MOVX @R0,A",0
I243 DB 0,24,0,"MOVX @R1,A",0
I244 DB 0,12,0,"CPL A",0
I245 DB 3,12,0,"MOV ",0,",A",0
I246 DB 0,12,0,"MOV @R0,A",0
I247 DB 0,12,0,"MOV @R1,A",0
I248 DB 0,12,0,"MOV R0,A",0
I249 DB 0,12,0,"MOV R1,A",0
I250 DB 0,12,0,"MOV R2,A",0
I251 DB 0,12,0,"MOV R3,A",0
I252 DB 0,12,0,"MOV R4,A",0
I253 DB 0,12,0,"MOV R5,A",0
I254 DB 0,12,0,"MOV R6,A",0
I255 DB 0,12,0,"MOV R7,A",0


Tabla LABEL BYTE
   Tabla_inst I        ;tabla a lista de instrucciones
   db 0ffh              ;localizador final tabla

no_conocido db 0,0,0,"????",0      ; codigo no desensamblable
nada db 0

   
.code
           
_interpreta proc uses si bx
           ;se indicará en DX direccion de la
           ;instruccion a decodificar y devolverá
           ;en ax la direccion de la lista de interpretado
        mov si,offset Tabla

busca_mas:
        leeinst
        inc dx
 busca:
        cmp al,[si]
        jz localizado
        jb n_conoc
        cmp byte ptr [si],0ffh
        jz n_conoc
        add si,3
        jmp busca
n_conoc:
        mov bx,offset no_conocido
        jmp fin

localizado:
        mov bx,[si+1]           ;se lee la direccion de la instruccion
        mov al,[bx]             ;de la secuencia se lee el indicador
        cmp al,0ffh             ;indicador de instruccion compuesta
        jne fin
        mov si,[BX+1]
        jmp busca_mas
 fin:
        mov ax,bx
        ret
_interpreta endp

end

jueves, 12 de diciembre de 2013

LIBRERÍA EMULADOR 8051

   Librería de funciones básicas de control de la targeta  8051 del emulador.



EMU8051.ASM


; Copyright (C) 2013  José Ángel Moneo Fernández

;    This program is free software: you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation, either version 3 of the License, or
;   (at your option) any later version.

;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.

;    You should have received a copy of the GNU General Public License
;    along with this program.  If not, see <http://www.gnu.org/licenses/

include io.mac
include 48iocent.mac
include 8051.mac

PASO EQU 1

        public _Dma_on
        public _Dma_off
        public _lee
        public _reset
        public _trace

        public _Guarda
        public _Toma  
       
        extrn des_base:word
        extrn Dir_cur:word
        extrn CONTROL:word
   
.model compact,pascal


.data
dir dw 0
paro db "Microprocesador Parado",0
.code

_Dma_on proc near
        pioconfig p0,PSALIDA
        ret
_Dma_on endp

_Dma_off proc near
        pioconfig P1,PA PB PCL PCH
        pioconfig P0,PSALIDA
        outpio p0,BUSRQPC_ON
        ret
_Dma_off endp


;lectura zona programa
_leeinst proc uses bx cx dx
        Dma_on
        pioconfig P1,PCL
        outpio P1,WR_ON
        outpio P1,RD_ON
        outpio P1,ALE_ON
        outpio P1,PSEN_ON
        outpio PA1,dl
        pioconfig P1,PA PCL
        outpio PB1,dh
        outpio P1,WR_ON
        outpio P1,RD_ON
        outpio P1,PSEN_OFF   ;LA LECTURA SE HACE CON RD=1 Y PSEN=0 POR SER PROGRAMA
        inpio al,PA1
        push ax
        outpio P1,RD_ON
        Dma_off
        pop ax

        ret
_leeinst endp

;lectura un dato de  area de datos
;dir =dirección de lectura en ram del prototipo
;devuelve el dato en al
_lee proc near uses bx cx dx,dir
        mov dx,dir
        Dma_on
        pioconfig P1,PCL
        outpio P1,WR_ON
        outpio P1,RD_ON   ;PSEN=1 POR SER LECTURA DE DATOS
        outpio P1,ALE_ON
        outpio P1,PSEN_ON
        outpio PA1,dl
        pioconfig P1,PA PCL
        outpio PB1,dh
        outpio P1,WR_ON
        outpio P1,PSEN_ON
        inpio al,PA1
        push ax
        outpio P1,RD_ON
        Dma_off
        pop ax
        ret
_lee endp


;Guarda de zona de datos
; dir =dirección de volcado en ram del prototipo
; dirección =puntero a los datos a volcar
; long =cantidad de datos
_Guarda proc uses bx dx,dir,dato,long
        mov dx,dir
        mov bx,dato
        mov cx,long
        Dma_on
        pioconfig P1,PCL
        outpio P1,WR_ON
        outpio P1,RD_ON   ;PSEN=1 POR SER LECTURA DE DATOS
        outpio P1,PSEN_ON
Guard:
        outpio P1,ALE_ON
        outpio PA1,dl
        outpio PB1,dh
        outpio P1,ALE_OFF
        outpio PA1,[bx]
        outpio P1,WR_OFF
        outpio P1,WR_ON
        inc bx
        inc dx
        loop guard
        Dma_off
        ret
_guarda endp

;toma una zona de datos
 ;lee una zona de memoria
; dir =dirección de volcado en ram del prototipo
; dirección =puntero a los datos a volcar
; long =cantidad de datos
_Toma proc uses bx dx,dir,datos,long
        mov dx,dir
        mov bx,dato
        mov cx,long
        Dma_on
Tom:
        lee dx,bx,cx
        mov [bx],al
        inc bx
        inc dx
        loop Tom
        Dma_off
        ret
_Toma endp

;ejecuta una instruciión
;en pulsos se pasará el numero de pulsos de reloj de la instrucción

_trace proc uses cx dx, pulsos
        pioconfig P1,PCL
        outpio P1,WR_ON
        outpio P1,RD_ON   ;PSEN=1 POR SER LECTURA DE DATOS
        outpio P1,PSEN_ON
        outpio P1,ALE_ON
        mov dx,Dir_cur
        outpio PA1,dl
        outpio P1,ALE_OFF
        Dma_off
        mov cx,pulsos
        dec cx
buc:
        outpio P0,CLK_ON
        outpio P0,CLK_OFF
        loop buc
busca_ale:
        inpio dl,PC1
        test dl,ALE
        jnz falta
        test dl,PSEN
        jnz final
falta:
        inpio dl,PA1
        inpio dh,PB1
        mov Dir,dx
        outpio P0,CLK_ON
        outpio P0,CLK_OFF
        jmp short busca_ale
final:  mov dx,dir
        mov Dir_cur,dx
        and CONTROL, not PASO
        ret
_trace endp

  ;resetea el programa
_reset proc uses cx
        mov Dir_cur,0
        pioconfig P0,PA PB
        outpio P0,EXTERNO_OFF
        outpio p0,BUSRQPC_ON
        pioconfig P1,PA PB PCH
        outpio P1,RESET_ON
        outpio P1,INT0_ON
        outpio P1,INT1_ON
        mov cx,12
bucr:
        outpio P0,CLK_ON
        outpio P0,CLK_OFF
        loop bucr
        outpio P1,RESET_OFF
        mov cx,12
        call _trace
        pioconfig P1,PA PB PCH PCL
        mov des_base,0
        ret
_reset endp

     end

domingo, 8 de diciembre de 2013

Concatenar un número indeterminado de cadenas

Como respuesta a una pregunta de un lector del blog, voy a añadir esta entrada.

   Si no me equivoco al interpretar su pregunta, el problema que se quiere es poder concatenar dos o más cadenas en una destino.
   Para hacerlo de forma elegante, voy a crear una macro.
    Esta macro va a concatenar las cadenas mediante llamadas consecutivas a la función strcat.
    La ventaja de la macro es que podemos añadir la llamada adicional solo si se pasa como parámetro el puntero a una nueva cadena, de forma que podemos dejar definida la función para un número indeterminado de cadenas. En este caso lo haré de 2 hasta 7.

   Para ello vamos a usar la función strcat para unir las dos primeras cadenas a la tercera. Luego, cuando exista el siguiente parámetro, tomaremos la dirección del destino, calcularemos su longitud y la sumaremos a su dirección de puntero. Así crearemos un puntero al final de la cadena destino. Esto lo haré en SI, para poder direccionarlo como [si] y pasarlo como parámetro a la macro strcpy. Así con esa nueva dirección puedo llamar de nuevo a strcpy y acoplar la cadena a la que ya existía.
    También lo podía haber hecho mediante sucesivas llamadas a strcat destino,destino,fuente?, pero de esta segunda forma perdería tiempo en copiar muchas veces la misma cadena.

    Si llamo a la macro
            Add_str destino,fuente1,fuente2  ;copia dos cadenas en destino
            Add_str destino,fuente1,fuente2,fuente3   ;copia tres cadenas en destino
        etc

    Add_Str macro destino,fuente1,fuente2,fuente3,fuente4,fuente5,fuente6,fuente7
               strcat destino,fuente1,fuente2      ;primera llamada con el mínimo permitido
            push si
           ifnb <fuente3>
               strlen destino
               lea si,destino
               add si,ax
               strcpy [si],fuente3      ;primera llamada
           else
                 ifnb <fuente4>
                     strlen destino
                      lea si,destino
                      add si,ax
                      strcpy [si],fuente4      ;segunda llamada
                  else
                      ifnb <fuente5>
                           strlen destino
                             lea si,destino
                             add si,ax
                            strcpy [si],fuente5      ;tercera llamada
                      else
                            ifnb <fuente6>
                                strlen destino
                                 lea si,destino
                                add si,ax
                                strcpy [si],fuente6      ;cuarta llamada
                             else
                                 ifnb <fuente7>
                                     strlen destino
                                     lea si,destino
                                    add si,ax
                                    strcpy [si],fuente7      ;quinta llamada
                                 endif
                            endif
                     enidf
              endif
         endif
         pop si
endm

  Realmente lo he creado como una nueva función, pero podría haber ampliado simplemente las que ya tenía publicadas en la cabecera string.mac.
   Por ejemplo, podríamos haberla llamado strcat directamente, pues en su modo más corto llama directamente a esa función. 

    Hay que tener en cuenta que el enlace está enviado a una versión inicial de las macros. En ella no usábamos el comando USE del TASM 2.0.
   Para pasar al modo de programación actual, en el que si usamos ya el comando USE del TASM2.0 habrá que aplicar la conversión indicada en la entrada del blog "pasamos a integrar TASM2.0"
   Con esta salvedad, colocaré ahora una versión actualizada de strcat y otra de strcpy, que podrán hacer la misma función que la anterior.

    Si en vez de crear una función nueva ampliamos strcat para que tenga polimorfismo, quedará:

strcat macro destino,fuente1,fuente2,fuente3,fuente4,fuente5,fuente6,fuente7
 ifndef _strcat
      extrn _strcat:near
 endif
        lea ax,destino
        push ax
       lea ax,fuente1
        push ax
        lea ax,fuente2
        push ax
        call _strcat                ;primera llamada. Sumamos fuente1, y fuente2 en destino
  
; a partir de ahí todo es igual    
        push si
           ifnb <fuente3>
               strlen destino
               lea si,destino
               add si,ax
               strcpy [si],fuente3      ;primera llamada
           else
                 ifnb <fuente4>
                     strlen destino
                      lea si,destino
                      add si,ax
                      strcpy [si],fuente4      ;segunda llamada
                  else
                      ifnb <fuente5>
                           strlen destino
                             lea si,destino
                             add si,ax
                            strcpy [si],fuente5      ;tercera llamada
                      else
                            ifnb <fuente6>
                                strlen destino
                                 lea si,destino
                                add si,ax
                                strcpy [si],fuente6      ;cuarta llamada
                             else
                                 ifnb <fuente7>
                                     strlen destino
                                     lea si,destino
                                    add si,ax
                                    strcpy [si],fuente7      ;quinta llamada
                                 endif
                            endif
                     enidf
              endif
         endif
         pop si 

  endm

    El ahorro es simplemente tener una sola función que vale para todo, ya que no tiene sentido strcat si Add_Str hace lo mismo.
 
     Pero claro, igualmente podríamos pensar con strcpy. Dado que strcat no es mas que dos llamadas a strcpy, podríamos construir strcat mediante strcpy, y si hacemos polimorfismo, nos bastará con strcpy, para realizar todas las posibilidades.

      ¿Que Sacrificamos?
      Sacrificamos código. Si usamos macro, cada llamada consumirá más código, que si usamos la llamada a la función strcat. Es por eso que cree strcat. Sin embargo, Add_Str si interesa hacerlo en macro, ya que concatenar  más de una cadena a la vez es más raro, y tener una función que no se usa, también puede aumentar el código, al incorporar librerías innecesarias.

    De todas formas vamos a ver como crear un strcpy,que sirva para todo.

strcpy macro destino,fuente1,fuente2,fuente3,fuente4,fuente5,fuente6,fuente7
ifndef _strcpy
    extrn _strcpy:near
endif
        lea ax,destino
        push ax
        lea ax,fuente1    ;copia inicial
        push ax
        call _strcpy
; a partir de ahí todo es igual , pero con una llamada más
        push si
           ifnb <fuente2>
               strlen destino
               lea si,destino
               add si,ax
               strcpy [si],fuente2      ;primera llamada
           else
                 ifnb <fuente3>
                     strlen destino
                      lea si,destino
                      add si,ax
                      strcpy [si],fuente3     ;segunda llamada
                  else
                      ifnb <fuente4>
                           strlen destino
                             lea si,destino
                             add si,ax
                            strcpy [si],fuente4      ;tercera llamada
                      else
                            ifnb <fuente5>
                                strlen destino
                                 lea si,destino
                                add si,ax
                                strcpy [si],fuente5      ;cuarta llamada
                             else
                                 ifnb <fuente6>
                                     strlen destino
                                     lea si,destino
                                    add si,ax
                                    strcpy [si],fuente6       ;quinta llamada
                                 else
                                    ifnb <fuente7>
                                         strlen destino
                                         lea si,destino
                                         add si,ax
                                         strcpy [si],fuente7       ;quinta llamada

                                    endif
                                 endif
                            endif
                     enidf
              endif
         endif
         pop si 
 endm

    Como se ve, hará lo mismo, y podrá usarse tanto para copiar una cadena, como para acoplar de 2 a 7 cadenas.
    Pero claro, para sumar dos cadenas perderemos más espacio si lo usamos varias veces, frente a usar strcat.

CABECERA EMULADOR 8051

    Vamos a empezar con el programa de control para el emulador 8051, definiendo la cabera para sus funciones básicas de control.
      En este caso lo primero será definir las señales de control y las funciones básicas de lectura, escritura y manejo de la memoria a través del DMA. Así mismo añado en esta cabecera la función Trace para controlar el reloj y ejecutar una instrucción.
   
EMU5051.MAC
; Copyright (C) 2013  José Ángel Moneo Fernández

;    This program is free software: you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation, either version 3 of the License, or
;   (at your option) any later version.

;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.

;    You should have received a copy of the GNU General Public License
;    along with this program.  If not, see <http://www.gnu.org/licenses/

;identificacion señales
;PC1 -  PUERTO "C" ALTO DE LA TARJETA 48IOCENT
INT0 EQU 1    ;SEÑAL DE INTERRUPCIÓN 1
INT1 EQU 2    ;SEÑAL DE INTERRUPCIÓN 2
RESET EQU 4   ; RESET
WR EQU 20h     ; SEÑAL DE ESCRITURA
RD EQU 10h    ;SEÑAL DE LECTURA
PSEN EQU 40h    ; SEÑAL PSEN
ALE EQU 80h     ;SEÑAL Ale


;salidas control 
;PC0 . PUERTO "C" BAJO DEL LA TARJETA 48IOCENT
CLK_ON EQU 11B    ; ACTIVAR SEÑAL DE RELOJ 
CLK_OFF EQU 10B    ; DESACTIVAR SEÑAL DE RELOJ
BUSRQPC_ON EQU 01B  ; ACTIVAR SOLICITUD DE BUS
BUSRQPC_OFF EQU 00B  ;DESCTIVAR SEÑAL DE RELOJ
EXTERNO_ON EQU 101B   ;ACTIVAR BUS EXTERNO
EXTERNO_OFF EQU 100B   ;DESCTIVAR BUS EXTERNO

;control PC1
PSEN_ON EQU 1101B    ;ACTIVAR PSEN
PSEN_OFF EQU 1100B   ;DESACTIVAR PSEN
ALE_ON EQU 1111B      ;ACTIVAR ALE
ALE_OFF EQU 1110B    ;DESACTIVAR ALE


WR_ON EQU 1011B    ;ACTIVAR WR
WR_OFF EQU 1010B ; DESCTIVAR WR
RD_ON EQU 1001B  ; ACTIVAR RD
RD_OFF EQU 1000B   ;DESACTIVAR RD
RESET_ON EQU 101B   ; ACTIVA RESET
RESET_OFF EQU 100B   ;DESACTIVAR OFF
INT1_ON EQU 11B   ; ACTIVAR INT1
INT1_OFF EQU 10B   ; DESACTIVAR INT1
INT0_ON EQU 1    ; ACTIVAR INT0
INT0_OFF EQU 0   ; DESACTIVAR INT0


;MACROS de control de tarjeta
;activa DMA
Dma_on macro
ifndef _Dma_on
     extrn _Dma_on:near
endif
    call _dma_on   
endm

;desactiva DMA
Dma_Off macro
ifndef _Dma_off
     extrn _Dma_off:near
endif
    call _dma_off   
endm

;Lee de zona de programa
LeeInst macro 
ifndef _LeeInst
     extrn _LeeInst:near
endif
    call _LeeInst
endm

;Lee de zona de datos
;lectura un dato de  area de datos
;dir =dirección de lectura en ram del prototipo
;devuelve el dato en al
Lee macro dir
ifndef _Lee
     extrn _Lee:near
endif
    lea ax,dir
    push ax
    call _Lee
endm

;Guarda de zona de datos
; dir =dirección de volcado en ram del prototipo
; dirección =puntero a los datos a volcar
; long =cantidad de datos
Guarda macro dir,datos,long
ifndef _Guarda
     extrn _Guarda:near
endif
    mov ax,dir
    push ax
    lea ax, datos
    push ax
    mov ax,long
    push ax
    call _Guarda
endm

;lee una zona de memoria
; dir =dirección de volcado en ram del prototipo
; dirección =puntero a los datos a volcar
; long =cantidad de datos
Toma macro dir
ifndef _Toma  dir,datos,long
     extrn _Toma:near
endif
    mov ax,dir
    push ax
    lea ax, dato
    push ax
        mov ax,long
    push ax
    call _Toma 
endm


;tracea una instrución. 
;En el parámetro se le pasa el número de pulsos  de la instruciión
Trace macro pulsos
ifndef _Trace
     extrn _Trace:near
endif
    mov ax,pulsos 
    pusxh ax
    call _Trace  
endm


jueves, 5 de diciembre de 2013

EMULADOR 8051 (HARDWARE)

   Propósito

 Para poder seguir ampliando el blog y subiendo el nivel de los programas, siempre basándome solamente en código propio, hoy voy a colocar una tarjeta muy sencillas, que unida a la anterior tarjeta de 48IO, me permitió en su día crear una de mis mejores herramientas de desarrollo.
    En este caso el resultado del conjunto es un emulador de 8051. 
 
   Esta tarjeta es una versión para el 8051 de un emulador Z80 previo que desarrolle durante las vacaciones de navidad de 1993. Por ello este año cumple 20 años. Y por eso, como si se tratase de una patente, lo hago de  dominio público.


    Este emulador permitirá insertar un pincho en el lugar del microprocesador y tracear, ejecutar, y ver la ejecución de un programa sobre un hardware que desarrollemos con 8051.

 Historia

    Esto ahora está muy extendido, pero en 1993, que es cuando lo desarrollé era una herramienta muy cara.
   Este al igual que otro que podré para Z80, se puede realizar por 30€, y el coste entonces de uno equivalente estaba en 6000€. Por lo que valía la pena hacerlo.

   Me lo intentaron copiar, por supuesto, pero no llegue a comercializarlos, por estar metido en otra guerra. Ahora servirá simplemente para mostrar código ASM. No solo el hard es sencillo, sino además el software de emulación está enteramente en ensamblador. 
   Esto me permite ahora colocar todo el código del depurador, el cual incorpora un  desensamblador, ventanas de Dump, Gestión de la tarjeta 48iocent, Traceo, control DMA etc.

 Hardware

  La tarjeta es esta:


EMULADOR 8051

  



     Como se puede ver la tarjeta no puede ser mas simple. 
      Básicamente esta tarjeta es un medio de conseguir acceder a la tarjeta a probar y al micro de forma separada. 
     Mediante una serie de elementos, vamos a poder aislar el microprocesador de la placa, para poder leer y manejar el Hard de la placa a probar desde el PC. 
     Igualmente estos elementos de la placa nos permitirán tener conectado al microprocesador 8051 a la placa de prueba cuando lo deseemos, pudiendo poner en marcha el sistema.
       En ningún momento ejecutaremos instrucciones, ni secuencias desde el PC. Esto no maneja la placa de prueba emulando al 8051. Lo que realmente hace es conectar a este con la placa cuando lo necesitemos. De esta forma, la ejecución es siempre real. Pudiendose poner el sistema en marcha a la velocidad de reloj real.
       Para conseguir mayor velocidad en el traceo, realicé el soft en ensamblador.

Principio de Funcionamiento

       El principio del sistema es DMA. Generamos DMA, para acceder al programa interno de la placa a probar, para conocer su estado de memoria y sus datos, y para leer y escribir el programa o los datos. 
       En este caso, como el 8051 no tiene gestión de DMA, hemos colocado unos buffer que realizarán esta función. Los integrados U2,U3 y U4 tienen la misión de aislar el 8051 del bus de la tarjeta. De esta forma podremos tomar el control de la tarjeta desde el PC cuando lo necesitemos. Esto nos genera un control DMA, básico para poder tracear.
     Esta tarjeta emulador será transparente 100% para el programa. No consume interrupciones, no consume memoria, no consume, puertos. Podrá ser enchufado a cualquier placa que se desarrolle o haya sido ya montada. No necesita tener el programa en el PC, pues el emulador toma el programa de la propia tarjeta. Por ello se puede tracear una tarjeta desconocida, con solo soltar el micro y conecta en su lugar el zócalo de este emulador.
        

Funcionamiento

      El conector JP1 es el conector en el que están, en orden, los 6 puertos de la tarjeta 48iocent. En este caso mediante un conector con cable plano de 60 pines, se conecta a esta tarjeta, y por el otro lado el cable se divide para ir conectado 6 conectores de 10 pines para cable plano. Uno a cada puerto,de la tarjeta 48IOCEN.
       El conector JP2 es en cambio un conector de 40 pines para cable plano de formato DIP40, el cual será usado para enchufar en el zócalo en que irá luego el microprocesador.

       Dado que esto se usará con una placa real, el reloj de ejecución debería de poder ser el el real. Por ello el U7, se encargará de generar un reloj mediante el cristal que se encuentre en la placa de prueba, preparado para el 8051. La frecuencia generada por este reloj podrá ser usada como frecuencia de reloj del 8051. Así mismo, mediante un señal recogida del PC a través de la tarjeta 48iocent, podremos generar otro reloj controlado por el PC. De esta forma se podrá trabajar con la placa de prueba a la velocidad real, o a velocidad controlada. Incluso se podrá detener la ejecución en cualquier momento, y en cualquier ciclo de reloj del microcontrolador. Esto nos permite verificar mediante un simple milímetro cualquier el estado de cualquier señal del bus, en cualquier estado de acceso a programa, datos, buses, etc.
   La frecuencia de reloj que llega al microporcesador U6, estará tomada de este generador interno de la placa Emulador 8051, o de una señal que viene del PC a través de la tarjeta 48IOcent. Esta selección se hace mediante U5. Mediante esta selección podremos pasar de Modo Run,en tiempo real, a Mode Trace, paso a paso, o como he dicho, parar la instrución en cualquier punto de ejecución.  Esto nos permite tracear prácticamente a nivel de microinstrución.
   
     El Acceso Directo a Memoria, DMA se activa mediante la señal RQPC, la cual bloque el paso de datos desde el microprocesador a la placa prototipo. Para ello, la señal procedente de la placa 48IOcent. Es enviada a un inversor (U3B), para que active o desactive los buffers U1, U2 y U4. El U3B simplemente mantiene la lógica, para que un nivel alto en RQPC bloquee las puertas y pase los integrados 74LS245 a estado flotante. De esta forma quedan los buses libres para que el PC pueda escribir en ellos.

    U1 y U2 aíslan el bus de datos y direcciones entre el microprocesador de la tarjeta y el zócalo del micro de la tarjeta a probar. Aísla el micro para crear el DMA. U2 funcionará en modo bidireccional, seleccionado por RD.

     U4 aísla las señales de control de microprocesador, para que sea el PC y no el micro el que controle el bus de la placa.

      Cuando el micro esté ejecutando, la señal RQPC estará a 0, y los puertos de la tarjeta 48IOcENT, que estén conectados a los buses de datos, direcciones y control estarán en estado flotante, para no interferir.
       Cuando deseemos conectarnos y leer la memoria de la tarjeta prototipo analizada pondremos RQPC a 1 y activaremos los puertos de la tarjeta 48IOCEN, para que desde y por ellos activemos las señales de control, datos y direcciones de la placa.
    
   La señal del pin 16 que va a la placa prototipo, y sería el WR del micro original ,está procesada por medio de U5 de manera que no solo mande escribir en la memoria, siempre que se de orden de leer desde el PC.

   Ahora solo queda realizar un programa capaz de controlar esta tarjeta, y desensamblar  y tracear el programa que contenga el prototipo.

     Para ello, como siempre, primero crearé una libería de funciones para el simulador 8051, la cual, configurará la carta 48IOCENT, para cada operación, y nos permitirá realizar todas las funciones de acceso a memoria de la tarjeta y de traceo. Así mismo tendremos las funciones de pantalla para realizar el dump, visualizar los registros del microprocesador, y visualizar el programa.

       Un caso muy especial es sacar los registros del microprocesador al exterior.



  
Prototipo inicial del emulador
Prototipo unido a la tarjeta prototipo 48IOCENT
                   


Prototipo 8051, ya en placa



lunes, 2 de diciembre de 2013

LIBRERÍA 48IOCENT

   
   Ya estamos listos para crear las funciones de control de la tarjera de 48 E/S. Esta librería la asociaremos a la general o crearemos una nueva para  nuestro hard.


    Dado que las funciones van asociadas al puerto paralelo, y dado que solo existe uno normalmente en los ordenadores, no defino dos puertos y trabajo directamente con el lpt1. Por eso en el programa trabajo directamente con las direcciones del puerto centronics, ya que crear variables y asignarlas a diferentes direcciones, creo que resulta un trabajo innecesario, dado el uso que haremos de las funciones.

    
48IOCENT.ASM
; Copyright (C) 2013  José Ángel Moneo Fernández

;    This program is free software: you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation, either version 3 of the License, or
;   (at your option) any later version.

;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.

;    You should have received a copy of the GNU General Public License
;    along with this program.  If not, see <http://www.gnu.org/licenses/


;CONTROL DE ACCIONES SOBRE LOS 8255
;ESTAS VARIABLES MUEVEN ELAS SEÑALES DE CONTROL
;DE LOS BUFER Y LATCH DE LA TARJETA PARA ABRIR
;PASO A LOS DATOS DENTRO ENTRE EL CENTRONICS Y
;LOS PUERTOS DE LOS 8255
RDL   EQU 0010b     ;LECTURA DE NIBLE BAJO
RDH   EQU 0100B     ;LECTURA DEL NIBLE ALTO
WR    EQU 0001B     ;ESCRITURA
ENDIR EQU 1000B     ;DIRECCIONAMIENTO

.model compact,pascal
.code
        public _inpio
        public _outpio
;lee un dato del puero 8255 indicado y lo devuelve en al
_inpio proc near uses bx cx dx,PUERTO
      
        push PUERTO             ;Selecciona el puerto indicado
        call _selecreg
        add sp,2
        mov al,RDH               ;Activa la orden RDh lectura parte alta
        out dx,al
        mov bx,379h
        xchg bx,dx
        in al,dx                ;lee el pueto 379h
        shl al,1                ; sube 1 bit lo leido para dejarlo como parte
        and al,0f0h
        mov ch,al               ;alta
        xchg bx,dx
        mov al,RDL              ;activa la orden RDl lectura parte baja
        out dx,al
        xchg bx,dx
        in al,dx                ;lee el puerto 379h y toma la parte baja
        mov bl,al
        mov cl,3
        shr bl,cl               ;traslada los bit abajo para posicionarlos
        and bl,0fh              ;elimina la posible basura
        or ch,bl                ;une lo leido a lo leido anteriormente
        mov al,ch               ;pasa el resulatado a al
        ret
_inpio endp

 ;envío  un dato del puero 8255 indicado
_outpio proc near   uses bx cx dx,PUERTO,DATO:byte

        push PUERTO             ;Selecciona el puerto indicado
        call _selecreg
        add sp,2
        mov dx,378h             ;lanzo el numero
        mov al,DATO
        out dx,al
        mov dx,37ah
        mov al,WR               ;indico WR
        out dx,al
        xor al,al               ;desactivo WR
        out dx,al

        ret
_outpio endp

        
;selecciona un registro interno de la targeta
; ESTA FUNCIÓN ES INTERNA A ESTA LIBRERÍA Y PERMITE
; ESCRIBIR LA DICRECIÓN DEL PUERTO ACTIVADA, PARA QUE
; SE GUARDE EN EL REGISTRO DE DIRECCIONES
; INTERNO DE LA TARJETA 48IOCENT
_selecreg proc near uses dx bx,puerto:byte    ;registro indicado en pila

        mov dx,37ah     ;elijo puerto de control del centronics
        mov al,ENDIR
        out dx,al        ;activo lach de direccion y desactivo resto
        mov bx,378h
        xchg bx,dx
        mov al,puerto
        out dx,al        ;lanzo la direccion indicada
        xchg dx,bx
        xor al,al
        out dx,al       ;deselecciono el lach de direccion

        ret
_selecreg endp

end



viernes, 29 de noviembre de 2013

CABECERA 48IOCENT

   Una vez que tenemos la tarjeta de E/S lista y hemos probado que llegan todas las señales correctamente y funcionan todas sus puertas, podemos comenzar a realizar el driver, o la librería para su control directo.
   Con esta librería podremos manejar ya sus puertos de forma transparente y configurar de forma sencilla la tarjeta.

    Para ello, como siempre, primero crearemos la cabecera con las constantes de control y configuración y las funciones de llamada.

    Para manejar los puertos de esta tarjeta necesitaremos:
                         Una función de configuración de tarjeta que llamaremos PioConfig.
                         Una función de envío a puerto que llamaremos OutPio.
                         Una función de lectura de puerto que llamaremos InPio.



    Aquí pongo la definición de la cabecera. En la siguiente entrada pondré la definición de las funciones que aquí llamo.



48IOCENT.ASM
; Copyright (C) 2013  José Ángel Moneo Fernández

;    This program is free software: you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation, either version 3 of the License, or
;   (at your option) any later version.

;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.

;    You should have received a copy of the GNU General Public License
;    along with this program.  If not, see <http://www.gnu.org/licenses/

;DIRECCIONAMENTO DE PUERTOS DE LA TARJETA 48IOCENT
;USART 1
PA0 EQU 0
PB0 EQU 1
PC0 EQU 2
P0 EQU 3        ;REGISTRO DE CONTROL
;USART 2
PA1 EQU 4
PB1 EQU 5
PC1 EQU 6
P1 EQU 7        ;REGISTRO DE CONTROL

;CONFIGURACIONES DE LOS PUERTOS
PSALIDA EQU 80H
PA EQU  10010000B
PB EQU  10000010B
PCH EQU 10001000B
PCL EQU 10000001B

;lee un dato del puero 8255 indicado y lo devuelve en al o en la variable indicada
InPio macro dest,puerto
ifndef _inpio
        extrn _inpio:near
endif
        mov ax,puerto
        push ax
        call _inpio
ifdif <reg>,<al>
        mov dest,al
endif
endm

;envío  un dato del puero 8255 indicado
OutPio macro puerto,dato
ifndef _outpio
        extrn _outpio:near
endif

        mov ax,puerto
        push ax
        mov al,dato
        push ax
        call _outpio

 endm

;CONFIGURA UN PUERTO 8255 DE LA TARJETA
 ;PARA DEFINIR LOS MODOS DE FUNCIONAMIENTO DE LOS PUERTOS BASTARA
;INDICAR LOS QUE SEAN DE ENTRADA, LOS DEMAS SERAN DE SALIDA
;ASI PA-I PB-O PCH-I SE INDICARA COMO.. "PA" OR "PCH"
;EN EL CASO DE TODO COMO SALIDA SE INDICARA "PSALIDA"
; PIOCONFIG P0,PA,PB ESTO CONFIGURARIA EL 8255 0 CON LOS PUERTOS
; A Y B COMO ENTRADA
; el puerto C podrá ser definido por nible como entrada o salida
PioConfig macro pio,cnfg0,cnfg1,cnfg2,cnfg3
ifndef _outpio
        extrn _outpio:near
endif
        mov ax,pio
        push ax
ifnb <cnfg3>
        mov al,cnfg0 or cnfg1 or cnfg2 or cnfg3
else
  ifnb <cnfg2>
        mov al,cnfg0 or cnfg1 or cnfg2
else
  ifnb <cnfg1>
        mov al,cnfg0 or cnfg1
else
        mov al,cnfg0
endif
endif
endif
        push ax
        call _outpio
endm

 

martes, 26 de noviembre de 2013

TEST 48IOCENT

   Tras poner en la entrada anterior la tarjeta de  48 E/S, ahora hay que pensar en probarla. Para ello, podemos usar una versión modificad del programa de Test de puerto Centronics.

   lo que haremos es cambiar los textos, para que simbolicen la función asignada para la tarjeta 48IOCENT en vez de indicar la función estándar de Impresión.



TEST_48IO.ASM
; Copyright (C) 2013  José Ángel Moneo Fernández

;    This program is free software: you can redistribute it and/or modify
;    it under the terms of the GNU General Public License as published by
;    the Free Software Foundation, either version 3 of the License, or
;   (at your option) any later version.

;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.

;    You should have received a copy of the GNU General Public License
;    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
;programa test para poner en marcha la tarjeta 48IOcent

include main.mac
include stdio.mac
include mouse.mac
include math.mac
include window.mac

.model compact,pascal
.stack 100

.data
raton dw 0   ;bandera para detectar flanco de pulsación

m1 db "Test tarjeta 48ioCENT  Por Jose Angel Moneo Fernández",0

datos db "Dato-378H",0
control db "Cont-37AH",0
Estado db "Esta-379H",0
iddat1 db "┌───────────── D0",0
iddat2 db "│┌──────────── D1",0
iddat3 db "││┌─────────── D2",0
iddat4 db "│││┌────────── D3",0
iddat5 db "││││┌───────── D4",0
iddat6 db "│││││┌──────── D5",0
iddat7 db "││││││┌─────── D6",0
iddat8 db "│││││││┌────── D7",0


idest1 db "---------- ────────┘││││",0
idest2 db "D3-D7----- ─────────┘│││",0
idest3 db "D2-D6----- ──────────┘││",0
idest4 db "D1-D5----- ───────────┘│",0
idest5 db "D0-D4──────────────────┘",0

idcont1 db "││││└────── WR",0
idcont2 db "│││└─────── RDL",0
idcont3 db "││└──────── RDH",0
idcont4 db "│└───────── EN DIR",0
idcont5 db "└────────── ",0

msalir  db "Salir",0

orden db "76543210",0
;Inicializamos la varialbe con 100h para que al presentar el número en 
; binario siempre mantenga los 8 dígitos. Despues borraremos el 1 inical
; en pantalla
numero dw 100h,0
ncontrol dw 100h,0
nestado dw 100h,0



.code

test48io  proc far

      main
      setcursor off
      resmouse
      mouse on
      putmouse 64,64
      clrscr

      gotoxy 10,1
      puts m1
      draw_win 9,35,12,46,control
      gotoxy 37,10
      puts orden
      draw_win 15,25,18,36,estado
      gotoxy 27,16
      puts orden
      draw_win 9,18,12,29,datos
      gotoxy 20,10
      puts orden
      gotoxy 8,19
      puts idest1
      gotoxy 8,20
      puts idest2
      gotoxy 8,21
      puts idest3
      gotoxy 8,22
      puts idest4
      gotoxy 8,23
      puts idest5
      gotoxy 40,13
      puts idcont1
      gotoxy 40,14
      puts idcont2
      gotoxy 40,15
      puts idcont3
      gotoxy 40,16
      puts idcont4
      gotoxy 40,17
      puts idcont5
       gotoxy 50,22
      puts msalir    
      mov dx,378h
      in al,dx
      mov byte ptr numero,al
      mov dx,37Ah
      in al,dx
      mov byte ptr ncontrol,al
refresca:      
      ;imprime el valor para enviar como dato
      gotoxy 19,11
      printf numero,@b   ;imprime el valor en binario
      gotoxy 19,11   ; borra el uno no valido
      putchar ' '
      ; imprime el valor para ncontrol
      gotoxy 36,11
      printf ncontrol,@b
      gotoxy 36,11   ; borra el uno no valido
      putchar ' '

test2:
      mov dx,379h
      in al,dx
      cmp al,byte ptr nestado  ; no imprime si no cambia el valor
      je espera
      mov byte ptr nestado,al
      gotoxy 26,17
      printf nestado,@b
      gotoxy 26,17   ; borra el uno no valido
      putchar ' '
      
 espera:

        getmouse                ;comprueba el estado del raton
        cmp ax,1
        jnz actual              ;si no se ha pulsado se espera
        cmp ax,raton            ;si se pulso se verifica si ha sido un
        mov raton,ax            ;flanco de subida
        jnz  seleccion           ;si es asi se selecciona la opcion deseada
actual:
        mov raton,ax
        jmp espera      
seleccion:
        getmousexy 19,11,8,0    ; numero
        cmp al,0ffh
        jnz op_numero
        getmousexy 36,11,8,0    ; ncontrol
        cmp al,0ffh
        jnz op_control
        getmousexy 50,22,5,0    ; ventana para salir
        cmp al,0ffh
        jnz fin
        jmp espera
        
op_control:        
        ; ncontrol

        ;Creamos una palabra con el bit indicado a 1
        ;mediante traslaciones de un bit
       
        mov cl,ah
        xor al,al   ; borra al , donde crearemos la mascara
        stc    ; carry a 1
        rcr al,cl      ; situa el bit elegido
        mov ah,al
        mov al,byte ptr ncontrol
        xor al,ah
        ;cambiar bit ncontrol para enviarlo
        mov byte ptr ncontrol,al
        mov dx,37Ah
        out dx,al
      
        jmp refresca
        
op_numero: ;numero 

        ;Creamos una palabra con el bit indicado a 1
        ;mediante traslaciones de un bit
        mov cl,ah
        xor al,al   ; borra al , donde crearemos la mascara
        stc    ; carry a 1
        rcr al,cl      ; situa el bit elegido
        mov ah,al
        mov al,byte ptr numero
        xor al,ah
        ;cambiar bit ncontrol para enviarlo
        mov byte ptr numero,al
        ;saca el dato por el puerto
        mov dx,378h
        out dx,al
       
        jmp refresca
fin:
      setcursor on
      mouse off
      ret
test48io endp

end test48io


sábado, 23 de noviembre de 2013

TARJETA 48IO CONTROLADA POR CENTRONICS (HARDWARE)

    Esta vez voy a colocar hardware en vez de software, por primera vez. No tiene sentido el ensamblador sin hardwre, por ello para aprender ensamblador hace falta hard.

   Voy a colocar el desarrollo de una tarjeta de 48 entradas/salidas, que será controlada por puerto centronics.

      Mediante esta tarjeta podremos controlar 6 puertos de 8 bits, configurables. 
      Esta tarjeta es una aplicaión de uso general que puede servirnos para probar, controlar o desarrollar otras futuros desarrollos hardware.
      Como la complejidad de control de manejo de los puertos de esta tarjeta aumenta respecto de la ya de por si complicada forma de manejo del Centronics, para manejarla deberemos de crear una librería de funciones.
      Estas librería estará compuesta por unas macros para crear llamadas a funciones de manejo, y una librería de funciones, que implementen las diferentes configuraciones y las diferenteS aperturas y cierres de puertos para el control a través del Centronics.
     Con la librería "48IO", que pondré en sucesivas entradas, haremos tanto el puerto Centronics, como esta tarjeta transparente a nuestras aplicaciones, de manera que será muy fácil centrarse en la tarjeta aplicación que estemos desarrollando olvidándonos del hardware que sirve de interface.
     
      Dado que la comunicación resulta crítica en muchos casos, por su ralentización en la respuesta de la aplicación que estemos desarrollando, el puerto Centronics resulta perfecto, dado que la velocidad de respuesta es muy alta en comparación con un puerto serie. Así mismo que las librerías de comunicación estén en ensamblador nos permitirá tener una gran velocidad de proceso, reservando para el proceso real, todo el mayor tiempo posible, y reduciendo al mínimo el tiempo de lectura y escritura de datos sobre la tarjeta prototipo.



ESQUEMA TARJETA 48IOCENT
 Copyright (C) 2013  José Ángel Moneo Fernández

    This program is free hardware: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

    This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

     Esta tarjeta aunque es simple, pues lleva pocos componentes, puede que necesite algunas aclaraciones.

     JP8 es el conector de alimentación
     JP7 es el conector centonics, el cual se usa para conectar al PC.
     JP1 a JP6 son los puertos de salida, colocados en conectores de 8 bits con toma de tensión y tierra, de forma que puedan usarse independientemente para alimentar y controlar diferentes tarjetas.

      El corazón del equipo son dos 8255, los cuales se utilizarán directamente como puertos. Por ello no se utilizan latch y buffer de protección. igualmente no se han colocado ningún tipo de sistema de optoacoplamiento, pues esta tarjeta es de propósito general. Estos elementos deberán ser parte de los prototipos colgados de esta carta. De manera que la protección de los 8255 queda en manos de las cartas adicionales. Esto es necesario para permitir que esta tarjeta pueda trabajar como entradas o salidas en cualquier caso.

      Como interface entre los 8255 y el puerto centronics están el U1,U4 y U5. El U1 es un buffer que sirve de puerta para pasar los datos del puerto de salida centronics a los 8255. Esto nos permitirá mover los datos del puerto centronics, sin que el 8255 vea los cambios hasta que la información esté estable. Esta puerta será abierta a través de la señal WR mediante el Strobe del puerto centronics (pin 1), que pasará a tener la función de WR.

     A su vez U4 y $5 son dos latch de 4 bits que nos permitirán recoger el bus de datos de 8 bits del 8255, y descomponerlo en dos nible. Dado que el puerto centronics no tiene un puerto de 8 bits de entradas sino solo de 4 bits. Por lo tanto usaremos el puerto de estado del centronics para recoger los datos de los 8255, en dos partes, una el nible alto y otra el nibel bajo. Este direccionamiento se hará a través de las señales RDL y RDH que serán controladas por los pines 14 y 16 del centronics. La señal del pin 14 está en el centronics invertida, por lo que para que sea más transparente a nuestro programa la invertimos por hardware mediante el inversor U2B(74LS04). De esta manera en programa consideraremos un 1 lectura, independientemente de que la señale en el centronics la inverta.

    Los diodos D1 y D2 con U2D forman una NOR qye nos permite tener una señal común de lectura para abrir el puerto de lectura de los 82555 tanto si leemos el nible bajo como si leemos el nible alto.

    El U3 es un lacth, o bufer para el almacenamiento del direccionamiento interno de los 8255. En el almacenaremos la dirección del registro al que queremos acceder dentro de los 8255. De este registro solo se usan 3 bits, y el bit alto, bit 2, será el que seleccione el 8255, mientras que los dos bit bajos seleccionan el registro interno dentro del 8255. De esta forma el mapa quedará como 4 direcciones consecutivas. Las cuatro primeas 0-3 direccionan los puertos de U6 y las siguientes 4-7 los puertos del U7.

   Para finalizar indicar simplemente que las resistencias R2,R4,R5 están puestas para asegurar el estado correcto de las señales a falta de señal.

    Esta tarjeta ha sido probada durante mucho tiempo en aplicaciones posteriores, muy complejas. Y puedo garantizar su estabilidad y perfecto funcionamiento.
     
     El esquema es tan sencillo que no resulta difícil fabricarlo en placas pretaladradas, mediante cables soldados. De hecho el primer prototipo lo fabrique así, y todavía funciona. por ello fabricar esta placa resulta muy barato. Por 15€ se puede hacer.

     Aquí tenéis el Esquema en formato PDF.
  
                                   
Prototipo Inicialtarjeta 48IOCENT
Prototipo Inicial tarjeta 48IOCENT

Prototipo Inicial tarjeta 48IOCENT

Prototipo Final tarjeta 48IOCENT