6502-Assembler para ORIC

Foro dedicado a la programación en todo tipo de sistemas clásicos.
dancresp
Amiga 1200
Amiga 1200
Mensajes: 1393
Registrado: 23 Dic 2008, 17:53
Sistema Favorito: MSX
primer_sistema: ZX81
Primera consola: Atari 2600
Gracias dadas: 3 veces
Gracias recibidas: 20 veces

6502-Assembler para ORIC

Mensajepor dancresp » 21 May 2014, 13:53

6502_2.gif
6502_2.gif (9.28 KiB) Visto 3824 veces


EL PROGRAMA
Este ensamblador es un completo entorno de desarrollo para el microprocesador 6502 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 introducir 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. Dejar vacío para salir del editor.

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.

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.

P
Activa o desactiva la impresión por pantalla del proceso de ensamblado, haciendo que sea un 50% más rápido.

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

S [nombre]
Graba el programa fuente con el nombre indicado o con el último nombre usado. El nombre por defecto es “SOURCE”.

O [nombre]
Carga el programa fuente con el nombre indicado o con el último nombre usado. El nombre por defecto es “SOURCE”.

N
Inicializa el ensamblador borrando todas las líneas del programa actual.

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 debe 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 200 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 904 y 902 respectivamente.

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) de la línea 902, y el “Q<50” de la línea 214.

Las líneas 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 hexadecimales porque los decimales 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.

Por defecto el programa en código máquina se carga a partir de la dirección 30000. Para modificar este valor se debe usar la directiva “*= 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 “.BYTE” (un byte) y “.WORD” (dos bytes). Si se introducen listas de números se deben separar con comas y estar en formato hexadecimal. Si se introduce una cadena de caracteres se debe usar la directiva “.ASC” debe estar comprendida entre unas comillas simples. No se pueden mezclar listas de números y cadenas en la misma línea.

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

Descargar el programa en formato "TAP":
6502ASM.rar
(3.01 KiB) Descargado 277 veces


BLOQUES
He dividido el listado en 4 grandes bloques:

- Análisis de la línea a ensamblar.
- Ensamblador e introducción en memoria del código generado.
- Menú de opciones.
- Rutinas del entorno de desarrollo.


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

100 – Bajo la cantidad de memoria disponible desde BASIC y salto al menú principal.
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 etiqueta.
120 – Detectar si la línea ya se había ensamblado.
122 – Guardar el valor de los parámetros.
124 – Si se usa una directiva saltar a la línea 300.
160 – Bucle para identificar la etiqueta usada.
216 – En la primera pasada muestra la dirección de memoria de las etiquetas del programa.
220 – En la segunda pasada muestra la dirección de memoria y los valores hexadecimales del ensamblado de la línea.
244 – Bucle que introduce los valores hexadecimales en la memoria.
260 – Final del proceso de ensamblado.
302 – Tratar las directivas “.BYTE” y “.WORD”.
320 – Tratar la directiva “.ASC”.
330 – Tratar la directiva “*=”.
700 – Menú principal.
720 – Separar una línea por parámetros.
740 – Comando EDIT (E)
760 – Comando INSERT (I)
770 – Comando PRINT (P)
780 – Comando DELETE (D)
800 – Comandos LIST (L)
820 – Comando SAVE (S)
840 – Comando LOAD (O)
860 – Comando EXECUTE (X)
870 – Comando ASSEMBLE (A)
880 – Rutina de INPUT.
890 – Rutina de USING para mostrar el número de línea.
900 – Inicio del ensamblador. Carga de matrices y mensaje en pantalla.
920 – Comando QUIT (Q)
950 – Líneas DATA con las listas de instrucciones y códigos de ensamblado.


EL PROGRAMA

Código: Seleccionar todo

    ' Bajar el RAMTOP y saltar al Prompt
100 HIMEM#4000:GOTO900

    ' Inicio del proceso de Ensamblado
110 IFZ1=1THENQ=0
    ' Control de pasadas de ensamblado
112 FORY=Z1TOZ2
    ' Dirección de ensamblado por defecto
114 D=30000: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$="":GOTO210
    ' La línea ya se ha ensamblado?
120 IFH$(Z)<>""THENO=0:E$=H$(Z):GOTO210
    ' Guardar los parámetros y el Id de instrucción
122 A$=L$(Z,2):B$=L$(Z,3):O=VAL(L$(Z,1)):M=0:E$="":L=0
    ' La línea es una directiva?
