viernes, abril 14, 2017

Calcular la letra del DNI ... en Locomotive Basic (CPC)

Reconozco que esto es una frikada. Pero el otro día venía dándole vueltas, y pensé: ¿Implementar el algoritmo de cálculo de DNI en Locomotive Basic, el intérprete de Basic que viene con los ordenadores Amstrad de la serie CPC. El algoritmo es muy sencillo: Hay que calcular el resto de la división del número de DNI y 23. Dicho valor sirve para indexar la siguiente tabla y obtener la letra del DNI.

T R W A G M Y F P D X
0 1 2 3 4 5 6 7 8 9 10
B N J Z S Q V H L C K E
1112 13 14 15 16 17 18 19 20 21 22

Hacer esta operación en un ordenador que soporte división entera de 32 bits es muy sencillo. Mi pequeño reto es hacerlo en BASIC del CPC. Esto da para varias limitaciones que hay que tener en cuenta.:

  • No se puede usar aritmética en coma flotante
  • El Locomotive Basic sólo tiene aritmética entera de 16 bits en complemento a 2
  • No quería usar una rutina en código máquina

Tras cerca de 25 años sin tocar el Basic de Amstrad, programando en lenguajes estructurados, tiene su aquel volver a usar números de línea, el goto, colocar el cursor en la pantalla,... e implementar una simple división entre 23 - aquí sí, podemos usar la división entera del Locomotive Basic, ya que como mucho el número mayor a dividir será 207 - y diseñar el pequeño programita en Basic con la siguiente captura y el listado del programa

Y este es el listado en Locomotive Basic que lo genera

10 ' Calcula la letra de DNI
20 MODE 1
30 t$="Letra de dni"
40 msg1$="Pulsa q para salir"
50 msg2$="Pulsa c para borrar"
60 msg3$="Pulsa n para nuevo"
70 dl$="TRWAGMYFPDXBNJZSQVHLCKE"
80 tlen%=LEN(t$)
90 xp=(20-tlen%)/2
100 LOCATE xp,1
110 PEN 1
120 PRINT t$
130 '
140 PEN 2
150 LOCATE 1,3
160 st$="DNI: " + STRING$(8,CHR$(127))
170 ldni%=LEN(st$)
180 PRINT st$;
190 PEN 3
200 LOCATE ldni% + 2, 3
210 PRINT CHR$(127);
220 PEN 1
230 LOCATE 1,5:PRINT msg1$;
240 LOCATE 1,6:PRINT msg2$;
250 ' Bucle principal
260 P%=0
270 dni$=""
280 PEN 1
290 WHILE p%<8
300 k$=INKEY$
310 IF k$="" GOTO 300
320 IF k$="q" THEN 640
330 IF k$="c" THEN 140
340 IF ASC(k$) < ASC("0") OR ASC(k$)>ASC("9") THEN PRINT CHR$(7): GOTO 300
350 p%=p% + 1
360 dni$ = dni$ + k$
370 LOCATE 5 + p%,3
380 PRINT k$;
390 WEND
400 inx%=3
410 div%=(ASC(MID$(dni$,1,1))-ASC("0"))*10
420 div%=div% + (ASC(MID$(dni$,2,1))-ASC("0"))
430 ' Este bucle funciona porque vamos
440 ' diviendo modulo 23
450 WHILE inx% < 9
460 q% = div% \ 23
470 IF q% <> 0 THEN div% = div% MOD 23
480 div%=div%*10
490 div% = div% + (ASC(MID$(dni$,inx%,1))-ASC("0"))
500 inx%=inx%+1
510 WEND
520 r% = (div% MOD 23) + 1
530 LOCATE ldni%+2,3
540 PEN 3
550 PRINT MID$(dl$,r%,1);
560 LOCATE 1,6:PRINT SPACE$(LEN(msg2$))
570 LOCATE 1,6:PRINT msg3$;
580 K$=INKEY$
590 IF K$="" THEN 580
600 IF K$="q" THEN 640
610 IF K$="n" THEN LOCATE 1,6:PRINT SPACE$(LEN(msg3$)):GOTO 130
620 GOTO 580
630 ' Clear screen
640 PEN 1
650 MODE 1
660 END

Supongo que el siguiente reto será hacerlo en ensamblador de Z80A y Amstrad ;). Veremos si tengo un hueco.

No hay comentarios: