Sugerencias y sugerencias de programación en lenguaje ensamblador [cerrado]

Estoy intentando escribir mi propio sistema operativo de "juguete" y, por el momento, lo estoy haciendo principalmente en ensamblaje (NASM), en parte porque espero que me ayude a comprender el desensamblaje de x86 y también porque estoy encontrando ¡También es bastante divertido!

Esta es mi primera experiencia programando en ensamblador: estoy aprendiendo las cosas más rápido de lo que esperaba, sin embargo, al aprender cualquier lenguaje significativamente diferente, descubro que mi código está estructurado de manera bastante caótica mientras trato de averiguar qué patrones y convenciones debería estar usando.

En este momento en particular estoy luchando con:

Hacer un seguimiento de los registros

Por el momento, todo está en modo de 16 bits, por lo que solo tengo 6 registros de propósito general con los que jugar, y aún menos de los que se pueden usar para acceder a la memoria. Sigo pisoteando mis propios registros, lo que a su vez significa que estoy intercambiando registros con frecuencia para evitar esto; en consecuencia, me cuesta mucho hacer un seguimiento de qué registros contienen qué valores, incluso con comentarios liberales. ¿Esto es normal? ¿Hay algo que pueda hacer para facilitar el seguimiento de las cosas?

Por ejemplo, comencé a comentar todas mis funciones con una lista de los registros que están golpeados:

; ================
; c_lba_chs
; Converts logical block addressing to Cylinder / Head / Selector
;  ax (input, clobbered) - LBA
;  ch (output) - Track number (cylinder)
;  cl (output) - Sector number
;  dh (output) - Head number
; ================

Hacer un seguimiento de la pila

En un par de casos, comencé a usar la pila cuando me quedé sin registros, pero esto está empeorando las cosas, algo más complejo que un simple push call pop La secuencia para preservar los registros hace que pierda la pista por completo, lo que dificulta incluso saber si tengo la cantidad correcta de elementos en la pila (particularmente cuando se trata de manejo de errores, ver más abajo), y mucho menos en qué orden están. Sé que debe haber una mejor manera de usar la pila, simplemente no puedo ver cuál es.

Errores de manejo

He estado usando la bandera de acarreo y la bandera de cero (según la función) para indicar un error a la persona que llama, por ejemplo:

myfn:
    ; Do things
    jz .error
    ; Do more things
    ret

    .error:
        stc
        ret

¿Es esta una forma normal de indicar errores?

¿También hay otras sugerencias o trucos que pueda utilizar para estructurar mejor mi ensamblaje?

Finalmente, ¿hay buenos recursos / ejemplos de ensamblaje bien escrito? Me he encontrado El arte de la programación en lenguaje ensamblador sin embargo, parece centrarse mucho en el meollo del lenguaje con menos énfasis en cómo se debe estructurar el código. (También algunos de los ejemplos de código usan segmentos, que creo que debería evitar).

Estoy haciendo todo esto usando segmentos cero (un modelo de memoria plana) para mantener las cosas simples y facilitar las cosas si / cuando empiezo a usar C.

preguntado el 10 de mayo de 11 a las 14:05

Puede utilizar la memoria para las variables. No es necesario forzar todo en los registros o en la pila. -

1 Respuestas

No se preocupe, está en el camino correcto. Al ser ensamblador, puede hacer lo que quiera para que tenga la libertad de decidir cómo desea administrar sus registros y datos. Recomendaría desarrollar algún estándar para usted, y usar un estándar similar a C puede que no sea una mala idea. También recomendaría usar un lenguaje ensamblador diferente para un primer proyecto como este (por ejemplo, ARM ejecutándose en qemu), x86 es algo horrible en cuanto a conjuntos de instrucciones. pero ese es un tema aparte ...

Los ensambladores generalmente le permiten declarar variables si lo desea, memoria con nombres:

bob: .word 0x1234

Luego, del ensamblador (usando ARM asm aquí)

ldr r0,bob
add r0,#1
str r0,bob

Los registros se utilizan temporalmente, los datos reales se guardan en la memoria. Un modelo como este puede ayudar a realizar un seguimiento de las cosas, ya que los datos reales se guardan en la memoria con nombres de variables creados por el usuario, como en un lenguaje de alto nivel. x86 hace que esto sea aún más fácil, ya que puede realizar operaciones en la memoria y no tener que pasar por registros para todo. Del mismo modo, puede administrar esto con un marco de pila para las variables locales, reste algún número de la pila para cubrir su marco de pila para esa función, y dentro de esa función, sepa / recuerde que la variable joe es el puntero de pila +4 y ted es el puntero de pila +8, etc. Probablemente use comentarios en su código para recordar dónde están estas cosas. Recordando restaurar el puntero / marco de la pila a su punto de entrada antes de regresar. Este método es un poco más complicado ya que no utiliza nombres de variables sino compensaciones numéricas. Pero proporciona variables locales y recursividad y / o algunos ahorros de memoria global.

Al hacer este trabajo como humano con sus ojos y manos (teclado y mouse), probablemente desee mantener los datos en un registro no más de lo que puede caber en la pantalla de su editor de texto a la vez, de un vistazo vea la variable ir a el registro luego regresa a la variable en la memoria, todo de un vistazo. Un programa / compilador ciertamente puede realizar un seguimiento de la cantidad de memoria que tiene en el sistema, mucho más que un humano. Es por eso que los compiladores en promedio generan mejores ensambladores que los humanos (casos específicos, los humanos siempre pueden modificar o solucionar un problema).

Manejo de errores, debe tener cuidado con el uso de banderas, no me parece correcto por alguna razón. Puede muy bien estar bien, las interrupciones preservan las banderas, su código tendrá que preservar o configurar las banderas, etc. Hmm, el problema con las banderas es que tiene que verificar / usar ese valor de retorno inmediatamente después de que la función regrese, antes tienes una instrucción que modifica las banderas. donde si usa un registro, puede optar por no modificar ese registro de retorno para muchas más instrucciones antes de que necesite muestrear o usar ese valor de retorno.

Creo que la conclusión aquí es que, mire las reglas de la convención de llamadas de C que los compiladores usan para ese conjunto de instrucciones, y quizás otros conjuntos de instrucciones, verá fuertes similitudes y por una buena razón. Son manejables. Con tan pocos registros, puede ver por qué las convenciones de llamada a veces van directamente a la pila para todos los argumentos y, a veces, también los valores de retorno. Me dijeron que la bios de Amiga tenía una convención de llamada personalizada para cada función de la bios, lo que lo convertía en un sistema de ejecución ajustado y rápido, pero que intentaba recrear la bios en C usando compiladores y / o adjuntar a funciones con una envoltura de ensamblador. es difícil en el mejor de los casos. Estoy seguro de que sin una buena documentación sobre cada función, es inmanejable. En el futuro, puede decidir que desea este portátil y puede desear haber elegido una convención de llamadas de uso común. Todavía querrá comentar su código para decir que el parámetro 1 es este y el parámetro 2 es aquello, etc. Por otro lado, si actualmente o en el pasado ha programado un ensamblador x86 llamando a DOS y llamadas al BIOS, se sentiría bastante cómodo mirando subir cada función en una referencia y colocar los datos en los registros adecuados para cada función. Debido a que había buenos materiales de referencia, fue posible personalizar cada función.

contestado el 10 de mayo de 11 a las 18:05

Quizás tenga sentido usar EQU para las variables locales? Puedes usar EQU con etiquetas locales? - ninjalj

Creo que un equ es más como una macro #define en C, hace un reemplazo en la primera pasada de ensamblado, no asigna memoria. - viejo contador de tiempo

Quiero decir, si tu variable local foo Está en [EBP-8], Podrías hacerlo .foo EQU -8 - ninjalj

@ninjalj, ahh, sí, ya veo, esa es una buena idea. - viejo contador de tiempo

¿Puedes echarle un vistazo a esto? stackoverflow.com/questions/29708532/… - comprometido

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.