Z80-Assembler para MSX

dancresp
Mensajes: 5009
Registrado: 13 Nov 2010 02:08
Agradecido : 14 veces
Agradecimiento recibido: 85 veces

Z80-Assembler para MSX

Mensajepor dancresp » 02 May 2014 21:44

z80_2.gif
z80_2.gif (62.54 KiB) Visto 2288 veces


EL PROGRAMA
Este ensamblador es un completo entorno de desarrollo para el microprocesador Z80 que incorpora un editor y traduce el 100% de sus mnemotécnicos a código máquina.

Como funciona:
Al ejecutar el programa entramos en el modo “prompt” donde podemos entrar una de las siguientes opciones con parámetros opcionales separados por una coma o espacio:

E [línea]
Introducir líneas a partir de la línea indicada o a partir de la última línea introducida.
El editor no permite editar una línea ya introducida. Se debe reescribir completamente.

I desde , nºlíneas
Inserta un número de líneas en blanco a partir de la línea indicada. Las líneas existentes se desplazan hacia abajo.

D desde [hasta]
Borra las líneas indicadas. Las líneas existentes se desplazan hacia arriba.

L [desde] [hasta]
Permite listar por pantalla todo el programa, la línea indicado o desde una línea hasta otra.

P [desde] [hasta]
Permite listar por impresora todo el programa, la línea indicado o desde una línea hasta otra.

A [opción]
Ensambla el programa con la opción indicada. Por defecto el ensamblado se realiza en dos pasadas.
“opción = 1” solo realiza la primera pasada, que calcula la dirección de las etiquetas y ensambla las líneas que no las usan.
“opción = 2” solo realiza la segunda pasada y no verifica la dirección de las etiquetas. Es más rápido.

E [dirección]
Ejecuta el programa desde la posición por defecto del programa (58000), el indicado por ORG o el indicado por el parámetro “dirección”.

S [nombre]
Graba el programa fuente con el nombre indicado o el último nombre de fichero usado. El nombre no puede llevar la extensión y por defecto se pone “.ASM”. El nombre de fichero por defecto es “SOURCE.ASM”.

O [nombre]
Carga el programa fuente con el nombre indicado o el último nombre de fichero usado. El nombre no puede llevar la extensión y por defecto se pone “.ASM”. El nombre de fichero por defecto es “SOURCE.ASM”.

F
Muestra por pantalla los ficheros con extensión “.ASM”.

W columnas
Se especifica el número de columnas usado en pantalla, que puede estar entre 32 y 80. Por defecto se usan 38.

Q
Salir del ensamblador. Se puede volver a entrar al ensamblador en frío con “RUN” o en caliente con “GOTO 700”.
Si se modifica el programa BASIC la vuelta al ensamblador se realizará siempre en frío.


Características del ensamblador:
Lo primero y más importante: todos los comandos e instrucciones se deben introducir usando mayúsculas.

El editor admite la introducción de hasta 250 líneas de programa, que puede ser ampliado modificando el valor de la variable H% y de la matriz L$(200,3) de la línea 100.

El editor admite la definición de hasta 50 etiquetas. Este valor puede ser ampliado modificando los valores de las matrices Q$(50) y M(50), y el “Q%<50” de la línea 174.

Las líneas no se pueden tabular y no pueden contener más espacios que el que hay entre la instrucción y sus parámetros. Los parámetros pueden ir separados por un espacio o una coma.

Los valores hexadecimales se introducen con un símbolo $ delante. De todas formas se recomienda el uso de valores decimales porque los hexadecimales no son admitidos en algunos casos que se indican más adelante.

Las etiquetas se definen con un nombre de hasta 8 letras y siempre empiezan con dos puntos. No puede haber una etiqueta y una instrucción en la misma línea.

No se admiten comentarios en el código fuente.

Por defecto el programa en código máquina se carga a partir de la dirección 58000. Para modificar este valor se debe usar la directiva “ORG dirección” siendo el valor de la dirección un número decimal o hexadecimal.

Para cargar valores numéricos en una determinada dirección de memoria se pueden usar las directivas DB (un byte) y DW (dos bytes). Si se introducen listas de números se debe usar “DB” y los valores se deben separar con comas y estar en formato decimal. Si se introduce una cadena de caracteres debe estar comprendida entre unas comillas simples. No se pueden mezclar listas de números y cadenas en la misma línea.

Todas las pruebas se han realizado en un equipo con disquetera. Para usar el ensamblador con cintas de casete se debe introducir CAS: delante del nombre. De todas formas se recomienda el usado de disquetera.

Para guardar el programa en código máquina generado durante el ensamblado es necesario el uso del comando “BSAVE” desde el BASIC, saliendo del ensamblador con “Q”.

Descargar el programa en formato "DSK":
z80asm.rar
(4.99 KiB) Descargado 105 veces


Descargar el programa en formato "WAV": (3.34 MB)
Z80-Assembler.rar


BLOQUES
He dividido el listado en 5 grandes bloques:

- Análisis de la línea a ensamblar.
- Introducción en memoria del código ensamblado.
- Colección de algoritmos para ensamblar las distintas instrucciones.
- Menú de opciones.
- Rutinas del entorno de desarrollo.


COMO FUNCIONA
Todo el programa ocupa un total de 179 líneas.
A continuación detallo las distintas secciones del programa.

