Las 2 memorias que presente son EPROMs, osea, Ereasable Programable Read Only Memory. Yo las profane de una PC 386 ACER.
Ambas son de 28 pines, de la marca STMicroelectronics, pero presentan distintas entradas de direccion de memoria. La M27C256 es de 256Kbits de memoria, y por cada direccion de memoria, se lee un Byte (8 Bits). Sacando cuentas, tenemos 32Kbytes de memoria en total. Para la M27C512 es lo mismo, solamente que son 512Kbits y en resumen 64Kbytes.
| Modelo | Pines de direccion de memoria | Memoria |
| M27C256 | 15 | 32Kbytes = 256Kbits |
| M27C512 | 16 | 64Kbytes = 512Kbits |
Con esa tablita mas o menos te guias en cuantos pines (o bits) son necesarios para direccionar la cantidad de memoria que traen.
Si tengo 15 bits, el maximo va a ser 111111111111111 (15 bits 1) significan 32767 en decimal, que a su vez es logico, ya que 32Kbytes son 32768 Bits.
Ahora, si tengo 16 pines (o bits) para posicionar la memoria, el maximo va a ser 1111111111111111 que son 65535, y tambien es logico ya que 64Kbytes son 65536 Bits.
Destaco que hay una diferencia entre ambos numeros de 1; bueno esto pasa porque el binario arranca en 0, y el decimal arranca en 1. Ahi tenes la diferencia.
Bueno hasta aca tenemos mas o menos entendido como funcionan. Te explico mas o menos como son por dentro.
Aca hice un esquema en base a otra memoria que tambien tengo (pero que esta vacia, por eso no hable de esta). Es de National Semiconductors. 32Kbits = 4Kbytes. Con lo que explique arriba, nos da una cantidad de 12 pines para direccionar la memoria.

Basicamente es asi, al seleccionarle la memoria estas seleccionando 8 bits del matrix de celdas, pasa por un buffer de salida (que en realidad habilita las salidas o no segun el pin OutputEnable tenga un estado logico 1). Normalmente los pines de ChipEnable u OutputEnable estan negados.
Dentro de esos diagramas de bloques, hay millones de transistores, y de FETs (transistores de efecto de campo). Hay un tipo de FETs que cumplen la funcion de guardar la informacion, que tienen el nombre de FAMOS (Floating Gate Avalanche-Injection Metal Oxide Semiconductor). La carga electrica que esta almacenada en la puerta aislada o flotante determina que el bit de la celda contenga un 1 o un 0. Aunque parezca extraño, las celdas cargadas son leidas como un 0, y las que no estan son leidas como un 1. Es asi que cuando una EPROM esta limpia o virgen, todos sus valores son 255 (11111111 o FF en hexadecimal).
Ya que estas celdas estan aisladas, para poder programarlos, es necesario una tension mayor a la comun TTL o CMOS para que los electrones pasen por la puerta. Normalmente 12,75V o en algunos casos 25V.
Bueno, ya se sabe mas o menos como funcan los bichos estos.
Ahora pasamos al circuito y la logica de como hacerlo funcionar.
Bien, como en mi otro post habia comentado, el paralelo tiene 4 bits de entrada (en realidad 5, pero igual no alcanzan). Es por eso que decidi usar el selector de datos CD4512. Este es un integrado de la familia logica 4000. Su funcion es simple; tenes 3 bits de seleccion de salida, 8 bits de entrada y 1 solo de salida. Con esto logramos multiplexar 8 bits paralelos en 1 bit en modo serie (es decir, un bit atras del otro hasta llegar a los 8 bits).
Aca te dejo el esquema del diagrama de conexiones y la tabla de verdad. Con esto te das cuenta como es el asunto:

Es decir, yo al tener 3 bits, estoy seleccionando 1 bit de los 8 bits de entrada.
Con eso podemos leer los 8 bits de salida de la EPROM y enviarlo a una sola entrada de las 4 que posee el paralelo. Pero ademas, necesitamos enviar los 3 bits de direcciones. Mas adelante explico como funciona. A este integrado yo lo llamo Encoder.
Ahora ya sabemos como leer los 8 bits de la EPROM, pero necesitamos expandir los 8 bits de salida del paralelo a minimamente 15 bits (vamos a hacerlo de 16 para leer tambien la de 512).
Para eso vamos a usar un Buffer con Latch: 74HC373. Pertenece a la familia logica 74HC y "retiene 8 bits en su memoria temporalmente". Un pin del integrado, la llamada Latch es la encargada de "guardar" esta informacion. Cuando tiene el estado logico 1, todos los datos que reciba, los va a repetir en su salida. Al cambiar al estado 0, la salida se mantiene constante; es decir, "quedaron guradados" los 8 bits. Ahora, podemos joder con la entrada y la salida va a seguir siendo lo mismo, mientras que el pin de Latch siga con estado logico 0.
Aca te dejo un esquemita de como seria logicamente:

