31 mayo 2007

Estantería REALMENTE fabulosa

He aquí parte de un folleto propagandístico aparecido en el buzón de mi humilde morada ayer mismo. Se trata de una empresa armariera que por la compra de sus productos regala una estantería, según dicen, fabulosa:


Ya lo creo que es fabulosa, teniendo en cuenta que mide veinte metros de alto por cinco de ancho por tres de fondo. Se me antoja realmente un buen negocio, pues con semejante monstruo en mi poder, las posibilidades son infinitas, pudiendo por ejemplo:

  • Alquilar las baldas como plazas de parking.
  • Montar un hotel cápsula al más puro estilo nipón (a cuatro cápsulas por balda).
  • Vender mi piso y vivir en el susodicho armario (¡seis habitaciones de 15 m2 cada una!)
El problema es dónde colocar semejante mamotreto, pues aunque mi piso es razonablemente diáfano, desde luego no tengo los techos a veinte metros de altura.

En fin, curioso lo que puede uno encontrar por esos mundos de Yupi.

25 mayo 2007

Mi orgullo friki (extracto)

;================================================
;=== Proceso del paquete recibido: ===
;=== Comprobacion del protocolo del paquete ===
;================================================

ld hl,(IN_BUFFER+2) ;Ether-Type

;--- Comprueba si el protocolo es conocido,
; si lo es salta a la parte adecuada del codigo

ld de,#0008
call COMP
jp z,IS_IP

ld de,#0608
call COMP
jp z,IS_ARP

;--- Protocolo desconocido: lo ignoramos

jp END_GET_PACK


;==============================
;=== Paquete ARP recibido ===
;==============================

;Algoritmo segun RFC826, "Packet Reception"

IS_ARP: ;--- Comprueba el hardware y el protocolo

ld hl,(IN_BUFFER+4)
ld de,#0100
call COMP
jp nz,END_GET_PACK

ld hl,(IN_BUFFER+6)
ld de,#0008
call COMP
jp nz,END_GET_PACK

;--- Comprueba si el tamanyo de la dir HW es 0: paquete UNARP.
; En ese caso busca la entrada y la borra si no es estatica.

ld a,(IN_BUFFER+8)
cp 6
jr z,NO_UNARP
or a
jp nz,END_GET_PACK ;Si no es 0 ni 6, es incorrecto

ld de,IN_BUFFER+18 ;Busca entrada ARP...
call SRCH_ARP
jp c,END_GET_PACK

ld a,(ix)
cp 1
jp z,END_GET_PACK

ld (ix),0 ;...y si la encuentra, la borra
call END_GET_PACK ;si no es estatica.
NO_UNARP:

;--- Si la IP de origen es 0, no cachea la direccion
; (clientes DHCP comprobando la direccion que se
; les acaba de asignar pueden enviar un ARP request
; con la IP del enviador a cero)

ld a,#FF
ld (MERGE_FLAG),a

ld hl,IN_BUFFER+18
ld de,ZERO32
call COMP32
jr z,IS_ARP_2

;--- Comprueba si hay una entrada ARP para esa IP

xor a
ld (MERGE_FLAG),a

ld de,IN_BUFFER+18
call SRCH_ARP
jr c,NOTINCACHE

;* Encontrada: actualiza dir MAC

ld a,#FF
ld (MERGE_FLAG),a

ld a,(ix) ;Si es estatica, no hace nada
cp 1
jp z,NOUPTIMER

push ix
pop de
inc de
ld hl,IN_BUFFER+12
ld bc,6

ldir
NOUPTIMER:

;* Actualiza temporizador de expiracion

ld (ix),2 ;Por si estaba en resolucion
push ix
pop hl
ld bc,11
add hl,bc
ex de,hl
ld hl,ARP_TOUT
ld bc,4
ldir
NOTARPSTAT:
NOTINCACHE:

;--- Si la IP requerida no es la nuestra, terminar ya

IS_ARP_2: ld hl,IN_BUFFER+28
ld de,BUF_IPLOCAL
call COMP32
jp nz,END_GET_PACK

;--- Si la entrada no estaba en la tabla, anyadirla

ld a,(MERGE_FLAG)
or a
jr nz,OKMERGEARP

call GET_FREE_ARP

ld (ix),2 ;Entrada dinamica

push ix ;Copia la MAC y la IP de una sentada
pop de
inc de
ld hl,IN_BUFFER+12
ld bc,10
ldir

ld hl,ARP_TOUT ;Establece temp. de expiracion
ld bc,4
ldir
OKMERGEARP:

;--- Si el paquete era Request, enviar Reply

ld a,(IN_BUFFER+11)
cp 1
call z,SEND_ARP_RP

jp END_GET_PACK

MERGE_FLAG: db 0


;===============================
;=== Datagrama IP recibido ===
;===============================

IS_IP: ;

;--- Primero comprobamos que llegue al tamanyo minimo:
; Eth1 Eth2 Type Cabecera(20)

ld hl,(INBUF_SIZE)
ld de,34
call COMP
jp nc,END_GET_PACK

;--- Guardamos el tamanyo de la cabecera IP en OUT_BUFFER.
; Si es <20 bytes, ignoramos el paquete.

ld a,(IN_BUFFER+4)
and #0F
cp 5
jp c,END_GET_PACK

sla a
sla a
ld l,a
ld h,0
ld (IP_HEADER_LEN),hl

;--- Guardamos el tamanyo total del paquete en OUT_BUFFER+2.
; Si es >576 bytes, ignoramos el paquete.