124 IFO>56THEN300
    ' Detectar el tipo de direccionamiento
130 IFA$=""THENM=6:GOTO200
132 IFA$="A"THENM=1:GOTO200
134 IFB$="X)"THENM=8:A$=MID$(A$,2):GOTO150
136 IFLEFT$(A$,1)="#"THENM=5:A$=MID$(A$,2):GOTO150
138 IFLEFT$(A$,1)="("ANDRIGHT$(A$,1)=")"THENM=7:A$=MID$(A$,2,LEN(A$)-2)
140 IFM=7ANDB$="Y"THENM=9:GOTO150
142 IFO>5ANDO<14THENM=10:GOTO150
    ' Detectar registro usado
144 R=0:IFM=0THENM=2
146 IFB$="X"THENR=1
148 IFB$="Y"THENR=2
    ' El parámetro es un valor numérico?
150 C$=LEFT$(A$,1):IFC$>="0"ANDC$<="9"THENA=VAL(A$):GOTO170
    ' El parámetro es un número hexadecimal?
152 IFC$="$"THENA$=MID$(A$,2):A=VAL("#"+A$):GOTO170
    ' Comprobar si se está usando una etiqueta
154 L=1:IFY=1THENA$=STR$(D):A=D:GOTO170
160 FORF=1TOQ
162 IFQ$(F)=A$THENA$=STR$(M(F)):A=M(F):F=99
164 NEXT
166 IFF<>100THENE$="":GOTO220
    ' Adaptar el valor numérico a 1 o 2 bytes
170 IFM<>10THEN176
172 A=D-A:IFA<0THENA=ABS(A)-2ELSEA=254-ABS(A)
174 IFA>255THENA=A/255
176 A$=RIGHT$("000"+MID$(HEX$(A),2),4)
180 IFA<256THEN190
182 IFM=2THENM=M+R
184 E$=RIGHT$(A$,2)+LEFT$(A$,2)
186 GOTO200
190 IFM=2THENM=11+R
192 E$=RIGHT$("0"+MID$(HEX$(A),2),2)
    ' Montar el código hexadecimal de ensamblado
200 A$=C$(O,M):IFA$=""THENE$="":GOTO220ELSEE$=A$+E$
    ' Si la línea no usa una etiqueta se guarda el código
202 IFL=0THENH$(Z)=E$
    ' Si es la segunda pasada salta la parte de etiquetas
210 IFY=2THEN220
212 IFO<>99THEN250
214 IFQ<50THENQ=Q+1:Q$(Q)=MID$(L$(Z,1),2):M(Q)=D
    ' Mostrar la dirección de la etiqueta
216 IFPTHEN250ELSEPRINTD;"= ";Q$(Q):GOTO250
    ' Mostrar el ensamblado de la línea
220 IFPTHEN240
222 GOSUB890:PRINTD;:IFLEFT$(L$(Z,1),1)=":"THENPRINTL$(Z,1):GOTO250
224 PRINT" ";E$;TAB(22);I$(VAL(L$(Z,1)))+" "+L$(Z,2);
226 IFL$(Z,3)<>""THENPRINT","+L$(Z,3)ELSEPRINT
    ' Si no se ha generado código mostrar el error (excepto *=)
240 IFE$=""ANDO<60THENPRINT"Error in ";Z:Z=999:Y=2:GOTO252
242 IFE$=""THEN250
    ' Introducir el código generado en memoria
244 FORF=1TOLEN(E$)-1STEP2:POKED,VAL("#"+MID$(E$,F,2)):D=D+1:NEXT:GOTO252
250 D=D+LEN(E$)/2
    ' Siguiente línea del programa
252 NEXTZ
254 PRINT
    ' Siguiente pasada de ensamblado
256 NEXT Y
    ' Indicar el tamaño total en bytes del programa ensamblado
258 IFE$<>""ANDZ2=2THENPRINT"Program length:";D-B
260 RETURN

    ' Tratar directivas del ensamblador
300 IF O>58THEN320
    ' Tratar .BYTE y .WORD
302 C$="":A$=A$+",":IFB$<>""THENA$=A$+B$+","
304 FORF=1TOLEN(A$)
306 IFMID$(A$,F,1)<>","THENC$=C$+MID$(A$,F,1):GOTO310
308 IFO=57THENE$=E$+RIGHT$("0"+C$,2)ELSEE$=E$+RIGHT$("000"+C$,4)
310 NEXT:GOTO202
    ' Tratar .ASC