100 – Declaración de matrices del ensamblador.
110 – Inicio del proceso de ensamblado. Control de las pasadas y números de líneas.
116 – Inicio del analizador de los parámetros de la línea.
118 – Detectar si es un valor numérico decimal o hexadecimal y si lleva paréntesis.
136 – Bucle para identificar el registro usado.
152 – Bucle para identificar la etiqueta usada.
168 – Llamar a la subrutina correspondiente a la instrucción a ensamblar.
174 – En la primera pasada muestra la dirección de memoria de las etiquetas del programa.
178 – En la segunda pasada muestra la dirección de memoria y los valores hexadecimales del ensamblado de la línea.
184 – Bucle que introduce los valores hexadecimales en la memoria.
194 – Final del proceso de ensamblado.
300 – Ensamblado de LD
330 – Ensamblado de PUSH / POP
340 – Ensamblado de INC / DEC
350 – Ensamblado de ADD / ADC / SUB / SBC / AND / XOR / OR / CP
370 – Ensamblado de RET
380 – Ensamblado de CALL / JP
390 – Ensamblado de JR / DJNZ
410 – Ensamblado de BIT / RES / SET
420 – Ensamblado de RLS / RRC / RL / RR / SLA / SRA / SRL
430 – Ensamblado de IN / OUT
440 – Ensamblado de IM
450 – Ensamblado de RST
460 – Ensamblado de EX
470 – Ensamblado de DB / DW
490 – Ensamblado de ORG
700 – Menú principal.
720 – Separar una línea por parámetros.
740 – Comando EDIT (E)
760 – Comando INSERT (I)
780 – Comando DELETE (D)
800 – Comandos LIST (L) y PRINT (P)
820 – Comando SAVE (S)
840 – Comando LOAD (O)
860 – Rutina de PRESSKEY
870 – Comando ASSEMBLE (A)
880 – Comando FILES (F)
890 – Comando EXECUTE (E)
900 – Inicio del ensamblador. Carga de matrices y mensaje en pantalla.
910 – Comando QUIT (Q)
912 – Rutina de salto si hay error.
950 – Líneas DATA con las listas de instrucciones, registros, código de ensamblado y líneas de salto por instrucción.


EL PROGRAMA

Código: Seleccionar todo

     Reservar 4KB para variables de cadena y definición de matrices.
100 CLEAR 4096:H%=250:N$="SOURCE.ASM":DIM I$(70),V$(34),R$(32),P(7),J%(36),P$(5),M(50),L$(250,3),H$(250),Q$(50)
     Inicializar y saltar al prompt.
102 GOSUB900:GOTO700

     Inicio del proceso de Ensamblado
110 IFZ1%=1THENQ%=0
     Control de pasadas de ensamblado
112 FORY%=Z1%TOZ2%
     Dirección de ensamblado por defecto
114 D=58000!:B=D:PRINT"Pass:";Y%
     Bucle de líneas del programa
116 FORZ%=1TOLF%-1
     La línea es una etiqueta?
118 IFLEFT$(L$(Z%,1),1)=":"THENO%=99:E$="":GOTO172
     La línea ya se ha ensamblado?
120 IFH$(Z%)<>""THENO%=0:E$=H$(Z%):GOTO172
     Si la instrucción no lleva parámetros coger su código y ensamblar
122 O%=VAL(L$(Z%,1)):IFO%>36THENE$=V$(O%-36):GOTO172
     Controlar número de parámetros
124 IFL$(Z%,3)=""THENX%=2ELSEX%=3
     Guardar los parámetros y el Id de instrucción
126 P(2)=0:P(3)=0:P(4)=.1:P(5)=.1:E$="":E1$="":E2$="":P$(4)="":P$(5)=""
     Analizar el/los parámetros
128 FORN%=2TOX%
130 R%=0:B$=L$(Z%,N%):IFLEFT$(B$,1)="("THENP(N%+4)=1:A$=MID$(B$,2,LEN(B$)-2)ELSEP(N%+4)=0:A$=B$
     El parámetro es un valor numérico?
132 C$=LEFT$(A$,1):IFC$>="0"ANDC$<="9"THEN160
     El parámetro es un número hexadecimal?
134 IFC$="$"THENA$=MID$(A$,2):P(N%+2)=VAL("&H"+A$):GOTO162
     Detectar registro usado
136 FORF%=1TO32
138 IFB$=R$(F%)THENP(N%)=F%:F%=99
140 NEXT
142 IFP(N%)ORO%=14THEN164ELSEIFP(N%+4)=0THEN150
     Se usa un registro de indice?
144 IFLEFT$(B$,4)="(IX+"THENE1$="DD"
146 IFLEFT$(B$,4)="(IY+"THENE1$="FD"
148 IFE1$<>""THENE2$=RIGHT$("00"+HEX$(VAL(MID$(B$,5))),2):P(N%)=7:GOTO164
     Comprobar si se está usando una etiqueta
150 R%=1:IFY%=1THENA$=STR$(D):GOTO160
152 FORF%=1TOQ%
154 IFQ$(F%)=A$THENA$=STR$(M(F%)):P(N%+2)=M(F%):F%=99
156 NEXT
158 IFP(N%+2)<0THENE$="":GOTO178
160 P(N%+2)=VAL(A$):A$=RIGHT$("000"+HEX$(VAL(A$)),4)
162 P$(N%+2)=RIGHT$(A$,2)+LEFT$(A$,2)
164 NEXTN%
     Guardar los valores de los parámetros
166 R1%=P(2):R2%=P(3):V1=P(4):V2=P(5)
     Saltar a la rutina que ensambla la instrucción
168 ONJ%(O%)GOSUB300,330,340,350,370,380,390,410,420,430,440,450,460,470,490
     Si la línea no usa una etiqueta se guarda el código
170 IFR%=0THENH$(Z%)=E$
     Si es la segunda pasada salta la parte de etiquetas
172 IFY%=2THEN178ELSEIFO%<>99THEN186
174 IFQ%<50THENQ%=Q%+1:Q$(Q%)=MID$(L$(Z%,1),2):M(Q%)=D
     Mostrar la dirección de la etiqueta
176 PRINTUSING"##### = ";D;:PRINTQ$(Q%):GOTO186
     Mostrar el ensamblado de la línea
178 PRINTUSING"### ##### ";Z%;D;:IFLEFT$(L$(Z%,1),1)=":"THENPRINTL$(Z%,1):GOTO188
180 PRINT" ";E$;TAB(21);I$(VAL(L$(Z%,1)));" ";L$(Z%,2);:IFL$(Z%,3)<>""THENPRINT",";L$(Z%,3)ELSEPRINT
     Si no se ha generado código mostrar el error (excepto ORG)
182 IFE$=""THENIFO%<>36THENPRINT"Error in ";Z%:Z%=999:Y%=2:GOSUB860:GOTO188
     Introducir el código generado en memoria
184 IFE$<>""THENFORF%=1TOLEN(E$)-1STEP2:POKED,VAL("&H"+MID$(E$,F%,2)):D=D+1:NEXT:GOTO188
186 D=D+LEN(E$)/2
     Siguiente línea del programa / Siguiente pasada de ensamblado
188 NEXTZ%:PRINT:NEXTY%
     Indicar el tamaño total en bytes del programa ensamblado
194 IFE$<>""THENPRINTUSING"Program length:##### bytes";D-B:RETURNELSERETURN

     Ensamblado de LD
300 IFR1%>0ANDR1%<9ANDR2%>0ANDR2%<9THENE$=E1$+HEX$(56+8*R1%+R2%-1)+E2$:RETURN
302 IFR1%>0ANDR1%<9ANDV2<>.1ANDP(7)=0THENE$=RIGHT$("0"+HEX$(-2+8*R1%),2)+LEFT$(P$(5),2):RETURN
304 IFR1%<>8THEN310ELSEIFV2<>.1ANDP(7)THENE$="3A"+P$(5):RETURN
306 IFR2%=16ORR2%=17THENIFR2%=16THENE$="0A":RETURNELSEE$="1A":RETURN
308 IFR2%=20ORR2%=21THENIFR2%=20THENE$="ED57":RETURNELSEE$="ED5F":RETURN
310 IFR2%<>8THEN316ELSEIFV1<>.1ANDP(6)THENE$="32"+P$(4):RETURN
312 IFR1%=16ORR1%=17THENIFR1%=16THENE$="02":RETURNELSEE$="12":RETURN
314 IFR1%=20ORR1%=21THENIFR1%=20THENE$="ED47":RETURNELSEE$="ED4F":RETURN
316 IFR1%=13ORR1%=14THENIFR1%=13THENE1$="DD":R1%=11ELSEE1$="FD":R1%=11
318 IFR1%>8ANDR1%<15ANDV2<>.1ANDP(7)=0THENE$=E1$+RIGHT$("0"+HEX$(1+16*(R1%-9)),2)+P$(5):RETURN
320 IFR1%>8ANDR1%<15ANDV2<>.1ANDP(7)THENIFR1%<>11THENE$="ED"+HEX$(75+16*(R1%-9))+P$(5):RETURNELSEE$=E1$+"2A"+P$(5):RETURN
322 IFR2%>8ANDR2%<15ANDV1<>.1ANDP(6)THENIFR2%<>11THENE$="ED"+HEX$(67+16*(R2%-9))+P$(4):RETURNELSEE$=E1$+"22"+P$(4):RETURN
324 IFR2%=13ORR2%=14THENIFR2%=13THENE1$="DD":R2%=11ELSEE1$="FD":R2%=11
326 IFR1%=12ANDR2%=11THENE$=E1$+"F9":RETURNELSERETURN
     Ensamblado de PUSH / POP
330 IFR1%=15THENR1%=12
332 IFR1%=13ORR1%=14THENIFR1%=13THENE1$="DD":R1%=11ELSEE1$="FD":R1%=11
334 IFR1%>8ANDR1%<15THENE$=E1$+HEX$(197+4*(O%-3)+(R1%-9)*16):RETURNELSERETURN
340 IFR1%>0ANDR1%<9THENE$=E1$+RIGHT$("0"+HEX$(-8+8*R1%+O%),2)+E2$:RETURN
342 IFR1%=13ORR1%=14THENIFR1%=13THENE1$="DD":R1%=11ELSEE1$="FD":R1%=11
344 IFR1%>8ANDR1%<15THENE$=E1$+RIGHT$("0"+HEX$(3+16*(R1%-9)+8*(O%-4)),2):RETURNELSERETURN
     Ensamblado de ADD / ADC / SUB / SBC / AND / XOR / OR / CP
350 IF(O%<8ORO%=9)ANDR1%=8ANDX%=3THENR1%=R2%:V1=V2:P$(4)=P$(5)
352 IFR1%>0ANDR1%<9THENE$=E1$+HEX$(128+8*(O%-6)+R1%-1)+E2$:RETURN
354 IFR1%=0ANDV1<>.1THENE$=HEX$(198+8*(O%-6))+LEFT$(P$(4),2):RETURN
356 IF(O%=7ORO%=9)ANDR1%=11ANDR2%>8ANDR2%<13THENE$="ED"+HEX$(66+4*(9-O%)+16*(R2%-9)):RETURN
358 IFR1%=13ORR1%=14THENIFR1%=13THENE1$="DD":R1%=11ELSEE1$="FD":R1%=11
360 IFR2%=13ORR2%=14THENR2%=11
362 IFR1%=11ANDR2%>8ANDR2%<13THENE$=E1$+RIGHT$("0"+HEX$(9+16*(R2%-9)),2):RETURNELSERETURN
     Ensamblado de RET
370 IFR1%=2THENR1%=28ELSEIFL$(Z%,2)<>""ANDR1%<25THENRETURN
372 IFR1%=0THENE$="C9":RETURNELSEE$=HEX$(192+8*(R1%-25)):RETURN
     Ensamblado de CALL / JP
380 IFR1%=2THENR1%=28
382 IFO%=15THENIFR1%=0THENE$="CD"+P$(4):RETURNELSEE$=HEX$(196+8*(R1%-25))+P$(5):RETURN
384 IFR1%=23ORR1%=24THENIFR1%=23THENE1$="DD"ELSEE1$="FD"
386 IFX%=2THENIFR1%=0THENE$="C3"+P$(4):RETURNELSEE$=E1$+"E9"+P$(4):RETURN
388 IFR1%>24THENE$=HEX$(194+8*(R1%-25))+P$(5):RETURNELSERETURN
     Ensamblado de JR / DJNZ