ld a,(IN_BUFFER+6)
ld h,a
ld a,(IN_BUFFER+7)
ld l,a
ld de,577
call COMP
jp c,END_GET_PACK

ld (IP_TOTAL_LEN),hl

;--- Calcula el checksum de la cabecera

ld a,(CHKVECT)
and %10
jr z,IPCHKSOK

ld ix,IN_BUFFER+4
ld bc,(IP_HEADER_LEN)
call CALC_CHKSUM
ld a,d
or e
jp nz,END_GET_PACK
IPCHKSOK: ;

;--- Comprueba la direccion IP de destino,
; debe ser la nuestra o una de broadcast

;* Es nuestra IP?

ld hl,IN_BUFFER+20
ld de,BUF_IPLOCAL
call COMP32
jr z,OK_IPDEST

;* Es de broadcast? (Mascara OR dir = todo 1s?)

ld hl,SUBNET_MASK
ld de,IN_BUFFER+20
ld b,4
CHKIPBROAD2: ld a,(de)
or (hl)
inc a
jp nz,END_GET_PACK
inc hl
inc de
djnz CHKIPBROAD2
OK_IPDEST:

;--- Si el campo de version no tiene 4, ignoramos el datagrama

ld a,(IN_BUFFER+4)
and #F0
cp #40
jp nz,END_GET_PACK

;--- Si es un fragmento de datagrama, lo ignoramos

ld hl,(IN_BUFFER+10)
res 7,l ;Pone a 0 el bit no usado (previene futuras extensiones de IP)
res 6,l ;Pone a 0 el bit DF. Desde ahora HL solo tiene DF y Frag. Off.
ld a,h ;Si MF o Fragment Offset son <>0,
or l ;es un fragmento: ignorarlo
jp nz,END_GET_PACK

;--- Si el paquete contiene opciones, las descartamos
; (movemos los datos a la posicion IN_BUFFER+24)

ld hl,(IP_HEADER_LEN) ;HL = Long cabecera
ld de,20
call COMP
jr z,IPOPTOK

ld bc,IN_BUFFER+4
add hl,bc ;HL = Inicio de los datos
ld de,IN_BUFFER+24
ld bc,(IP_TOTAL_LEN)
ldir

ld hl,(IP_TOTAL_LEN) ;Cambia el tamanyo
ld de,(IP_HEADER_LEN) ;del paquete para no incluir
or a ;las opciones IP
sbc hl,de ;HL = Long. sin cabeceras
ld de,20
add hl,de ;HL = Long. con cabecera sin opciones
ld a,h
ld (IN_BUFFER+6),a
ld a,l
ld (IN_BUFFER+7),a
ld (IP_TOTAL_LEN),hl
ld (IP_HEADER_LEN),de
IPOPTOK:

;--- Sustituye el campo de tamanyo total del paquete
; por el tamanyo de los datos, y en little-endian

ld hl,(IP_TOTAL_LEN)
ld de,(IP_HEADER_LEN)
or a
sbc hl,de
ld (IN_BUFFER+6),hl

;--- Decide accion segun protocolo transportado

ld a,(IN_BUFFER+13)
cp 6
jp z,IS_TCP
cp 17
jp z,IS_UDP
cp 1
jp z,IS_ICMP

jp END_GET_PACK ;Desconocido: ignoramos


;===============================
;=== Mensaje ICMP recibido ===
;===============================

IS_ICMP: ;--- Comprueba el checksum

ld a,(CHKVECT)
and %10000
jr z,ICMPCHK_OK

ld ix,IN_BUFFER+24
ld bc,(IN_BUFFER+6)
call CALC_CHKSUM
ld a,d
or e
jp nz,END_GET_PACK
ICMPCHK_OK: ;

;--- Comprobamos el tipo

ld a,(IN_BUFFER+24)
or a ;ECHO Reply?
jr z,IS_ICMP_EREP
cp 3
jp z,IS_ICMP_HOSTUN
cp 8 ;ECHO request?
jp nz,END_GET_PACK ;Otro: lo ignoramos

;--- ICMP Echo Request: Lo respondemos si REPLYECHO<>0

IS_ICMP_EREQ: ld a,(REPLYECHO)
or a
jp z,END_GET_PACK

ld hl,IN_BUFFER+24 ;Copiamos el mensaje tal cual en la respuesta
ld de,OUT_BUFFER+24
ld bc,(IN_BUFFER+6)
push bc
inc bc ;Para que incluya el 0 de padding
ldir

xor a
ld (OUT_BUFFER+24),a ;Cambia el tipo a "Echo reply"
pop bc
ld hl,0
ld (OUT_BUFFER+26),hl ;Borra checksum antiguo
ld ix,OUT_BUFFER+24
call CALC_CHKSUM
ld (OUT_BUFFER+26),de ;Establece nuevo checksum

ld hl,(IN_BUFFER+16) ;IP destino = el originador del mensaje
ld de,(IN_BUFFER+18)
ld bc,(IN_BUFFER+6) ;Longitud
ld a,1 ;Protocolo = ICMP
jp SEND_IP

;--- ICMP Echo Reply: lo encolamos si no hay ya 4 encolados

IS_ICMP_EREP: ld a,(ICMPI_PINDEX)
ld b,a

inc b
ld ix,ICMPI_IP0-11
ld de,11
BUC_EREP1: add ix,de
djnz BUC_EREP1 ;Ahora IX apunta a la zona de datos adecuada

ld a,(ICMPI_PINDEX)
ld b,a
ld a,(ICMPI_GINDEX)
cp b
jr nz,EREP_HAYSITIO

ld a,(ix) ;Indice GET = Indice PUT:
or (ix+1) ;Hay sitio si entrada vacia (IP=0),
or (ix+2) ;si no, terminamos.
or (ix+3)
jp nz,END_GET_PACK
EREP_HAYSITIO: ;

ld hl,(IN_BUFFER+16)
ld (ix),l
ld (ix+1),h
ld hl,(IN_BUFFER+18)
ld (ix+2),l
ld (ix+3),h
ld a,(IN_BUFFER+12) ;TTL
ld (ix+4),a
ld hl,(IN_BUFFER+28) ;Identificador (guardamos en L.E.)
ld (ix+5),h
ld (ix+6),l
ld hl,(IN_BUFFER+30) ;Numero de secuencia (guardamos en L.E.)
ld (ix+7),h
ld (ix+8),l
ld hl,(IN_BUFFER+6) ;Longitud de los datos
ld bc,8 ;Resta long. cabecera ICMP
or a
sbc hl,bc
ld (ix+9),l
ld (ix+10),h

ld a,(ICMPI_PINDEX) ;Incrementa indice PUT
inc a ;saltando de 3 a 0
and %111
ld (ICMPI_PINDEX),a

jp END_GET_PACK

;--- ICMP Destination Unreachable: si esta asociado a una
; conexion TCP, la abortamos con codigo 7

;Nota: la cabecera del paquete causante de esta respuesta
;ICMP esta en IN_BUFFER+32.

IS_ICMP_HOSTUN: ld a,(IN_BUFFER+9+32)
cp 6 ;Si el paquete causante no es TCP, ignorarlo
jp nz,END_GET_PACK

ld hl,(IN_BUFFER+20+28)
ld de,(IN_BUFFER+22+28)
ld a,(IN_BUFFER+24+28)
ld iyh,a
ld a,(IN_BUFFER+25+28)
ld iyl,a
ld a,(IN_BUFFER+26+28)
ld ixh,a
ld a,(IN_BUFFER+27+28)
ld ixl,a
call SEARCH_TCP
cp #FF ;Conexion asociada inexistente: terminar
jp z,END_GET_PACK

call LOAD_TCB
xor a ;Cierra la conexion con codigo 7
ld (TCP_STATE),a
ld a,7
ld (LAST_CLOSE),a
call SAVE_TCB

jp END_GET_PACK


;==============================
;=== Paquete UDP recibido ===
;==============================

IS_UDP: ;--- Comprueba checksum, excepto si CHKVECT dice lo contrario

ld a,(CHKVECT)
and %1000
jp z,OK_UDP_CHKSUM

;* Para calcular el checksum, componemos la pseudo-cabecera
; justo antes de los datos, pisando la cabecera IP.
; Pero como el orden de los datos (palabras de 16 bits)
; no es importante para el calculo,
; aprovechamos que las direcciones IP y el campo de protocolo
; ya estan establecidos en la cabecera IP, y asi
; solo necesitamos establecer 0 donde estaba el TTL y la longitud UDP
; donde estaba el checksum IP.

xor a
ld (IN_BUFFER+12),a ;Campo TTL
ld hl,(IN_BUFFER+28) ;Long. UDP en el campo checksum IP
ld (IN_BUFFER+14),hl

ld hl,(IN_BUFFER+6)
ld bc,12 ;Para que incluya pseudo-cabecera
add hl,bc
push hl
pop bc
ld ix,IN_BUFFER+12
call CALC_CHKSUM
ld a,d
or e
jp nz,END_GET_PACK
OK_UDP_CHKSUM: ;

;--- Checksum correcto: comprobamos si es una respuesta DNS
; o un paquete DHCP, en ese caso se salta al codigo
; de procesamiento adecuado

ld a,(IN_BUFFER+26)
ld h,a
ld a,(IN_BUFFER+27)
ld l,a

ld de,DNS_PORT
call COMP
jp z,IS_DNS

ld de,68
call COMP
jp z,IS_DHCP

;--- Comprobamos que se puede almacenar el paquete

ld hl,(IN_BUFFER+6) ;Paquete demasiado grande?
ld de,548+8+1
call COMP
jp c,END_GET_PACK

ld a,(UDPI_PINDEX)
ld b,a

inc b
ld ix,UDPI_IP0-10
ld de,10
BUC_UDP1: add ix,de
djnz BUC_UDP1 ;Ahora IX apunta a la zona de datos adecuada

ld a,(UDPI_PINDEX)
ld b,a
ld a,(UDPI_GINDEX)
cp b
jr nz,UDPI_HAYSITIO

ld a,(ix) ;Indice GET = Indice PUT:
or (ix+1) ;Hay sitio si entrada vacia (IP=0),
or (ix+2) ;si no, terminamos.
or (ix+3)
jp nz,END_GET_PACK
UDPI_HAYSITIO: ;

;--- Almacenamos paquete

ld hl,(IN_BUFFER+16) ;IP origen
ld (ix),l
ld (ix+1),h
ld hl,(IN_BUFFER+18)
ld (ix+2),l
ld (ix+3),h
ld hl,(IN_BUFFER+24) ;Puerto origen (little endian)
ld (ix+4),h
ld (ix+5),l
ld hl,(IN_BUFFER+26) ;Puerto destino (little endian)
ld (ix+6),h
ld (ix+7),l
ld hl,(IN_BUFFER+6) ;Longitud datos UDP
ld bc,8
or a
sbc hl,bc
ld (ix+8),l
ld (ix+9),h

ld a,h ;Copia la parte de datos
or l ;si no tiene long 0
jr z,OK_UDPIDATA

push hl
ld hl,UDP_BUFFERS-512
ld de,512
ld a,(UDPI_PINDEX)
inc a
ld b,a
UDPLEN_LOP1: add hl,de
djnz UDPLEN_LOP1
ex de,hl
ld hl,IN_BUFFER+32
pop bc
ldir
OK_UDPIDATA: ;

ld hl,UDPI_PINDEX
call INC_UDPINDEX ;Actualiza indice para el proximo paquete
jp END_GET_PACK


;==============================
;=== Paquete DNS recibido ===
;==============================

IS_DNS: ;--- Comprueba si hay una peticion en curso
; y si coincide el ID con el ultimo enviado,
; si no, descarta el paquete

ld a,(DNS_STAT_P)
cp 1
jp nz,END_GET_PACK

ld hl,(IN_BUFFER+32)
ld de,(ID_DNS)
call COMP
jp nz,END_GET_PACK

;--- Si la respuesta esta truncada, ponemos error 21
; a no ser que podamos reintentar con el DNS secundario.

ld a,(IN_BUFFER+34)
and %00000010
jr z,DNS_NOTRUNC

call DNS_USE_SEC
jp nc,END_GET_PACK

ld a,3
ld (DNS_STAT_P),a
ld a,21
ld (DNS_STAT_S),a
jp END_GET_PACK
DNS_NOTRUNC: ;

;--- Si la respuesta contenia algun error,
; lo establecemos, a no ser que podamos reintentar
; con el DNS secundario.

ld a,(IN_BUFFER+35)
and %00001111
jr z,DNS_NOERR

call DNS_USE_SEC
jp nc,END_GET_PACK

ld a,3
ld (DNS_STAT_P),a
ld a,(IN_BUFFER+35)
and %00001111
ld (DNS_STAT_S),a
jp END_GET_PACK
DNS_NOERR: ;

;--- La respuesta no contiene error.
; Comprobamos si contiene alguna respuesta valida.

ld ix,IN_BUFFER+32 ;Apunta al ppio del paquete DNS

ld h,(ix+6)
ld l,(ix+7)
ld (ANCOUNT),hl ;Numero de respuestas
ld h,(ix+8)
ld l,(ix+9)
ld (NSCOUNT),hl ;Numero de servidores autoritativos
ld h,(ix+10)
ld l,(ix+11)
ld (ARCOUNT),hl ;Numero de secciones adicionales

ld ix,IN_BUFFER+44 ;IX=Inicio de la pregunta

SKIPQ_LOOP: ld a,(ix) ;Se salta la pregunta
inc ix ;(campo QNAME)
or a ;comprobando si hay compresion
jr z,SKIPQ_LOOP3
bit 7,a
jr z,SKIPQ_LOOP
SKIPQ_LOOP2: inc ix ;Se salta QTYPE y QCLASS
SKIPQ_LOOP3: inc ix ;(mas el segundo byte del puntero
inc ix ;si es necesario)
inc ix
inc ix ;Ahora IX apunta a las respuestas

;* Comprobamos si se nos da directamente la respuesta

SCAN_FOR_AN: ld bc,(ANCOUNT)
ld a,b
or c
jr z,SCAN_FOR_NS
call SCAN_DNS_RR
or a
jr z,SCAN_FOR_NS

ld a,2 ;Si habia una respuesta valida,
ld (DNS_STAT_P),a ;ponemos status=2 y terminamos
xor a
ld (DNS_STAT_S),a
jp END_GET_PACK

;* Si no, comprobamos si hay IPs de otros servidores
; en "Authoritative" o en "Aditional"

SCAN_FOR_NS: ld bc,(NSCOUNT)
ld a,b
or c
jr z,SCANNS_FAILED ;Seccion NS vacia?

call SCAN_DNS_RR ;Busca IPs de servidores
or a ;en "authoritative"
jp nz,CHANGE_DNS_IP

ld bc,(ARCOUNT)
ld a,b
or c
jr z,SCANNS_FAILED ;Seccion AR vacia?

call SCAN_DNS_RR ;Busca IPs de servidores
or a ;en "additional"
jp nz,CHANGE_DNS_IP

SCANNS_FAILED: ld a,3 ;Si no hay nada en NS ni AR, error 20
ld (DNS_STAT_P),a
ld a,20
ld (DNS_STAT_S),a
jp END_GET_PACK

;* Se ha encontrado la IP de otro DNS:
; Repetimos la consulta usandola

CHANGE_DNS_IP: ld hl,DNS_RESULT
ld de,DNS_IP ;Establecemos nueva direccion
ld bc,4 ;del DNS a consultar
ldir

ld hl,(ID_DNS) ;Incrementamos identificador
inc hl
ld (ID_DNS),hl

ld a,3 ;Ponemos estado secundario a 3
ld (DNS_STAT_S),a
xor a ;Inicializamos reintentos
ld (DNS_RETRY),a
inc a
ld (DNS_TOUT),a ;Para que se reenvie inmediatamente

jp END_GET_PACK


;--- Esta subrutina examina la zona a la que apunta IX
; y busca un RR del tipo "Direccion IP".
; Si lo encuentra, pone la IP en DNS_REPLY
; y DNS_RESP_FLAG a #FF (que devuelve en A).
; Al final, IX apunta a la siguiente zona.
;
; Entrada: BC = Numero de RRs en la zona.

SCAN_DNS_RR: xor a
ld (DNS_RESP_FLAG),a

DNS_AN_LOOP: push bc
SKIPQ_LOOP4: ld a,(ix) ;Se salta el nombre
inc ix ;comprobando si esta comprimido
or a
jr z,SKIPQ_LOOP6
bit 7,a
jr z,SKIPQ_LOOP4
SKIPQ_LOOP5: inc ix
SKIPQ_LOOP6: ;

ld a,(DNS_RESP_FLAG) ;Si ya hay una respuesta
or a ;valida, simplemente se salta
jr nz,DNS_AN_LOOP2 ;el RR

;* Comprueba que el tipo sea "Direccion IP"

ld h,(ix) ;IX apunta a TYPE
ld l,(ix+1)
ld de,1
call COMP
jr nz,DNS_AN_LOOP2

;* Se ha encontrado respuesta:
; se copia a DNS_RESULT

ld l,(ix+10)
ld h,(ix+11)
ld e,(ix+12)
ld d,(ix+13)
ld (DNS_RESULT),hl
ld (DNS_RESULT+2),de

ld a,#FF
ld (DNS_RESP_FLAG),a

;* Pasa al siguiente RR

DNS_AN_LOOP2: ld bc,10
add ix,bc ;Para que apunte a RDATA
ld b,(ix-2)
ld c,(ix-1) ;BC = RDLENGTH
add ix,bc

;* Si quedan RRs, vuelve a empezar

pop bc
dec bc
ld a,b
or c
jr nz,DNS_AN_LOOP

ld a,(DNS_RESP_FLAG)
ret

DNS_RESP_FLAG: db 0 ;#FF cuando se encuentra respuesta


;--- Esta subrutina es llamada cuando se recibe un paquete
; DNS erroneo o se consumen todos los reenvios.
; Lo que hace es comprobar si el DNS
; usado era el primario y hay un secundario disponible.
; Si es asi, establece DNS_IP con la direccion
; del DNS secundario, pone DNS_STAT_S a 2
; y DNS_RETRY a 0 (es decir, lo prepara todo
; para repetir la consulta usando el DNS secundario),
; y devuelve Cy=0.
; En caso contrario, devuelve Cy=1 (error).

DNS_USE_SEC: ld a,(DNS_STAT_S)
cp 1
scf
ret nz ;Si no era el primario

ld ix,BUF_IPDNS1
ld a,(ix+4)
or (ix+5)
or (ix+6)
or (ix+7)
scf
ret z ;Si era el primario pero no hay sec.

ld hl,BUF_IPDNS2 ;Establece secundario
ld de,DNS_IP ;y reinicia contador de
ld bc,4 ;reenvios
ldir
ld a,2
ld (DNS_STAT_S),a
xor a ;De paso pone Cy=0
ld (DNS_RETRY),a
inc a
ld (DNS_TOUT),a ;Para que se repita el envio inmediatamente
ret


;===============================
;=== Paquete DHCP recibido ===
;===============================

IS_DHCP: ld a,(DHCP_VECT) ;Ignorar si no usamos DHCP
or a
jp z,END_GET_PACK

ld a,(DHCP_STATE)
cp CONFIGURED
jp z,END_GET_PACK

;--- Obtiene el tipo del paquete

ld a,(IN_BUFFER+32) ;Es BOOTREPLY?
cp 2
jp nz,END_GET_PACK

ld hl,IN_BUFFER+36 ;El 'xid' coincide?
ld de,DHCP_XID
call COMP32
jp nz,END_GET_PACK

call DHCP_GET_TYPE
cp DHCPOFFER
jr z,IS_DHCP_OFFER
cp DHCPACK
jr z,IS_DHCP_ACK
cp DHCPNAK
jp z,IS_DHCP_NAK
jp END_GET_PACK ;Otros tipos son ignorados


;--- Paquete DHCPOFFER

IS_DHCP_OFFER:

;* Si el estado no es SELECTING, ignorarlo

ld a,(DHCP_STATE)
cp SELECTING
jp nz,END_GET_PACK

;* Guarda el identificador del servidor

call DHCP_GET_SERVER
jp c,END_GET_PACK
ld de,DHCP_SERVER
ld bc,4
ldir

;* Guarda el campo 'yiaddr' como la IP ofrecida

ld hl,IN_BUFFER+48
ld de,DHCP_YIADDR
ld bc,4
ldir

;* Guarda el 'xid' recibido

ld hl,IN_BUFFER+36
ld de,DHCP_RCVXID
ld bc,4
ldir

;* Pasa a estado REQUESTING y envia DHCPREQUEST

ld a,REQUESTING
ld (DHCP_STATE),a
ld a,DHCPREQUEST
call SEND_DHCP
call DHCP_FIRST
jp END_GET_PACK


;--- Paquete ACK

IS_DHCP_ACK:

;* Si el estado no es REBINDING, REQUESTING, RENEWING
; o INFORMING, ignorarlo

ISDHCPACK2: cp REQUESTING
jr z,ISDHCPACK3
cp RENEWING
jr z,ISDHCPACK3
cp REBINDING
jr z,ISDHCPACK3
cp INFORMING
jp nz,END_GET_PACK
ISDHCPACK3:

;* Si el identificador del servidor no coincide con el anterior,
; ignorar el mensaje
; (Problema: Que pasa si el ACK ha sido recibido en estado REBINDING,
; por un servidor distinto al usado anteriormente?
; Por eso, nos fiaremos unicamente del XID)

;call DHCP_GET_SERVER
;jp c,END_GET_PACK
;ld de,DHCP_SERVER
;call COMP32
;jp nz,END_GET_PACK

;* Inicializa los campos Lease, T1 y T2

ld hl,DHCP_T1
ld de,DHCP_T1+1
ld bc,12-1
ld (hl),0
ldir

;* Guarda el campo 'yiaddr' como la IP asignada,
; a no ser que hubieramos enviado DHCPINFORM

ld a,(DHCP_VECT)
and 1
jr z,ISDHCPACK4

ld hl,IN_BUFFER+48
ld de,BUF_IPLOCAL
ld bc,4
ldir
ld a,1
ld (DHCP_VECT_O),a
ISDHCPACK4:

;* Recorre todas las opciones y las procesa

call DHCP_INIT_OP

ISDHCPACKL: call DHCP_NEXT_OP
or a
jp z,ISDHCPACKEND ;No quedan opciones?

;* T1: Lo almacena

cp 58
jr nz,ISDHCPACK_NOT1

push ix
pop hl
ld de,DHCP_T1
ld bc,4
ldir

ld ix,DHCP_T1
call POR60_32
jr ISDHCPACKL
ISDHCPACK_NOT1:

;* T2: Lo almacena

cp 59
jr nz,ISDHCPACK_NOT2

push ix
pop hl
ld de,DHCP_T2
ld bc,4
ldir

ld ix,DHCP_T2
call POR60_32
jr ISDHCPACKL
ISDHCPACK_NOT2:

;* Lease: Lo almacena

cp 51
jr nz,ISDHCPACK_NOLS

push ix
pop hl
ld de,DHCP_LEASE
ld bc,4
ldir

ld ix,DHCP_LEASE
call POR60_32
jr ISDHCPACKL
ISDHCPACK_NOLS:

;* Mascara de subred: la almacena si la habiamos pedido

cp 1
jr nz,ISDHCPACK_NOSB

ld a,(DHCP_VECT)
and %10 ;La habiamos pedido?
jr z,ISDHCPACKL

push ix
pop hl
ld de,SUBNET_MASK
ld bc,4
ldir

ld a,(DHCP_VECT_O)
or %10
ld (DHCP_VECT_O),a
jr ISDHCPACKL
ISDHCPACK_NOSB:

;* Gateway por defecto: lo almacena si lo habiamos pedido

cp 3
jr nz,ISDHCPACK_NOGW

ld a,(DHCP_VECT)
and %100 ;La habiamos pedido?
jr z,ISDHCPACKL

push ix
pop hl
ld de,DEFGW
ld bc,4
ldir

ld a,(DHCP_VECT_O)
or %100
ld (DHCP_VECT_O),a
jp ISDHCPACKL
ISDHCPACK_NOGW:

;* Servidores DNS: los almacena si los habiamos pedido

cp 6
jr nz,ISDHCPACK_NODN

ld a,(DHCP_VECT)
and %1000 ;Los habiamos pedido?
jp z,ISDHCPACKL

ld a,(DHCP_VECT_O)
or %1000
ld (DHCP_VECT_O),a

push bc
push ix
pop hl
ld de,BUF_IPDNS1
ld bc,4
ldir

pop bc ;Hay mas de un DNS en la opcion?
ld a,b
cp 8
jp c,ISDHCPACKL
ld de,BUF_IPDNS2
ld bc,4
ldir
jp ISDHCPACKL
ISDHCPACK_NODN:

;* Timeout ARP: lo almacena si lo habiamos pedido

cp 35
jr nz,ISDHCPACK_NOAT

ld a,(DHCP_VECT)
and %10000 ;Lo habiamos pedido?
jp z,ISDHCPACKL

push ix,ix
pop hl
ld de,ARP_TOUT
ld bc,4
ldir
pop hl
ld de,ARP_TOUT_SECS
ld bc,4
ldir
ld ix,ARP_TOUT
call POR60_32

ld a,(DHCP_VECT_O)
or %10000
ld (DHCP_VECT_O),a
jp ISDHCPACKL
ISDHCPACK_NOAT:

;* Tipo de trama ethernet: lo almacena si lo habiamos pedido

cp 36
jr nz,ISDHCPACK_NOFT

ld a,(DHCP_VECT)
and %100000 ;Lo habiamos pedido?
jp z,ISDHCPACKL

ld a,(ix)
or a
jr z,ISDHCPACK_FT
ld a,#FF
ISDHCPACK_FT: ld (FRAME_TYPE),a

ld a,(DHCP_VECT_O)
or %100000
ld (DHCP_VECT_O),a
;jp ISDHCPACKL
ISDHCPACK_NOFT:

;* Opcion no reconocida: ignorarla

jp ISDHCPACKL
ISDHCPACKEND:

;* Fin de las opciones.
; Si estamos en INFORMING, pasar a CONFIGURED y terminar.

ld a,(DHCP_STATE)
cp INFORMING
jr nz,ISDHCPACKEND0

ld a,CONFIGURED
ld (DHCP_STATE),a
jp END_GET_PACK
ISDHCPACKEND0:

;* Si T1 es 0, establecerlo a lease/2

ld hl,DHCP_T1
ld de,ZERO32
call COMP32
jr nz,OKT1NZ

ld hl,#FFFF ;Si lease infinito, T1 infinito
ld (DHCP_T1),hl
ld (DHCP_T1+2),hl
ld a,(DHCP_LEASE)
cp h
jr z,OKT1NZ

ld hl,DHCP_LEASE
ld de,DHCP_T1
ld bc,4
ldir
ld ix,DHCP_T1
ld b,1
call ENTRE2_32
OKT1NZ:

;* Si T2 es 0, establecerlo a 0.875*lease ((7/8)*lease)

ld hl,DHCP_T2
ld de,ZERO32
call COMP32
jr nz,OKT2NZ

ld hl,#FFFF ;Si lease infinito, T2 infinito
ld (DHCP_T2),hl
ld (DHCP_T2+2),hl
ld a,(DHCP_LEASE)
cp h
jr z,OKT2NZ

ld hl,DHCP_LEASE
ld de,DHCP_T2
ld bc,4
ldir

ld ix,DHCP_T2 ;Divide T2 entre 8
ld b,3
call ENTRE2_32

ld hl,DHCP_T2
ld de,IN_BUFFER
ld bc,4
ldir

ld b,6
T2ISZL: push bc ;Multiplica (T2/8) por 7
ld hl,IN_BUFFER
ld de,DHCP_T2
ld bc,DHCP_T2
call ADD32
pop bc
djnz T2ISZL
OKT2NZ:

;* Fin: pasa a BOUND y termina

ld a,BOUND
ld (DHCP_STATE),a
jp END_GET_PACK


;--- Paquete NAK

IS_DHCP_NAK:

;* En estado BOUND, SELECTING, INFORMING o CONFIGURED, ignorarlo

ld a,(DHCP_STATE)
cp BOUND
jp z,END_GET_PACK
cp SELECTING
jp z,END_GET_PACK
cp INFORMING
jp z,END_GET_PACK
cp CONFIGURED
jp z,END_GET_PACK

;* En otros estados, volver a INIT

xor a ;ld a,INIT
ld (DHCP_STATE),a
jp END_GET_PACK


;===============================
;=== Segmento TCP recibido ===
;===============================

;Despues de la publicidad.

NOTA: Si no sabes qué es esto, no eres un friki auténtico. Háztelo mirar.

22 mayo 2007

¿Coincidencia? No lo creo...

Se abre el telón y estamos a domingo 20 de mayo. El friki me llama por fonófono para decirme que ponga la tele, pues en cierto canal están emitiendo un programa en el que aparece Parque España, un pintoresco lugar de Japón sobre el que ya escribí algo. Nos echamos unas risas y se cierra el telón.

Se vuelve a abrir el telón y estamos a martes 22 de mayo. Me da por comprobar las palabras que la gente estaba buscando cuando encontró mi blog, y me encuentro esto:


Se cierra el telón y pregunto: ¿seguro que la gente ya no ve tanto la tele por culpa del interdem?

17 mayo 2007

Especial Kaito

En esta ocasión voy a llevar a cabo un experimento blogológico: una entrada sin texto, sólo con fotos y pies de idem. Así pus, olvida que has leído esta frase, y deléitate con el siguiente batiburrillo de píxeles coloreados a mayor gloria del señor Soriano Koizumi.


- ¡Arriad las velas! ¡Trincad el foque! ¡Descogorciad el birlibirloque!


Somnífero infalible: recitar en verso el código fuente de NestorBASIC.


- ¡Teneis que creerme! ¡Era una visión horrible! ¡De verdad, he visto a mi padre desnudo! ¡¡No estoy loco, maldita sea!!


- Vito, no me fío un pelo de ese tipo. Encárgate de que tenga un desgraciado accidente.
- Sí, capo.


Prueba irrefutable de que, digan lo que digan, los niños vienen de Carrefúl.


- El Emperador y su Enchufe Sagrado se dignarán recibirle en audiencia.


- Y ahora, el gran fakir Ka-i-toh se tragará una auténtica cuchara de plástico.


GAME OVER. PRESS F5 TO CONTINUE.


- ¿Y tú qué miras? ¿Quieres pelea?


- No se preocupe, señor, despistaré a los paparachis en un momento.


- ¡SOCORRO! ¡Que me violan!


¡HASTA LA VICTORIA SIEMPRE!

11 mayo 2007

¡Que alguien subtitule esto!

He aquí un documento histórico de importancia capital hallado en YouTube. Año 1984, toda Inglaterra está dominada por Sir Clive Sinclair y su ejército de Spectrums. ¿Toda? ¡No! Un par de jovencísimos emprendedores llamados Bill Gates y Kazuhiko Nishi presentan el estándar MSX y explican sus bondades. Don Sinclair por su parte, da su opinión sobre las susodichas maquinitas.

Podeis ver el video cataclackando aquí, que no dejan empotrarlo vaya usté a saber por qué.

Sería muy interesante que alguien versado en la lengua shakesperiana hablada y lo suficientemente dotado de tiempo libre se currara una versión subtitulada al castillero, o al menos un resumen de los monólogos en forma de texto asciiano. Sobre todo por saber qué dice don Sinclair, pues aunque no pillo su impecable británico, parece estar diciendo de todo menos bonito.

Nota: Vaya pintas...

- Sí, sí, ríete de mis pintas... dentro de veinte años comparamos nuestras cuentas corrientes y a ver quién se ríe...


- Bueno, teniendo en cuenta que El Líder esta cursando cuarto de EGB y casi no sabe ni lo que es una máquina de escribir, creo que aún tendremos que esperar unos cuantos años para que salga el Dumas ese... mientras, podríamos ir inventando el USB o algo.