320 IFO=60THEN330
322 FORF=2TOLEN(A$)-1:E$=E$+RIGHT$("0"+HEX$(ASC(MID$(A$,F,1))),2):NEXT
324 GOTO 202
    ' Tratar *=
330 IFLEFT$(A$,1)="$"THENA=VAL("#"+MID$(A$,2))ELSEA=VAL(A$)
332 L=1:D=A:B=D:E$="":GOTO202

    ' Prompt de opciones del programa
700 PRINT"> ";:GOSUB880:GOSUB720:O=0
702 FORF=1TO11
704 IFMID$("EIDLASONQXP",F,1)=P$(1)THENO=F:F=11
706 NEXT
708 ONOGOSUB740,760,780,800,870,820,840,900,920,860,770: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+1:GOTO728
727 IFX=3THENP$(3)=P$(3)+MID$(A$,F):F=LEN(A$)-1
728 NEXT:RETURN

    ' Editor de líneas
740 F=VAL(P$(2)):IFF>0ANDF<LF+1THENLI=F
742 Z=LI:GOSUB890:PRINT" ";:GOSUB880:IFA$=""THENRETURN
744 GOSUB720:H$(LI)="":IFLEFT$(A$,1)=":"THENL$(LI,1)=LEFT$(P$(1),8):GOTO756
746 B$=P$(1):O=0
748 FORF=1TO60
750 IFB$=I$(F)THENO=F:F=99
752 NEXT
754 IFO=0THENPRINT"Syntax Error !!!":GOTO742
755 L$(LI,1)=MID$(STR$(O),2):L$(LI,2)=P$(2):L$(LI,3)=P$(3)
756 IFLI<HTHENLI=LI+1ELSERETURN
758 IFLI>LFTHENLF=LF+1:GOTO742ELSE742

    ' Insertar líneas en el programa
760 F=VAL(P$(2)):N=VAL(P$(3)):IFF>LF ORF+N>HTHENRETURNELSELF=LF+N
762 FORI=LFTOF+NSTEP-1
763 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)
764 NEXT:F=VAL(P$(2))
766 FORI=FTOF+N-1:L$(I,1)="":L$(I,2)="":L$(I,3)="":H$(I)="":NEXT:RETURN

    ' Activar/Desactivar Print en proceso de ensamblado
770 IFPTHENP=0:PRINT"Print:On":PRINT:RETURN
772 P=1:PRINT"Print:Off":PRINT:RETURN

    ' Eliminar líneas del programa
780 F=VAL(P$(2)):N=VAL(P$(3)):IFN=0THENN=F
782 IFF=0ORF>LF ORN>LF ORN<FTHENRETURNELSELF=LF-(N-F+1)
784 FORI=FTOLF:N=N+1
786 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 el programa
800 F=VAL(P$(2)):N=VAL(P$(3)):IFF=0ORF>LFTHENF=1:N=LF-1
802 IFN=0THENN=F
804 IFN>=LFTHENN=LF-1
806 FORI=FTON
808 Z=I:GOSUB890:IFLEFT$(L$(I,1),1)=":"THENPRINT" ";L$(I,1):GOTO812
810 PRINTTAB(15);I$(VAL(L$(I,1)))+" "+L$(I,2);
811 IFL$(I,3)<>""THENPRINT","+L$(I,3)ELSEPRINT
812 NEXT:RETURN

    ' Grabar el programa
820 IFP$(2)<>""THENN$=P$(2)
822 D=16385:PRINT"Wait";:FORF=1TOLF-1:FORI=1TO3:IF L$(F,I)=""THEN826
824 FORN=1TOLEN(L$(F,I)):A=ASC(MID$(L$(F,I),N,1)):POKED,A:D=D+1:NEXT
826 POKED,9:D=D+1:NEXT:PRINT".";:NEXT:POKED,0:D=D+1
828 CSAVEN$,A16385,ED:PRINT:RETURN

    ' Cargar el programa
840 IFP$(2)<>""THENN$=P$(2)
842 CLOADN$:D=16385:LI=1:I=1:L$(1,1)="":H$(1)="":PRINT"Wait";
844 A=PEEK(D):IFA=0THENLF=LI:PRINT:RETURNELSED=D+1
846 IFA<>9THENL$(LI,I)=L$(LI,I)+CHR$(A):GOTO844
848 I=I+1:IFI=4THENI=1:LI=LI+1:PRINT".";
850 L$(LI,I)="":H$(LI)="":GOTO844

    ' Ejecutar el programa ensamblado
860 F=VAL(P$(2)):IFF=0THENF=B
862 CALLF:RETURN

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

    ' Rutina INPUT alternativa a la del BASIC
880 A$=""
882 GETB$:IFASC(B$)=13THENPRINT:RETURN
884 PRINTB$;:IFASC(B$)=127ANDA$<>""THENA$=LEFT$(A$,LEN(A$)-1):GOTO882
886 IFLEN(A$)<70THENA$=A$+B$
888 GOTO882

    ' Rutina USING para número de línea
890 PRINTRIGHT$("  "+STR$(Z),3);:RETURN
 
    ' Iniciar el ensamblador y cargar matrices
900 CLEAR:CLS:PRINT"6502 Assembler - (c)Scainet Soft, 2014":PRINT
902 DIMI$(60),C$(60,13),P$(3),L$(200,3),H$(200),Q$(50),M(50)
904 LI=1:LF=LI:H=200:P=0:N$="SOURCE"
906 FORF=1TO56:READI$(F),A$:FORI=1TOLEN(A$)STEP3
908 C$(F,ASC(MID$(A$,I,1))-96)=MID$(A$,I+1,2)
910 NEXT I,F:READI$(57),I$(58),I$(59),I$(60):GOTO700

    ' Salir del ensamblador
920 END

    ' Datos de las Instrucciones del 6502 y sus códigos hexadecimales
950 DATA"ADC","h61k65l75e69d79b6Dc7Di71","AND","h21i31k25l35e29d39b2Dc3D"
952 DATA"ASL","k06a0Ab0El16c1E","BIT","k24b2C","BRK","f00","BCC","j90"
954 DATA"BCS","jB0","BEQ","jF0","BMI","j30","BNE","jD0","BPL","j10"
956 DATA"BVC","j50","BVS","j70","CLC","f18","CLD","fD8","CLI","f58"
958 DATA"CLV","fB8","CMP","iD1kC5lD5eC9dD9bCDcDDhc1","CPX","kE9bECeE0"
960 DATA"CPY","hC0kC4bCC","DEC","kC6lD6bCEcDE","DEX","fCA","DEY","f88"
962 DATA"EOR","h41i51k45l55e49d59b4Dc5D","INC","kE6lF6bEEcFE","INX","fE8"
964 DATA"INY","fC8","JMP","b4Cg6C","JSR","b20"
966 DATA"LDA","hA1iB1kA5lB5eA9dB9bADcBD","LDX","eA2kA6mB6bAEdBE"
968 DATA"LDY","kA4lB4bACcBCeA0","LSR","k46l56a4Ab4Ec5E","NOP","fEA"
970 DATA"ORA","h01k05e09b0Di11l15d19c1D","PHA","f48","PHP","f08","PLA","f68"
972 DATA"PLP","f28","ROL","k26l36a2Ab2Ec3E","ROR","k66l76a6Ab6Ec7E"
974 DATA"RTI","f40","RTS","f60","SBC","hE1iF1kE5lF5eE9dF9bEDcFD","SEC","f38"
976 DATA"SED","fF8","SEI","f78","STA","h81k85l95d99b8Dc9Di91"
978 DATA"STX","k86m96b8E","STY","k84l94b8C","TAX","fAA","TAY","fA8"
980 DATA"TSX","fBA","TXA","f8A","TXS","f9A","TYA","f98"
982 DATA".BYTE",".WORD",".ASC","*="


APUNTES FINALES

La historia
En el año 1990 me compré 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 me propuse programar en BASIC tres ensambladores para los microprocesadores Z80, 6502 y 6809.

El primero lo programé usando un MSX. Analizando las listas de instrucciones y mnemotécnicos del Z80 vi que con unas fórmulas relativamente sencillas se puede generar el código hexadecimal de grandes grupos de instrucciones. También programé una versión básica de este ensamblador para el ZX-81 con 1K.

Este segundo ensamblador para el 6502 lo he desarrollado en un ORIC ATMOS (emulado). Analizando las listas de instrucciones pude comprobar que el ensamblado de sus instrucciones es considerablemente más sencillo que las del Z80, ya que todo queda contenido en una tabla de 56 filas (instrucciones) por 13 columnas (modos de direccionamiento). Conociendo la instrucción y el modo de direccionamiento usado tienes el código hexadecimal de la instrucción.


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 200 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 del ORIC
El ORIC permite usar toda la memoria, hasta la dirección $9800 para almacenar el programa en BASIC y las variables. Realmente se puede usar algo más si no vas a utilizar el modo gráfico, pero por si acaso me he puesto un límite. Así, con el “HIMEM $4000” de la línea 100 reservo los primeros 16 KB de RAM para el BASIC y las variables y matrices que use, quedando hasta la dirección $9800 para el código máquina.

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. Por desgracia es un proceso bastante lento y puede detener la ejecución del ensamblador unos 15 segundos.

La rutina de INPUT es muy simple y no admite los valores vacíos o el uso de caracteres “especiales” como la coma o la almohadilla “#”, entre otros. En otros BASIC se puede evitar con un LINE INPUT pero aquí no. Tampoco permite esconder el “?” que aparece al principio de la entrada de datos. Lo he evitado programado una sencilla rutina que funciona como quiero.

Los “bugs” de su intérprete han seguido complicándome la vida, pero con paciencia los he ido evitando.

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


Velocidad. ¿Como se consigue?
Este ensamblador puede llegar a ensamblar unas 200 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 6502 vemos que se pueden usar hasta 13 tipos de direccionamientos:
1. Acumulador: INSTRUCCION A
2. Absoluto: INSTRUCCION $HHLL
3. Absoluto índice X: INSTRUCCION $HHLL,X
4. Absoluto índice Y: INSTRUCCION $HHLL,Y
5. Inmediato: INSTRUCCION #$BB
6. Implícito: INSTRUCCION
7. Indirecto: INSTRUCCION ($HHLL)
8. Indirecto índice X: INSTRUCCION ($BB,X)
9. Indirecto índice Y: INSTRUCCION ($LL),Y
10. Relativo: INSTRUCCION $BB
11. Zero page: INSTRUCCION $LL
12. Zero page índice X: INSTRUCCION $LL,X
13. Zero page índice Y: INSTRUCCION $LL,Y

La primera decisión ha consistido en detectar el tipo de direccionamiento usado. En la mayoría de casos es muy simple.

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 aumentar la velocidad del ensamblador.

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.

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.

Al ensamblar el programa también hay la opción de indicar si queremos que se muestre por pantalla las líneas ensambladas. Si lo desactivamos con el comando “P” el proceso de ensamblado se realiza casi un 50% más rápido, pudiendo superar las 200 líneas por minuto.

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 las opciones de ensamblado, el proceso se acelera considerablemente.


Codificando el juego de instrucciones
El microprocesador 6502 dispone de un total de 56 instrucciones y admite 13 modos de direccionamiento distinto. Para almacenar esta información lo ideal es crear una matriz de 56x13 elementos. Las filas son las instrucciones y las columnas son los modos de direccionamiento. En cada celda introduciremos un valor hexadecimal entre #00 (0) y #FF (255) correspondiente a esa instrucción según el direccionamiento elegido. Como hay instrucciones que no admiten ciertos modos de direccionamiento, esa celda debería quedar vacía, y de esta forma podemos detectar un error al ensamblar.

Para introducir esta información se requieren 56x13=728 valores en nuestro programa ensamblador. Esto ocupa unos 2KB de memoria RAM. Como se puede ver en el gráfico siguiente, la mayoría de las posiciones de la matriz están vacías. Vamos a ver como se puede hacer para reducir esta información en un 80%.

opcodematrix_mini.jpg
opcodematrix_mini.jpg (312.01 KiB) Visto 3824 veces


A partir de la línea 950 del programa se introducen los valores mediante líneas DATA. Previamente se ha creado en la línea 902 la matriz I$(60) donde se guardan los nombres de las 56 instrucciones y las cuatro directivas del ensamblador (.BYTE, .WORD, .ASC y *=) y la matriz C$(60,13) donde se guarda la relación instrucción/direccionamiento.

Teniendo en cuenta que la mayoría de instrucciones no admiten más de uno o dos modos de direccionamiento es absurdo perder espacio indicando valores vacíos. Para ello, los valores del DATA se leen de dos en dos. El primer valor contiene en nombre de la instrucción y se guarda en I$(F), y el segundo valor contiene una cadena de caracteres indicando los direccionamientos que usa, en grupos de 3 caracteres. El primer carácter de la izquierda es una letra minúscula entre “a” y “m”, que corresponden a los ASCII 97 y 109. Restando 96 a este valor ASCII obtenemos un valor entre 1 y 13, que corresponde a la columna. Los dos caracteres de la derecha corresponden al código hexadecimal de la instrucción/direccionamiento. Así que solo queda poner este valor en la posición correspondiente de la matriz. Con el CLEAR de la línea 900 hacemos que inicialmente todas las casillas de la matriz estén vacías y de esta forma solo asignaremos un valor a las que realmente lo deban tener.

De esta forma ahorramos muchas líneas de código, y el tiempo que tarda en procesar la información de esta forma compensa el que perdería en leer 5 veces más información. Total, que en unos 7 segundos tenemos el ensamblador listo para usar.


Guardando los programas
Otra de las gracias del BASIC del ORIC es que no permite guardar información en ficheros secuenciales, como si lo permite la mayoría de intérpretes en otros equipos.

Para sortear este nuevo problema he optado por volcar el contenido de la matriz L$(200,3) que contiene las líneas del programa divididas en tres partes (ID instrucción, primer parámetro, segundo parámetro) en una posición de memoria por encima del RAMTOP del BASIC. Al final de cada parámetro guardo un valor “9” y al finalizar el volcado grabo ese bloque de memoria como un bloque de bytes en cinta.

Para recuperar hago el proceso inverso. Cargo el bloque de bytes de la cinta en RAM y a continuación voy leyendo la memoria. Cada vez que encuentro un valor “9” cambio de columna en la matriz L$.

El proceso no es todo lo rápido que me gustaría pero funciona, y mientras se realiza el acceso de lectura o escritura en memoria presento el mensaje de “Wait” y un punto por cada línea que se ha procesado. A falta de pan buenas son tortas.


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. Es idéntico al usado en la versión del Z80 en MSX, pero he quitado alguna opción ya que en este caso no se usa disquetera ni se puede cambiar el número de columnas de pantalla.

El programa en BASIC esta comprendido entre las líneas 100 y 982, 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. Así que... Prueba superada ¡!!

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

Os invito a probarlo.

6502_1.gif
6502_1.gif (6.19 KiB) Visto 3824 veces

6502_4.gif
6502_4.gif (6.58 KiB) Visto 3824 veces

6502_3.gif
6502_3.gif (14.1 KiB) Visto 3824 veces
Buscando la IP de la W.O.P.R.

Avatar de Usuario
radastan
Amiga 2500
Amiga 2500
Mensajes: 4542
Registrado: 11 Jun 2007, 19:29
Sistema Favorito: Spectrum 16Kb/48Kb
primer_sistema: Spectrum 16Kb/48Kb
consola_favorita: Sega Genesis/Megadrive
Primera consola: TV Games/Pong Clone
Ubicación: Córdoba
Gracias dadas: 9 veces
Gracias recibidas: 40 veces
Contactar:

Re: 6502-Assembler para ORIC

Mensajepor radastan » 21 May 2014, 15:03

Es tan sencillo, parece tan fácil, es tan bonito, que da envidia sana. Te ha quedado bordado.

Tu no desayunabas Cola Cao y una tostada, a ti te daban dos buenos Bollycaos. Se nota.

Ahora a por una versión para QL... :-ss
Yo tengo una máquina del tiempo, se llama ZX Spectrum, siempre me devuelve a los buenos momentos.
(\.../) (\.../) (\.../) (\.../)
( *.*) ('.'= ) ('.'= ) ('.'= )
(")_(") (")_(") (")_(") (")_(")
╔═══╦═══╦═══╦══╦══╗
║╔═╗║╔═╗║╔═╗╠╣╠╩╣╠╝
║║─║║╚══╣║─╚╝║║─║║
║╚═╝╠══╗║║─╔╗║║─║║
║╔═╗║╚═╝║╚═╝╠╣╠╦╣╠╗
╚╝─╚╩═══╩═══╩══╩══╝

dancresp
Amiga 1200
Amiga 1200
Mensajes: 1393
Registrado: 23 Dic 2008, 17:53
Sistema Favorito: MSX
primer_sistema: ZX81
Primera consola: Atari 2600
Gracias dadas: 3 veces
Gracias recibidas: 20 veces

Re: 6502-Assembler para ORIC

Mensajepor dancresp » 21 May 2014, 20:59

radastan escribió:Tu no desayunabas Cola Cao y una tostada, a ti te daban dos buenos Bollycaos. Se nota.

Ahora a por una versión para QL... :-ss

Nocilla, merendaba Nocilla !!! =P~

QL no lo se, pero después del Z80 y este del 6502, ahora le toca al 6809 del DRAGON... :tecle:
Buscando la IP de la W.O.P.R.


Volver a “Programación”

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 6 invitados