390 IFR%=0THENIFX%=2THENN%=V1:GOTO394ELSEN%=V2:GOTO394
391 N%=D-P(2+X%):IFN%>126ORN%<-129THENRETURN
392 IFN%>0THENN%=254-N%ELSEN%=-N%-2
394 IFO%=18THENE$="10"+RIGHT$("0"+HEX$(N%),2):RETURN
396 IFR1%=2THENR1%=28ELSEIFR1%>28THENRETURN
398 IFX%=2THENE$="18"ELSEE$=HEX$(32+8*(R1%-25))
400 E$=E$+RIGHT$("0"+HEX$(N%),2):RETURN
     Ensamblado de BIT / RES / SET
410 IFR2%>0ANDR2%<9ANDV1>=0ANDV1<8THENE$=E1$+"CB"+E2$+HEX$(64*(O%-18)+V1*8+R2%-1):RETURNELSERETURN
     Ensamblado de RLS / RRC / RL / RR / SLA / SRA / SRL
420 IFO%=28THENO%=29
422 IFR1%>0ANDR1%<9THENE$=E1$+"CB"+E2$+RIGHT$("0"+HEX$(8*(O%-22)+R1%-1),2):RETURNELSERETURN
     Ensamblado de IN / OUT
430 IFO%=29ANDR1%>0ANDR1%<9ANDR2%=22THENE$="ED"+HEX$(56+8*R1%):RETURN
432 IFO%=30ANDR2%>0ANDR2%<9ANDR1%=22THENE$="ED"+HEX$(57+8*R2%):RETURN
434 IFO%=29ANDR1%=8ANDP(7)THENE$="DB"+LEFT$(P$(5),2):RETURN
436 IFO%=30ANDR2%=8ANDP(6)THENE$="D3"+LEFT$(P$(4),2):RETURNELSERETURN
     Ensamblado de IM
440 IFV1>=0ANDV1<3THENE$="ED"+MID$("46565E",V1*2+1,2):RETURNELSERETURN
     Ensamblado de RST
450 IFV1>=0ANDV1<=56THENE$=MID$("C7CFD7DFE7EFF7FF",V1/4+1,2):RETURNELSERETURN
     Ensamblado de EX
460 IFR1%=10ANDR2%=11THENE$="EB":RETURN
462 IFR1%=15ANDR2%=19THENE$="08":RETURN
464 IFR2%=13ORR2%=14THENIFR2%=13THENE1$="DD":R2%=11ELSEE1$="FD":R2%=11
466 IFR1%=18ANDR2%=11THENE$=E1$+"E3":RETURNELSERETURN
     Ensamblado de DB / DW
470 IFLEFT$(L$(Z%,2),1)=""THEN482
472 IFO%=34THENE$=LEFT$(P$(4),2)ELSEE$=P$(4)
474 IFL$(Z%,3)=""THENRETURNELSEA$=L$(Z%,3)+",":C$=""
476 FORF%=1TOLEN(A$)
478 IFMID$(A$,F%,1)<>","THENC$=C$+MID$(A$,F%,1)ELSEE$=E$+RIGHT$("0"+HEX$(VAL(C$)),2):C$=""
480 NEXT:RETURN
482 R%=0:A$=MID$(L$(Z%,2),2,LEN(L$(Z%,2))-2)
484 FORF%=1TOLEN(A$):E$=E$+RIGHT$("0"+HEX$(ASC(MID$(A$,F%,1))),2):NEXT:RETURN
     Ensamblado de ORG
490 R%=1:D=V1:B=D:E$="":RETURN

     Prompt de opciones del programa
700 LINE INPUT ">";A$:GOSUB720:O%=0
702 FOR F%=1TO13
704 IFMID$("EIDLASONQWFPX",F%,1)=P$(1)THENO%=F%:F%=99
706 NEXT
708 ONO%GOSUB740,760,780,800,870,820,840,904,910,850,880,800,890:GOTO700

     Separar los parámetros de la línea de entrada
720 X%=1:P$(1)="":P$(2)="":P$(3)=""
722 FORF%=1TOLEN(A$)
724 C$=MID$(A$,F%,1):IFC$<>" "ANDC$<>","THENP$(X%)=P$(X%)+C$:GOTO728
726 IFX%<3ANDP$(X%)<>""THENX%=X%+1ELSEIFX%=3THENP$(3)=P$(3)+MID$(A$,F%):F%=LEN(A$)
728 NEXT:RETURN

     Editor de líneas
740 F%=VAL(P$(2)):IFF%>0ANDF%<LF%+1THENLI%=F%
742 A$="":PRINTUSING"### ";LI%;:LINEINPUTA$:IFA$=""THENRETURNELSEGOSUB720
744 H$(LI%)="":IFLEFT$(A$,1)=":"THENL$(LI%,1)=LEFT$(P$(1),8):GOTO756
746 B$=P$(1):O%=0
748 FORF%=1TO70
750 IFB$=I$(F%)THENO%=F%:F%=99
752 NEXT
754 IFO%THENL$(LI%,1)=STR$(O%):L$(LI%,2)=P$(2):L$(LI%,3)=P$(3)ELSEPRINT"Syntax Error !!!":GOTO742
756 IFLI%<H%THENLI%=LI%+1ELSERETURN
758 IFLI%>LF%THENLF%=LF%+1:GOTO742ELSE742

     Insertar líneas en el programa
760 F%=VAL(P$(2)):N%=VAL(P$(3)):IFF%>LF%ORN%=0ORF%+N%>H%THENRETURNELSELF%=LF%+N%
762 FORI%=LF%TOF%+N%STEP-1:L$(I%,1)=L$(I%-N%,1):L$(I%,2)=L$(I%-N%,2):L$(I%,3)=L$(I%-N%,3):H$(I%)=H$(I%-N%):NEXT
764 F%=VAL(P$(2)):FORI%=F%TOF%+N%-1:L$(I%,1)="":L$(I%,2)="":L$(I%,3)="":H$(I%)="":NEXT:RETURN

     Eliminar líneas del programa