Es decir, podemos usar los 8 bits principales del puerto paralelo como bus principal entre dos integrados y manejarle los Latches por separado con uno de los 4 bits sobrantes del paralelo (los que estan negados).
Con esto podemos lograr desmultiplexar 10 bits en 16 bits (8 bits de datos principales + 2 bits de ambos Latches). Al setearle los datos a ambos integrados, el canal de datos lo podemos usar para otros fines, ya que los latches estan activados. Justamente, cuando se termina de setearle la memoria a ambos integrados, se puede usar el canal para enviar los 3 bits necesarios en el Encoder que mencione antes; y de esta manera leer los 8 bits de salida de la EPROM.
Si me seguiste hasta aca, entonces mirate este esquema de conexiones entre todos los integrados. Es necesario que lo interpretes, ya que ahi estan los pines del paralelo, los cuales luego vamos a usar en VB.

EDIT1: en el esquematico no estan las conexiones de voltaje. si van a hacer el circuito, usen 5V en comun para todo, es decir, para ambos buffers, para el encoder y para la memoria. usen una fuente aparte, no usen los 5v que entrega el paralelo y conecten ambas masas en comun (fuente externa y paralelo).
(remarco que los ultimos 4 pines de la salida del paralelo estan invertidos, ya que asi fue como lo hice en la plaqueta, ustedes lo pueden hacer como dios manda, pero tendrian que cambiar un poquito el codigo de VB)
Interpretemos un poquito el circuito. El pin 14 maneja el latch del buffer 1; el pin 17 maneja el buffer 2. El pin 13 es una entrada, por donde va a pasar el byte leido.
Basicamente esta todo explicado, solo falta un poquito de imaginacion para idear que es lo que va a pasar cuando hay un bit en cada salida.
Ahora, pasamos al code. Antes que nada declaramos solo lo que vamos a usar, 2 funciones en comun (decTObin y binTOdec) y la direccion del puerto:
Public Declare Function PortIn Lib "IO.DLL" (ByVal Port As Integer) As Byte
Public Declare Sub SetPortBit Lib "IO.DLL" (ByVal Port As Integer, ByVal Bit As Byte)
Public Declare Sub ClrPortBit Lib "IO.DLL" (ByVal Port As Integer, ByVal Bit As Byte)
Private Const Out_Port As Integer = &H378 '0x378 o 888
Private Const In_Port As Integer = Out_Port + 1
Function decTObin(a As Long) As String
If Int(a / 2) = 0 Then decTObin = decTObin & a Mod 2 Else: decTObin = decTObin(Int(a / 2)) & a Mod 2
End Function
Function binTOdec(a As String) As Long
If Len(a) = 1 Then binTOdec = Val(a) Else: binTOdec = Val(Left(a, 1)) * 2 ^ (Len(a) - 1) + binTOdec(Right(a, Len(a) - 1))
End Function
Ahora, una funcion muy simple para setear los 8 bits de salida de cada buffer estado logico 0; es decir, hacer un Clear.
Private Function ClearBuffer(ByVal intBuffer As Integer)
Select Case intBuffer
Case 1 'Buffer 1
ClrPortBit Out_Port + 2, 1 'Desactivo Latch del buffer1
SetPortBit Out_Port + 2, 3 'Activo Latch del buffer2
Case 2 'Buffer 2
SetPortBit Out_Port + 2, 1 'Activo Latch del buffer1
ClrPortBit Out_Port + 2, 3 'Desactivo Latch del buffer2
End Select
'Limpio el Bus de 8 bits
ClrPortBit Out_Port, 0
ClrPortBit Out_Port, 1
ClrPortBit Out_Port, 2
ClrPortBit Out_Port, 3
ClrPortBit Out_Port, 4
ClrPortBit Out_Port, 5
ClrPortBit Out_Port, 6
ClrPortBit Out_Port, 7
'DoEvents 'No hace falta
'Dejo ambos Latches habilitados
SetPortBit Out_Port + 2, 1
SetPortBit Out_Port + 2, 3
End FunctionEs un codigo muy simple, que lo unico que hace es deshabilitarle el latch al buffer seleccionado, vaciar el bus de 8 bits y habilitar de nuevo el latch.
Ahora, vamos con 3 funciones que seleccionan la memoria: SelectMemory (que selecciona el byte a leer), Send2Bytes (que envia los 16 bits a la EPROM) y LoadBuffer (se encarga de enviar los 8 bits al buffer elegido).
Private Function LoadBuffer(ByVal intBuffer As Integer, ByRef blnBits() As Boolean) 'Envio un array de booleans a un determinado buffer
Dim intIndex As Integer
Select Case intBuffer
Case 1 'Buffer 1
ClrPortBit Out_Port + 2, 1 'Desactivo Latch del buffer1
SetPortBit Out_Port + 2, 3 'Activo Latch del buffer2
Case 2 'Buffer 2
SetPortBit Out_Port + 2, 1 'Activo Latch del buffer1
ClrPortBit Out_Port + 2, 3 'Desactivo Latch del buffer2
End Select
'Recorro todo el array y seteo el bus con sus valores
For intIndex = LBound(blnBits) To UBound(blnBits)
If blnBits(intIndex) Then 'aca hay un 1
SetPortBit Out_Port, intIndex 'seteo el bit intIndex (que a su vez es correlativo con el bucle) con 1
Else
ClrPortBit Out_Port, intIndex 'aca lo seteo con 0
End If
Next intIndex
DoEvents
'Dejo ambos Latches habilitados
SetPortBit Out_Port + 2, 1
SetPortBit Out_Port + 2, 3
'Limpio el bus de 8 bits
ClrPortBit Out_Port, 0
ClrPortBit Out_Port, 1
ClrPortBit Out_Port, 2
ClrPortBit Out_Port, 3
ClrPortBit Out_Port, 4
ClrPortBit Out_Port, 5
ClrPortBit Out_Port, 6
ClrPortBit Out_Port, 7
End Function
Private Function Send2Bytes(ByRef blnArr() As Boolean) 'Parte en 2 un array de Booleans para enviar 8 bits a un buffer y 8 bits al otro buffer
Dim blnArr1(7) As Boolean
Dim intIndex As Integer
For intIndex = 0 To 7 'Paso el array tal cual desde el 0 hasta el 7 (8)
blnArr1(intIndex) = blnArr(intIndex)
Next intIndex
Call LoadBuffer(1, blnArr1) 'Lo cargo en buffer1
For intIndex = 0 To 7 'Paso el array desde el index 8 a 15 (8)
blnArr1(intIndex) = blnArr(intIndex + 8)
Next intIndex
Call LoadBuffer(2, blnArr1) 'Lo cargo en buffer2
End Function
Private Function SelectMemory(ByVal lngPosition As Long) 'Selecciono la posicion en memoria que quiero leer
Dim strBin As String
Dim intIndex As Integer
Dim blnArr(15) As Boolean
strBin = decTObin(lngPosition) 'Paso la posicion a binario
strBin = String$(16 - Len(strBin), "0") & strBin 'Hago que el len siempre sea de 16 (rellenar lo que falta con 0)
'Cargo el array de 16 items a partir del string en binario
blnArr(12) = IIf(Mid$(strBin, 1, 1) = "1", True, False) 'estos son los que estan cruzados
blnArr(13) = IIf(Mid$(strBin, 2, 1) = "1", True, False) ' *
blnArr(14) = IIf(Mid$(strBin, 3, 1) = "1", True, False) ' *
blnArr(15) = IIf(Mid$(strBin, 4, 1) = "1", True, False) ' *
blnArr(11) = IIf(Mid$(strBin, 5, 1) = "1", True, False)
blnArr(10) = IIf(Mid$(strBin, 6, 1) = "1", True, False)
blnArr(9) = IIf(Mid$(strBin, 7, 1) = "1", True, False)
blnArr(8) = IIf(Mid$(strBin, 8, 1) = "1", True, False)
blnArr(4) = IIf(Mid$(strBin, 9, 1) = "1", True, False) 'estos tambien estan cruzados
blnArr(5) = IIf(Mid$(strBin, 10, 1) = "1", True, False) ' *
blnArr(6) = IIf(Mid$(strBin, 11, 1) = "1", True, False) ' *
blnArr(7) = IIf(Mid$(strBin, 12, 1) = "1", True, False) ' *
blnArr(3) = IIf(Mid$(strBin, 13, 1) = "1", True, False)
blnArr(2) = IIf(Mid$(strBin, 14, 1) = "1", True, False)
blnArr(1) = IIf(Mid$(strBin, 15, 1) = "1", True, False)
blnArr(0) = IIf(Mid$(strBin, 16, 1) = "1", True, False)
Call Send2Bytes(blnArr)
End FunctionCon eso ya estamos en condiciones de leer un byte. Para tal motivo, esta la siguiente funcion, que hace uso del bus de 8 bits sin molestar a los buffers, para seleccionar que bit leer de la EPROM. Esto esta en base a la tabla de verdad que esta mas arriba. Ademas, hay otra funcion que se encarga de obtener el estado logico de un pin la entrada del paralelo (que es justamente la salida del encoder).
Private Function ReadByteASCII() As String
'Direccion en 4512 | Bit
' A | 6
' B | 5
' C | 4
Dim strBin As String
Dim blnOut(7) As Boolean
ClrPortBit Out_Port, 6
ClrPortBit Out_Port, 5
ClrPortBit Out_Port, 4
strBin = IIf(ReadPin(1), "1", "0")
SetPortBit Out_Port, 6
ClrPortBit Out_Port, 5
ClrPortBit Out_Port, 4
strBin = strBin & IIf(ReadPin(1), "1", "0")
ClrPortBit Out_Port, 6
SetPortBit Out_Port, 5
ClrPortBit Out_Port, 4
strBin = strBin & IIf(ReadPin(1), "1", "0")
SetPortBit Out_Port, 6
SetPortBit Out_Port, 5
ClrPortBit Out_Port, 4
strBin = strBin & IIf(ReadPin(1), "1", "0")
ClrPortBit Out_Port, 6
ClrPortBit Out_Port, 5
SetPortBit Out_Port, 4
strBin = strBin & IIf(ReadPin(1), "1", "0")
SetPortBit Out_Port, 6
ClrPortBit Out_Port, 5
SetPortBit Out_Port, 4
strBin = strBin & IIf(ReadPin(1), "1", "0")
ClrPortBit Out_Port, 6
SetPortBit Out_Port, 5
SetPortBit Out_Port, 4
strBin = strBin & IIf(ReadPin(1), "1", "0")
SetPortBit Out_Port, 6
SetPortBit Out_Port, 5
SetPortBit Out_Port, 4
strBin = strBin & IIf(ReadPin(1), "1", "0")
ReadByteASCII = Chr$(binTOdec(strBin))
End Function
'by TodoRobot
Private Function ReadPin(ByVal intPin As Integer) As Boolean
Dim Lectura As Byte
Dim Estado(3) As Byte
Dim Estado_Str(3) As String
Dim Bucle As Byte
Lectura = PortIn(In_Port) 'leo el puerto de entrada
'filtro solo los bit's que necesito conocer
Estado(0) = Lectura And &H8 '= 00001000 en Binario
Estado(1) = Lectura And &H10 '= 00010000 en Binario
Estado(2) = Lectura And &H20 '= 00100000 en Binario
Estado(3) = Lectura And &H40 '= 01000000 en Binario
ReadPin = Estado(intPin) <> 0
End Function
Ahora si, ya podemos seleccionar que parte de la memoria leer y leerla. Entonces vamos a leerla completa:
Sub Main()
Dim lngIndex As Long
Open App.Path & "\dump.rom" For Binary Access Write As #1
'Nota aca: si vas a leer la memoria M27C512, el numero que tenes que poner es 65535
' si vas a leer la memoria M27C256, el numero que tenes que poner es 32767
For lngIndex = 0 To 65535
Call SelectMemory(lngIndex)
Put #1, , ReadByteASCII
Next lngIndex
Close #1
End SubEn ese caso, el programita te va a comer todo el micro. Al cabo de un rato, va a terminar y listo. En el directorio del proyecto vas a tener un archivito con el dump de la memoria.
Ya que estaba al pedo, attacheo ambos dumpeos de las EPROMs que tenia yo:
dump256.romdump512.romEDIT2: Paginas de referncia:
http://en.wikipedia.org/wiki/Parallel_port http://perso.wanadoo.es/pictob/eprom.htm (informacion interesante)
http://www.datasheetcatalog.com/ (datasheets de los integrados)
EDIT3: Ambas memorias se pueden usar en el mismo socket, con las mismas conexiones, solamente cambien el valor de lectura.
EDIT4: Texto, imagenes, idea y circuito bajo licencia:
EDIT5: Attacheo 2 fotos de como quedo el aparato hecho en una plaqueta universal:


En fin, espero que les haya sido de utilidad. Cualquier consulta haganla aca.