- ¿Pero qué mierda es esta? ¡Si ni siquiera se mezclan los colores! ¿Y qué pijada es esa de los cartuchos? ¡Donde esté una buena cinta...! ¿Qué se habrán creído estos melenudos?


- A mí dejadme en paz. Paso de frikadas.

10 mayo 2007

Microsoft no se fía de Microsoft

Resumiendo: Visual Studio bloquea contenido "potencialmente inseguro" generado por... Visual Studio. (Cataclack en la imagen para verla a tamaño más more)


¿Ataque de sinceridad, traición del subconsciente, seguridad paranoide? Haga click en "Opciones" para más idems.

(Y por cierto, esa ventana de "varios errores" no tenía ninguna razón de ser y desapareció al reiniciar todo el tinglado)

03 mayo 2007

Japón 2001

Aviso: Esta entrada contiene exactamente 34 fotos.

Dadas las amenazas de muerte, emails-bomba y otras amables sugerencias recibidas por parte de mis más acérrimos visitantes, me veo en la obligación moral de actualizar este vuestro blog favorito. Pero como no consigo hallar ningún tema interesante sobre el que verter lexemas, recurro de nuevo al rastrero socorrido truco de endosaros deleitaros con unas estúpidas impactantes instantáneas tomadas en Japón durante mi primer viaje a tan maravillosa tierra, en agosto de 2001.

Aclaro desde ya que no vereis las típicas estampas del tipo japonesa vestida con kimono jugando a las maquinitas o paisaje de cerezos en flor junto a edificios de 300 pisos, para eso ya está esta gente. Lo que aquí vereis es una recopilación de las fotos más raras y/o curiosas (según mi infalible criterio) que hice en esas calendas. Si os gusta, bien, y si no, DEFUSR=0: A=USR(0)

Empezamos, pues, por el principio, que no es sino el extremo contrario del final:

- ¡He encontrado a Wally! ¡Se escondía en Japón disfrazado de estatua!


- Deme un viento de poniente con componente norte en el estrecho.
- Está agotado, pero tenemos tifones tropicales que son cosa fina.


Cuando juegas al Gradius en una pantalla de 10000 pulgadas, puedes ver con todo detalle el interior de Vic Viper, y resulta ser una cosa así.


Al dueño de la cafetería le hizo mucha gracia la interjección proferida por los clientes españoles cuando se les hacía entrega de la cuenta, y decidió cambiar el nombre del negocio.


¿Ahorro energético? ¿Eso qué es?


Los japoneses respetan profundamente el santoral. Aquí tenemos homenajes a San Nomiya y a San Tica.


Restos arqueológicos recientemente hallados demuestran que los japoneses ya tenían robots en la edad de piedra.


Si no le ves la gracia a esta foto, no eres un usuario auténtico de MSX. Háztelo mirar.


¡Huyyy! Qué cerca se han quedado. Han fallado por uno.


¡Qué bien lucías en el oasis del recreo!


- Eres un friki. Muy friki. Superfriki.
- ¡De eso nada! ¡MEGAFRIKI y a mucha honra!


Las llaves inglesas no pueden fumar aquí.


Ladrones: tened cuidado con las señoras.


Teclado de Hello Kitty. Demasiado para mi intelecto. Por favor, dejad un chiste apropiado en los comentarios.


- Quiero que pongais un gato grande. ¡Y que se note que es GRANDE!


Para jugar a videojuegos en Japón hay que tener un master en filosofía.


Salpicadero del monovolumen de mi señor suegro. Ignoro si se necesita licencia de piloto para conducirlo.


El premio de esta maquinita es... un traje de enfermera. Ideal para quienes tengan vocación médica, supongo (por supuesto, no imagino qué otro uso podría tener algo así)


- Elemental, querido guasón: a ti lo que te pasa es que te estás quedando calvo.


- Entonces, ¿dónde quedamos?
- Enfrente de ¡Olé!
- Deja de ver los toros y dime ya dónde quedamos.


Tierna niñita en la fiesta de disfraces del colegio. Vamos, digo yo.


Al dueño del restaurante le hizo mucha gracia la interjección proferida por los clientes españoles cuando veían que en la mesa había palillos en vez de tenedor, y decidió cambiar el nombre del negocio.


- He venido a Japón para ver coches supermodernos, y me encuentro con esto...


Restos arqueológicos recientemente hallados demuestran que los japoneses ya tenían coches tuneados en la edad de piedra.


Esto es un todo a 100 y lo demás son tonterías.


- ¿Que tienes un antojo de churrasco? ¡Sí claro, y aquí en mitad de Japón vamos a encontrar una churrasquería! ¡Lo flipas!


Al dueño del restaurante le hizo mucha gracia... bueno, mejor dejémoslo ahí.


Pues sí, parece que Mario Olivetti ha pasado por Japón.


Este autobús es tan limpio que no sólo no contamina, sino que además le crecen flores encima. Esto sí que es ecología aplicada.


Esto es lo que pasa por no saber japonés y entrar en sitios aleatoriamente.


¡Alfonso Arús tiene un primo japonés!

¿Y dónde está el "Alma de Sumo" en España? ¿Eh?


Esta foto sólo es para que babeen los más frikis del lugar.


Y colorín colorado, aquí paz y después gloria, apaga y vámonos, y todo eso.

Nota: Lo de las amenazas que os comentaba al principio es cierto. He aquí la que me dió más miedo:

- ¡Que actualices ya, cooneh! ¡Que te ví a meté dos yoyas que te ví a volver del revés!