780 F%=VAL(P$(2)):N%=VAL(P$(3)):IFN%=0THENN%=F%
782 IFF%=0ORF%>LF%ORN%>LF%THENRETURNELSELF%=LF%-(N%-F%+1)
784 FORI%=F%TOLF%:N%=N%+1:L$(I%,1)=L$(N%,1):L$(I%,2)=L$(N%,2):L$(I%,3)=L$(N%,3):H$(I%)=H$(N%):NEXT:RETURN

     Listar/Imprimir el programa
800 F%=VAL(P$(2)):N%=VAL(P$(3)):IFF%=0ORF%>LF%THENF%=1:N%=LF%-1
802 IFN%=0THENN%=F%
804 IFN%>=LF%THENN%=LF%-1
805 IFO%=12THEN814
806 FORI%=F%TON%
808 PRINTUSING"### ";I%;:IFLEFT$(L$(I%,1),1)=":"THENPRINTL$(I%,1):GOTO812
810 PRINTTAB(14);I$(VAL(L$(I%,1)))+" "+L$(I%,2);:IFL$(I%,3)<>""THENPRINT",";L$(I%,3)ELSEPRINT
812 NEXT:RETURN
814 FORI%=F%TON%
815 LPRINTUSING"### ";I%;:IFLEFT$(L$(I%,1),1)=":"THENLPRINTL$(I%,1):GOTO817
816 LPRINTTAB(14);I$(VAL(L$(I%,1)))+" "+L$(I%,2);:IFL$(I%,3)<>""THENLPRINT",";L$(I%,3)ELSELPRINT
817 NEXT:RETURN

     Grabar el programa
820 IFP$(2)<>""THENN$=P$(2)+".ASM"
822 OPENN$FOROUTPUTAS#1
824 FORF%=1TOLF%-1
826 A$=L$(F%,1)+" "+L$(F%,2):IFL$(F%,3)<>""THENA$=A$+","+L$(F%,3)
828 IFL$(F%,1)<>""THENPRINT#1,A$
830 NEXT:CLOSE#1:RETURN

     Cargar el programa
840 IFP$(2)<>""THENN$=P$(2)+".ASM"
842 GOSUB906:OPENN$FORINPUTAS#1
844 IFEOF(1)THENCLOSE#1:LF%=LI%:RETURNELSELINEINPUT#1,A$:GOSUB720
846 L$(LI%,1)=P$(1):L$(LI%,2)=P$(2):L$(LI%,3)=P$(3):LI%=LI%+1:GOTO844
850 F%=VAL(P$(2)):IFF%>31ANDF%<81THENWIDTHF%:RETURNELSEWIDTH38:RETURN

     Rutina de PressKey
860 IFINKEY$=""THEN860ELSERETURN


     Número de pasadas al ensamblar
870 F%=VAL(P$(2)):IFF%=1ORF%=2THENZ1%=F%:Z2%=FELSEZ1%=1:Z2%=2
872 GOTO110

     Directorio de fuentes en el disquete
880 FILES"*.ASM":PRINT:RETURN

     Ejecutar el programa ensamblado
890 F=VAL(P$(2)):IFF=0THENF=B
892 DEFUSR=F:F=USR(0):RETURN

     Iniciar el ensamblador y cargar matrices
900 FORF%=1TO70:READI$(F%):NEXT:FORF%=1TO34:READV$(F%):NEXT
902 FORF%=1TO32:READR$(F%):NEXT:FORF%=1TO36:READJ%(F%):NEXT
903 FORF%=1TO10:READN%:KEYF%,I$(N%)+" ":NEXT
904 CLS:WIDTH38:PRINT"Z80 Assembler - (c) Scainet Soft, 2014":PRINT
906 LI%=1:LF%=1:Q%=0:FORF%=1TOH%:L$(F%,1)="":L$(F%,2)="":L$(F%,3)="":H$(F%)="":NEXT
908 RETURN:ONERRORGOTO912:RETURN

     Salir del ensamblador
910 END
912 PRINT "Internal Error.":PRINT:GOSUB908:RESUME700

     Datos de las Instrucciones del Z80 y sus códigos hexadecimales
950 DATA "LD","POP","PUSH","INC","DEC","ADD","ADC","SUB","SBC","AND","XOR","OR","CP","RET","CALL","JP","JR"
952 DATA "DJNZ","BIT","RES","SET","RLC","RRC","RL","RR","SLA","SRA","SRL","IN","OUT","IM","RST","EX","DB","DW"
954 DATA "ORG","DAA","CPL","NEG","HALT","EXX","RRCA","RLCA","RRA","RLA","RLD","RRD","LDI","LDIR","LDD","LDDR"
956 DATA "CPI","CPIR","CPD","CPDR","INI","INIR","IND","INDR","OUTI","OTIR","OUTD","OTDR","NOP","CCF","SCF"
958 DATA "EI","DI","RETI","RETN","27","2F","ED44","76","D9","0F","07","1F","17","ED6F","ED67","EDA0","EDB0"
960 DATA "EDA8","EDB8","EDA1","EDB1","EDA9","EDB9","EDA2","EDB2","EDAA","EDBA","EDA3","EDB3","EDAB","EDBB"
962 DATA "00","3F","37","FB","F3","ED4D","ED45","B","C","D","E","H","L","(HL)","A","BC","DE","HL","SP","IX"
964 DATA "IY","AF","(BC)","(DE)","(SP)","AF","I","R","(C)","(IX)","(IY)","NZ","Z","NC","C","PO","PE","P","M"
     Datos de Salto o función según ID de instrucción
966 DATA 1,2,2,3,3,4,4,4,4,4,4,4,4,5,6,6,7,7,8,8,8,9,9,9,9,9,9,9,10,10,11,12,13,14,14,15
     Datos de las teclas de función
968 DATA 1,3,4,15,16,13,2,5,14,17


APUNTES FINALES

La historia
Este es un proyecto que me planteé por primera vez hace más de 25 años.

En mis inicios en el mundo de la informática, y con la ayuda de mi ZX-81, empecé a hacer mis pinitos en el mundo del código máquina. En aquellos tiempos ensamblaba a mano, y desarrollé sencillas rutinas que no usé para nada.

Casi cuatro años más tarde conseguí mi ansiado INVES SPECTRUM+ y después de desarrollar tres programas en BASIC me decidí a programar en ensamblador de una forma más seria. Por aquellos tiempos intentar conseguir un ensamblador no era una tarea fácil si no conocías a alguien que lo tuviera, que no era mi caso, o estuvieras dispuesto a pagar. Y aquí empezó mi primer intento por hacerme un ensamblador en BASIC. Después de una tarde ante interminables listas de IF lo dejé correr y pude conseguir una copia del GENS-3 con fotocopias del manual por el módico precio de 2.000 pesetas. Hice muchos programas con este estupendo ensamblador.

Unos tres años más tarde me hice con un COMMODORE-64, que usé básicamente para jugar, pero me llamó la atención descubrir en una revista un ensamblador de 6502 realizado íntegramente en BASIC. Me tomé la molestia de teclearlo, y realmente funcionaba, aunque era lento. Bueno, el BASIC del C-64 es así. Aún conservo este programa en cinta.

Más de 20 años después, analizando las listas de instrucciones y mnemotécnicos del Z80 vi que con unas fórmulas relativamente sencillas podías generar el código hexadecimal de grandes grupos de instrucciones. De esta investigación salió mi “K-Assembler”, un sencillo pero funcional ensamblador para el ZX-81 con 1K de memoria RAM. Ensamblaba algo más del 50% de los mnemotécnicos del Z80, pero era consciente que con un poco más de memoria en una máquina más rápida y con mejor BASIC podía hacer el ensamblador completo. Aquí tenéis el resultado.


Condiciones iniciales
Antes de ponerme ha desarrollar el ensamblador hice una lista de las condiciones mínimas que debía tener:

1 – Velocidad. Este tema es básico. No podía tardar más de un minuto en ensamblar las 250 líneas.
2 – Sencillo. El entorno de desarrollo debería ser cómodo y sencillo de usar.
3 – Profesional. Debía poder ensamblar listados aparecidos en revistas realizados con otros ensambladores.

Por suerte y tras mucho análisis he conseguido cumplir con las tres condiciones.


Cosillas del BASIC de los MSX
El MSX reserva por defecto 200 bytes para almacenar cadenas de texto. En este caso es absolutamente insuficiente.
Con el “CLEAR 4096” de la línea 100 reservo 4 KB de RAM para poder introducir las distintas matrices y líneas del editor.

Es muy importante poner al principio del programa en BASIC las líneas más usadas ya que el intérprete BASIC las localiza más rápidamente. Por otro lado he eliminado todos los espacios que hay en el listado porque la ejecución es más rápida y el programa ocupa menos memoria.

He apreciado que, a veces, el programa se queda “paralizado” durante unos segundos durante el proceso de ensamblado. He llegado a la conclusión de que el intérprete va rellenando secuencialmente la memoria reservada a las cadenas y que cuando llega al final hace una especie de compactación, eliminando los espacios vacíos que puedan haber quedado al ir concatenando valores.

Con todo esto, el programa ocupa unos 7 KB y necesita unos 10 KB más para almacenar variables y matrices. Quedan así unos 12 KB libres para almacenar el programa en código máquina.


Velocidad. ¿Como se consigue?
Este ensamblador puede llegar a ensamblar más de 300 líneas de código por minuto. Conseguirlo no ha sido fácil.

Un ensamblador suele funcionar haciendo dos pasadas sobre el código fuente. La primera pasada sirve para calcular la dirección de memoria de las etiquetas que hay en el programa y la segunda para generar el código máquina.

Analizando el juego de instrucciones del Z80 vemos que hay de dos tipos: con parámetros y sin parámetros. Así que la primera decisión ha consistido en ordenar la lista de instrucciones en una matriz. Primero las que usan parámetro y después el resto.

Analizando las tablas de ensamblado vemos que se pueden agrupar ciertas instrucciones, ya que un mismo algoritmo puede tener un uso común. La segunda decisión ha consistido en agrupar las instrucciones en 15 grupos, poniendo primero las más usadas.

Para las instrucciones que no usan parámetro he creado una matriz donde guardo su código hexadecimal correspondiente, ya que siempre es el mismo. A la hora de ensamblar el proceso es directo ya que no han de usar ningún algoritmo.
En los listados hay dos tipos de líneas: las que usan etiquetas en algún parámetro y las que no. De esta forma, en la primera pasada del proceso de ensamblado ensamblo las líneas que no usan etiquetas mientras calculo la dirección de memoria de las etiquetas. El código de ensamblado de estas líneas que no usan etiquetas se guarda en una matriz y salvo que se modifiquen no se vuelven a calcular más. En la segunda pasada solo ensamblo las instrucciones que usan etiquetas. Esto ha sido básico para el buen rendimiento del ensamblador.

Las líneas pueden tener hasta dos parámetros y pueden ser de distintos tipos. Los he procesado según su importancia:
1 - ¿Va entre paréntesis? En caso afirmativo lo quito.
2 - ¿El valor es un número decimales o hexadecimales?
3 – ¿El parámetro es un registro?
4 - ¿El parámetro es una etiqueta?
5 - ¿El parámetro es un registro de índice?

Al introducir una línea en el programa identifico la instrucción. Si existe me guardo su identificado para evitar tener que identificarla posteriormente. Esto me ha servido para ahorrar memoria y acelerar el proceso de ensamblado. Si la instrucción no existe da un error y la línea no se guarda.

He creado una matriz donde indico la subrutina que usa cada una de las instrucciones con parámetro. De esta forma he podido usar un ON GOSUB en lugar de una lista de IF, y un RETURN se procesa más rápido que un GOTO y ocupa menos memoria.

Durante la ejecución de los algoritmos de ensamblado, que los hay más simples y más complicados, a la que se detecta el método correcto se fuerza un RETURN para evitar procesar más líneas de las necesarias.

La verificación de la línea que se intenta ensamblar está parcialmente desactivada. Esto quiere decir que si la instrucción es correcta el código generado es correcto, pero si la instrucción no es correcta (por ejemplo, “ADD B,A” no existe) el ensamblador puede generar un código incorrecto o aproximado (según el ejemplo anterior haría un “ADD A,B”).

Al ensamblar el programa hay la opción de indicar que solo se haga una pasada. Esto es especialmente útil si el programa no usa etiquetas o el código añadido no afecta a la dirección de memoria de éstas. De esta forma se mejora la velocidad.

Con todo esto, la primera vez que se ensambla el proceso es más lento porque realmente se ensamblan todas las líneas, a una velocidad de unas 100 líneas por minuto, pero a partir de entonces y en función de las instrucciones del programa, el uso o no de etiquetas y la opción de ensamblado, el proceso se acelera considerablemente pudiendo llegar a velocidades teóricas de hasta 350 líneas por minuto.


Buscando los algoritmos de ensamblado
No todas las instrucciones son igual de complejas de ensamblar. La instrucción más compleja es LD que requiere de múltiples líneas de código con múltiples condiciones. En cambio hay otras instrucciones que se ensamblan con una única línea de código, que además puede ser compartida con otras instrucciones.

Analizando la tabla de la instrucción LD de transferencias de 8 bits se puede ver que los códigos están comprendidos entre 40h (64) y 7Fh (127). Empieza en la columna “B” y acaba en la columna “A”, y cada fila que bajamos se incrementa en 8.

A partir de la codificación de registros que he usado, con la fórmula: “64 + 8 * IDreg1 + IDreg2” podemos ensamblar fácilmente 63 de los 132 mnemotécnicos distintos de la instrucción LD.

Para poder ensamblar el resto hace falta más código, aunque en general siempre siguen unos patrones definidos.


El entorno de desarrollo
Todo el ensamblador es muy sencillo de usar. El proceso de introducir líneas, modificar o borrarlas, ensamblar y ejecutar los programas se realiza sin ningún problema.

Con el comando “W” podemos adaptar el formato de pantalla a nuestro gusto, admitiendo un formato entre 32 y 80 columnas, y con el comando “P” podemos listar el código fuente por la impresora.

Por otro lado se han adaptado las 8 teclas de función con algunas de las instrucciones más habituales del Z80.

El programa en BASIC esta comprendido entre las líneas 100 y 950, pudiendo añadir nuevas líneas que pueden ser necesarias para la ejecución del programa en código máquina que estamos desarrollando.


Para terminar
He probado el ensamblado con distintas de rutinas en CM que he encontrado en revistas y libros, el código hexadecimal generado coincide y la ejecución es correcta. Además es bastante rápido. Así que... Prueba superada ¡!!

El código fuente es muy fácilmente adaptable a cualquier equipo de disponga de un Microsoft BASIC o compatible.
Me atrevo a decir que casi tal cual se podría teclear en un AMSTRAD CPC y funcionaría perfectamente.

Mi próximo proyecto consiste en adaptar el programa a un ORIC y ensamblar las instrucciones del 6502. A ver si hay suerte...

Os invito a probarlo.

DSC_0496.jpg
DSC_0496.jpg (40.52 KiB) Visto 2288 veces
z80_1.gif
z80_1.gif (37.92 KiB) Visto 2288 veces
z80_3.gif
z80_3.gif (39.33 KiB) Visto 2288 veces

Avatar de Usuario
apple2man
Mensajes: 257
Registrado: 03 Jun 2011 13:53
Agradecimiento recibido: 4 veces

Re: Z80-Assembler para MSX

Mensajepor apple2man » 03 May 2014 10:20

Excelente trabajo, me has dado algunas ideas para acabar un ensamblador de 6502 que empecé cuando era un chavalín con el Apple II, también en BASIC. Me pasó algo similar a lo que describes, cayó en mis manos un ensamblador de la época, el LISA para Apple II y lo dejé. Con tu permiso voy a utilizar algunas partes de tu programa.

Dani. ¿de dónde sacas el tiempo? que pedazo de curro!! Me ha encantado leerlo.

Una cosilla, estos días veo cómo se lo están pasando con la Retroesquivias y me está entrando añoranza de la Retrovilobí, ¿te animas? algo sencillo, para pasar un ratillo, charlar un poco y juntar a la afición.

Avatar de Usuario
htdreams
Mensajes: 553
Registrado: 12 Nov 2012 19:34
Agradecido : 5 veces
Agradecimiento recibido: 11 veces

Re: Z80-Assembler para MSX

Mensajepor htdreams » 03 May 2014 11:45

Impresionante, quien me diera encontrar algo como esto hace unos 10 años :-) por desgracia ahora mismo me veo muy limitado de tiempo para darle un uso a corto plazo, así que espero que otros retrowikianos puedan ponerle buen uso, y ver que puede salir con esto :-D

Muchas gracias por el aporte!!

Avatar de Usuario
Silicebit
Mensajes: 1332
Registrado: 16 May 2011 21:13
Ubicación: La buhardilla del silicio.
Agradecido : 32 veces
Agradecimiento recibido: 83 veces
Contactar:

Re: Z80-Assembler para MSX

Mensajepor Silicebit » 03 May 2014 18:46

¡Un currazo impresionante dancresp! Gracias, me va a venir muy bien para trabajar con mi nuevo MSX2. :-)
El 6809 es el Rolls-Royce de los 8bits, el 6502 es el Mercedes, y el Z80 el SEAT 850. Sorry, but... I think different. :-P -0r1c -m3s3x -t4nd1 -cbmja YouTube

race8086
Mensajes: 437
Registrado: 29 Oct 2010 22:05

Re: Z80-Assembler para MSX

Mensajepor race8086 » 03 May 2014 22:16

Dancresp!, sigues siendo mi héroe -11, assembler for ever!

dancresp
Mensajes: 5009
Registrado: 13 Nov 2010 02:08
Agradecido : 14 veces
Agradecimiento recibido: 85 veces

Re: Z80-Assembler para MSX

Mensajepor dancresp » 04 May 2014 00:43

Apple2man escribió:Excelente trabajo, me has dado algunas ideas para acabar un ensamblador de 6502 que empecé cuando era un chavalín con el Apple II, también en BASIC.

Pues como ya te he dicho, estoy trabajando en una versión para ORIC que ensambla precisamente en 6502. Creo que he encontrado un método que le permitirá compilar de una forma bastante rápida y sencilla, al igual que esta versión o incluso más.

La parte correspondiente al editar funciona casi tal cual con el código BASIC del MSX, excepto la parte de grabación y carga de datos, ya que este BASIC no trabaja con ficheros secuenciales y lo voy a adaptar para meterlo en una "especie" de cadena en memoria. Bueno, ya lo veréis.

Apple2man escribió:Con tu permiso voy a utilizar algunas partes de tu programa.

Por dios !!! Será todo un honor.

Apple2man escribió:¿de dónde sacas el tiempo?

Tengo la "suerte" de pasarme dos horas al día en tren de cercanías. Dan para mucho si las sabes aprovechar... -grin

Apple2man escribió:Una cosilla, estos días veo cómo se lo están pasando con la Retroesquivias y me está entrando añoranza de la Retrovilobí, ¿te animas? algo sencillo, para pasar un ratillo, charlar un poco y juntar a la afición.

Pues te has adelantado por poquito porque este 2014 habrá Retro Vilowiki si la gente responde. Ya he hablado con el del local de cada año, y encantado.
Si es que lo ponemos todo patas arriba, pero nos portamos tan bien... -507

Silicebit escribió:¡Un currazo impresionante dancresp! Gracias, me va a venir muy bien para trabajar con mi nuevo MSX2. :-)

Ya te digo yo que si tu equipo dispone de disquetera, este ensamblador funciona más que bien, salvo que te quieras currar un programa en código máquina enterito.

Yo tengo un ensamblador para MSX, el R.S.C. de Manhattan Transfer, que funciona a las mil maravillas, pero tiene dos inconvenientes:
- No usa la disquetera, con lo que es lento en cargar y grabar.
- Desactiva los 32KB del intérprete BASIC y los sustituye por RAM, donde carga el ensablador/desensamblador. Así que no puedes ir al BASIC para hacer ciertas pruebas sino hacer una historia bastante liada que activa/desactiva los bancos.

El mío ha corregido estos dos inconvenientes y para listados pequeños funciona más que bien, y siempre lo puedes mejorar.

Para terminar, este proyecto tiene 3 partes:
1) El ensamblador Z80 de MSX, que ya existe, y se puede adaptar fácilmente a otros equipos con Z80.
2) El ensamblador 6502 para ORIC que se podrá adaptar a equipos con el mismo procesador.
3) El ensamblador 6809 para DRAGON, que también debería funcionar en equipos THOMSON o TRS-80.

Pasito a pasito... -drinks

Avatar de Usuario
ron
Mensajes: 17331
Registrado: 28 Oct 2010 14:20
Ubicación: retrocrypta
Agradecido : 557 veces
Agradecimiento recibido: 572 veces

Re: Z80-Assembler para MSX

Mensajepor ron » 04 May 2014 21:29

dancresp, eres un crack ! y como dices, sin prisa pero sin pausa-. Me encanta !!! Felicidades

Avatar de Usuario
web8bits
Mensajes: 943
Registrado: 31 Oct 2010 10:34
Agradecido : 75 veces
Agradecimiento recibido: 45 veces

Re: Z80-Assembler para MSX

Mensajepor web8bits » 05 May 2014 17:23

Me quito el sombrero, que crack, disfruto como un enano todos tus artículos, felicidades.

Un saludo

dancresp
Mensajes: 5009
Registrado: 13 Nov 2010 02:08
Agradecido : 14 veces
Agradecimiento recibido: 85 veces

Re: Z80-Assembler para MSX

Mensajepor dancresp » 11 May 2014 22:51

Por gentileza de bitvision acabo de sustituir el escaneo de las dos páginas del listado por un texto en ASCII que hace que sea perfectamente entendible.

También me ha pasado un fichero con extensión ".BAS".
En cuanto descubra como meterlo en una imagen de disquete del BlueMSX lo subo también.

jltursan
Mensajes: 1929
Registrado: 20 Sep 2011 13:59
Agradecido : 56 veces
Agradecimiento recibido: 148 veces

Re: Z80-Assembler para MSX

Mensajepor jltursan » 11 May 2014 22:53

Usa el Disk-Manager. Imprescindible :-)

dancresp
Mensajes: 5009
Registrado: 13 Nov 2010 02:08
Agradecido : 14 veces
Agradecimiento recibido: 85 veces

Re: Z80-Assembler para MSX

Mensajepor dancresp » 12 May 2014 00:03

jltursan escribió:Usa el Disk-Manager. Imprescindible :-)

Bajado, instalado y pasado a la imagen. Muy simple.

Muchas gracias. -drinks

Mañana teclearé unos fuentes de ejemplo y subo la imagen, y ya puestos aprovecharé para cambiar las capturas.

dancresp
Mensajes: 5009
Registrado: 13 Nov 2010 02:08
Agradecido : 14 veces
Agradecimiento recibido: 85 veces

Re: Z80-Assembler para MSX

Mensajepor dancresp » 21 May 2014 20:40

He aprovechado que he subido el ensamblador de 6502 para el ORIC, para poner este del Z80 al mismo nivel.

Esto quiere decir que he cambiado las imágenes por capturas de emulador, he puesto una versión del código fuente en BASIC con muchos comentarios y he subido una imagen del programa en formato DSK para que lo podáis usar en un emulador.

Ustedes los disfruten. -grin


Volver a “Lenguajes de Programación y herramientas Dev”

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado