Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Mensajes - ~

#31
ASM / Re: Modo protegido, dolor de cabeza
4 Marzo 2013, 17:34 PM
Cita de: lweb20 en  4 Marzo 2013, 17:08 PM
Mmm. Amigo no lo tomes a mal, pero sobre el modo usuario sólo es una alternativa para aislar las aplicaciones normales del kernel. No estoy TAN (!) obsesionado con eso. Sé lo que me dices. Llevo meses con ese kernel y he estudiado la FAT como tu.
El hecho es que un modo usuario es más estorbo que algo de valor si no se acompaña de una buena multitarea, paginación, administración de memoria y permisos bien definidos, tanto de memoria, de software, como de puertos I/O.

Estoy intentando iniciar una discusión que pueda ayudarnos a todos. Quiero publicar código y discutir estándares. Está garantizado aprender más que los principiantes de OSDev de esta forma, dado que ni ahí se discute tan plenamente como estoy planteando. Hacer preguntas aisladas como las de estos errores jamás ha ayudado a un principiante. Cientos de principiantes con dudas más y menos complejas no han llegado a ningún lugar de esa forma, y simplemente han dejado de preguntar, uno y otro, y otro.

Este tema podría mantenerse vivo por años si se discute más que esos simples errores de dedo (yo también quiero divertirme ayudando sobre sistemas operativos que es una de las cosas que más me interesan, aprendiendo y haciendo que esto sea cada vez más fácil, con ayuda entre todos).

De lo contrario, si no hay interés en discutir a mayor nivel, puedo garantizar que este tema y el kernel del que hablamos no podrían normalmente durar ni 6 meses más, y mucho menos avanzar el código y el conocimiento de forma notable.





Si solamente son meses los que tiene el kernel pero sin ninguna otra base de experiencia anterior, es natural ver las cosas tan simplemente. Pero no es suficiente ni de cerca.

Yo tengo casi una década tratando de estudiar sobre sistemas operativos, y solo recientemente es que he comenzado realmente a poder desenvolverme y entender en todos los requerimientos, de forma más completa. Y los tutoriales y foros (OSDev, OSDever, Wikipedia, BrokenThorn, el tutorial de JamesM, y el resto de conocidos), no tienen suficiente información (solo hace falta buscar "¿qué efecto tiene cada registro de la VGA y cómo se programa?" para darse cuenta que la mejor información no está tan pública, y sin eso no se llega a ningún lado).

El hecho no es que haya estudiado la FAT. Yo entiendo la FAT12 hasta el nivel de leer el directorio raíz. ¿Este kernel actual tiene más capacidad que eso? Desde ahí se puede aprender mucho más.
#32
ASM / Re: Modo protegido, dolor de cabeza
4 Marzo 2013, 16:56 PM
Editado por un detalle práctico que olvidé: Para modo de 64 bits, para poder ejecutar interrupciones como las de video, no hay más opción que implementar o usar un emulador como x86emu (referenciado ampliamente en la Wiki y el foro de OSDev):

x86emu.zip

Por esto aconsejo ir rápido, pero también escuchar lo que otros tenemos que decir en lo que hemos intentado antes. En otras palabras, es mejor olvidarse en mayor medida del modo v86 virtual del modo de 32 bits y usar emulación de software, si uno no tiene drivers de video y aun así quiere poder cambiar modos de video.





Cita de: lweb20 en  4 Marzo 2013, 15:39 PM
Todo el kernel está muy estable (obviamente el manual me ayudó un montón). Muchas cosas no las entendía pero poco a poco mientras modificaba el kernel tuve que leer un poco más para pasarlo a español las funciones y tratar de implementar el lenguaje C++ (orientado a objetos). Y discúlpenme pero creo que está mal ubicado el tema en asm (yo lo puse en C). Uso el IDE de Visual Studio porque acostumbro a crear múltiples algoritmos en un solo programa y así tener una gran librería ordenada. Para mi es bueno asm (ya que es ensamblador y no compilador, nasm y masm sí los comprendo muy bien). Comprendo lo de no usar tantas herramientas, pero mi entorno preferido, como dije, es y será siendo Visual Studio (>2010) ya que con él aprendí a programar. Y respecto a las referencias pues son las que todo el mundo usa. Osdev, Osdever, Wikipedia, Wikibooks, El tutorial de JamesM, entre otros. Existe otra que como veo no muchos la conocen y como veo que mucho insisten (no lo tomen a mal) no veo por qué no mostrarlo (Brokenthorn). Bueno ya lo dije, ejem.
BokenThorn también es una referencia común que todos conocemos, y es muy similar a la de JamesM.

Sobre Visual Studio, al fin de cuentas sería irrelevante en un nuevo sistema operativo, y sería necesario aprender otras herramientas, incluso propias. De lo contrario uno sería demasiado dependiente, el sistema sería débil también en ese aspecto, y de todas maneras Visual Studio o cualquier herramienta son lo de menos cuando uno escribe un sistema operativo, tratando de entender todos los formatos, convenciones y estándares por uno mismo.

Es mejor dejar de depender de estas herramientas en este ámbito. Yo también empecé programando en Visual Basic 6, pero ahora no pienso sino en que si uso esos lenguajes, sea yo quien los implemente, o mi esfuerzo quedaría atrapado esclavo a esas herramientas y lenguajes, en lugar de estar totalmente a mi cargo y ser libre.

Cita de: lweb20 en  4 Marzo 2013, 15:39 PM¿Qué es lo que pretendo realmente hacer con el formato PE?
- Estudiarlo poco a poco para más adelante crear mi propio formato más liviano. Por qué digo más liviano: Porque para mi ese formato ocupa demasiado espacio en el disco y de esta forma el sistema operativo, aparte del tiempo que demora en buscar el archivo en la unidad de almacenamiento, demora en leer el archivo (ya que probablemente esté dividido en varios fragmentos) y cargarlo en la memoria.
Yo no recomendaría crear un formato propio de ejecutable. Incluso gente mucho más experta evita hacer esto, simplemente porque esto representa una desconexión total con el resto del mundo informático.

Una vez tras otra he comprobado que solo porque crear algo como un formato me resulte aparentemente más fácil que entender otro que alguien más hizo, y que todo el mundo usa, no significa que diseñarlo sea conveniente, comenzando porque será mucho menos robusto y nadie más lo va a usar (en otras palabras, un punto de muerte para un proyecto).

Pero tal vez sirva este formato de ejecutable que he creado para mi kernel. Este es capaz de importar funciones del kernel. Hasta ahora no necesito modo de usuario ni multitareas porque estoy aprendiendo hasta los detalles más intrincados del hardware, y para asegurarme de que mi entendimiento en eso no tenga fallas, estoy creando un emulador de PC.


Este es el programa de muestra, y se puede definir perfectamente en C++ o en C, etc:;v2012-06-29
;
;This is a version of CMDARGS2 that uses only imported functions
;from the kernel, and a new, slightly expanded header, to make possible
;for the kernel to link functions/variables imported by the application,
;for itself, instead of having the application do that.
;;


APPBASE equ 1048576*2

bits 32
org APPBASE



%include "include/external_exported_variables.inc"
%include "include/external_exported_functions.inc"



;Our kernel must read this address, jump
;to it and then skip it:
;;
IntendedBaseAddress  dq APPBASE
NumberOfFieldsBelow  dq 7
CommandLinePtr       dq 0
CommandLineLength    dq 0
CommandLineParamsPtr dq 0
KernelFnExportsTable dq 0
KernelVrExportsTable dq 0
AppFnImportsTable    dq myImportsFunctionsTable
AppVrImportsTable    dq 0



;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++





;Clean the screen:
;;
call dword[clrscr]





;;;INIT: Print each command line parameter in a separate line
;;;INIT: Print each command line parameter in a separate line
;;;INIT: Print each command line parameter in a separate line
;;;INIT: Print each command line parameter in a separate line





;Prepare to show the command line, one parameter a line.
;See if the length of the application's command line can possibly
;contain parameters. If it is 11 bytes or less, it cannot contain
;parameters as of our current kernel:
;;
mov eax,[CommandLineLength]
cmp dword[eax],11
jbe .noparams



;Get rid of the application's raw FAT file name,
;as of our current kernel:
;;
mov esi,[CommandLinePtr]
call dword[adjustCommandLineFATname]

;If we detected that there are no parameters after
;stripping the file name, end:
;;
cmp eax,0
 je .noparams






;Print the received command line:
;;
;      ESI == ASCIIZ string to print
;
;;
 ;Also get rid of the application's command line in our
 ;command line pointer:
 ;;
  mov esi,eax
  mov [CommandLinePtr],esi
  call dword[console_kprint_visible]


;Go to a new line:
;;
call dword[console_doCRLFeffect]




;Get the number of parameters in the command line. At this
;point there cannot possibly be an parameter-less command line:
;;
;Inputs:
;      ESI == ASCIIZ string
;
;Outputs:
;      EAX == Number of present parameters in the command line,
;             each other separated with blank spaces.
;;
 mov esi,[CommandLinePtr]
  call dword[getCommandLineArgsCount]
   mov ebx,eax
   xor ecx,ecx


;Set our destination pointer to copy the specified
;parameters one by one:
;;
mov edi,cmdstrbuff





;Do a loop to print all of the command line parameters
;individually:
;;
.parloop:



;Copy the current argument:
;;
 ;Inputs:
 ;       ESI == Command line string
 ;       EDI == String buffer to copy the argument to
 ;       ECX == Argument number (from 0)
 ;
 ;
 ;Outputs:
 ;       EAX == Nonzero if the argument was found, or 0 if the argument was not found
 ;
 ;;
    call dword[copyCommandLineArg]



;Print the argument we just copied:
;;
 ;Print the received command line:
 ;;
  ;      ESI == ASCIIZ string to print
  ;
  ;;
   xchg esi,edi
    call dword[console_kprint_visible]
    call dword[console_doCRLFeffect]
   xchg esi,edi



;Go to the next parameter. If it is still
;below the natural parameter count, keep
;looping:
;;
 inc ecx
 cmp ecx,ebx
 jb .parloop






;;;END:  Print each command line parameter in a separate line
;;;END:  Print each command line parameter in a separate line
;;;END:  Print each command line parameter in a separate line
;;;END:  Print each command line parameter in a separate line



;We will jump here if our program has no parameters, which means
;that this program won't do anything if we have no command line
;parameters:
;;
.noparams:





;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++







;Return control to the kernel:
;;
ret







;;;INIT: This is the most decent place to put application variables and data without memory management and without code jumps
;;;INIT: This is the most decent place to put application variables and data without memory management and without code jumps
;;;INIT: This is the most decent place to put application variables and data without memory management and without code jumps
;;;INIT: This is the most decent place to put application variables and data without memory management and without code jumps
;;;INIT: This is the most decent place to put application variables and data without memory management and without code jumps
;;;INIT: This is the most decent place to put application variables and data without memory management and without code jumps
;;;INIT: This is the most decent place to put application variables and data without memory management and without code jumps
;;;INIT: This is the most decent place to put application variables and data without memory management and without code jumps










myImportsFunctionsTable:
                  ImportsCount dd 6
                          clrscr dd clrscr@KernelCore
          console_kprint_visible dd console_kprint_visible@KernelCore
          console_doCRLFeffect   dd console_doCRLFeffect@KernelCore
adjustCommandLineFATname          dd adjustCommandLineFATname@KernelCore
getCommandLineArgsCount           dd getCommandLineArgsCount@KernelCore
copyCommandLineArg                dd copyCommandLineArg@KernelCore






cmdstrbuff times 1024 db 0










;;;END:  This is the most decent place to put application variables and data without memory management and without code jumps
;;;END:  This is the most decent place to put application variables and data without memory management and without code jumps
;;;END:  This is the most decent place to put application variables and data without memory management and without code jumps
;;;END:  This is the most decent place to put application variables and data without memory management and without code jumps
;;;END:  This is the most decent place to put application variables and data without memory management and without code jumps
;;;END:  This is the most decent place to put application variables and data without memory management and without code jumps
;;;END:  This is the most decent place to put application variables and data without memory management and without code jumps
;;;END:  This is the most decent place to put application variables and data without memory management and without code jumps


Cita de: lweb20 en  4 Marzo 2013, 15:39 PM- Crear una librería para mi sistema operativo y así crear aplicaciones en base a mi sistema operativo y de esta forma facilitar el llamado a syscall. Por ejemplo para leer un archivo usaría una sintaxis muy simple y en español.

Archivo archivo = SysAbrirArchivo("C:\\archivo.txt");
char* buffer = malloc(Archivo->Tamaño);
SysEstablecerPosiciónArchivo(archivo, 0); // es como un seek
SysLeerArchivo(archivo, buffer, Archivo->Tamaño);
SysCerrarArchivo(archivo);


Algo súper simple y entendible.

Todo esto lo digo porque tuve la oportunidad de leer un sistema operativo hecho en ASM con librería en C de 64 bits (que seguramente muchos saben de cuál se trata) que tenía syscalls e hice algunos programas en base a ese sistema (nasm y gcc).
Eso parece simple pero crear una librería capaz no solo de crear manejadores de archivo de forma estable, sino entender el formato del disco es algo avanzado, y no necesita modo de usuario para poder experimentar con esos algoritmos. Incluso en FAT12, definir unidades e ir más allá del directorio raíz necesita más estudio.

Yo ofrezco definir una investigación en este tema sobre lo que sé (leer archivos del directorio raíz de FAT12), y desde ahí definir cómo se logra más que eso, de forma implementada, con código y explicaciones reales.





Y sobre Bochs, este tiene una instrucción o un puerto de depuración, pero nunca lo he usado porque lo considero contaminar el código.

Lo que hago simplemente es hacer microprogramas con funciones de una futura librería, y los depuro incluso bajo Windows, o usando javascript, o en DOS. De esta forma es mucho más fácil saber qué está mal.

También es posible hacer un dump de la memoria de Bochs, y examinar si esta contiene la estructura que uno esperaría (especialmente en versiones de Bochs cercanas a la 2.6, etc.).





Todo esto es algo que puedo hacer (leer un programa del floppy y ejecutarlo). Tal vez hay problemas con el código de modo usuario, pero sin ver nada de código, y menos aun sin saber la lógica con la que se ha pensado, es imposible saber o ayudar demasiado.

Como digo, para apdender sobre muchos algoritmos, incluso cargar programas de forma multitarea, no requiere modo usuario (y se puede comenzar a hacer algo útil para un kernel que realmente uno vaya a usar de forma entusiasta y que aporte conocimiento -necesario para avanzar con paso firme, evitando estos errores de principiantes y superarlos a lo sumo en 1 año en lugar de en mucho mas que eso-, como visualizar GIFs, BMPs, documentos de texto, iconos, y trucos de hardware, entrar a modo de 64 bits, crear compiladores o ensambladores simples, etc.).

La idea es que lo que se aprenda en el lado de un kernel propio sea usable en cualquier otro entorno. O sino, ¿cuál sería el objetivo, y cuál sería el verdadero beneficio más allá de crear un kernel simple, que muchos hemos creado, pero de forma incompleta e inestable (inestable en que no lo usaremos demasiado ni podríamos fácilmente agregarle demasiadas cosas útiles)?
#33
ASM / Re: Modo protegido, dolor de cabeza
4 Marzo 2013, 15:19 PM
Yo quiero ayudar de forma práctica y real, así que pido por favor poner todas las referencias y URLs que han servido para implementar ese kernel.


  • ¿Dónde está la información que se usó para crear el kernel básico?

  • ¿Dónde está la información que se usó para pasar a modo de usuario?

  • ¿Dónde está la información que se usó para correr aplicaciones?

  • ¿Qué otra información se usó?
Con esto se puede garantizar ayudar de forma excepcionalmente más fácil, y compartir conocimiento a nivel de implementación, más allá de la teoría cruda.

Yo también pongo unas referencias en este momento, sobre lo que me sirvió para lograr crear programas PE 100% en ensamblador, sin ayuda de ninguna otra librería ni herramienta.

TinyPE (vital)
Todos los artículos de Matt Pietrek sobre PE
Las especificaciones PE/COFF

Gracias a esto puedo crear archivos ejecutables de Windows, desde Windows, DOS o Linux, o cualquier otro sistema que tenga NASM, y sin tantas herramientas.





Cita de: lweb20 en  4 Marzo 2013, 14:01 PM
La idea de ejecutar el formato PE no es cargar sus librerías en la memoria y hacerlo todo como Windows (por decirlo así), más bien es crear un programa en Visual Studio y poder ejecutarlo desde mi sistema operativo; ya que me parece mucho más sencillo el asm inline de Visual Studio que el de gcc (y además es mi IDE favorito).
¿Para qué usar Visual Studio solo para crear un Portable Executable? Es posible que le agregue mucha basura. Yo prefiero crear los PE directamente con ensamblador, 100% sin linkers ni compiladores.

Aquí tengo una muestra de lo que estoy hablando:

cuentareloj.asm

Este programa lo ensamblamos así:

nasm cuentareloj.asm -o cuentareloj.exe


Este incluso se genera automáticamente conteniendo el icono mostrado antes, sin usar linkers ni GCC ni muchísimo menos Visual Studio, y contiene todo lo que seguramente se necestaría para lo que se quiere lograr aquí.


Esto no quiere decir que no pueda usar GCC y NASM, y evitar también el ensamblador inline de GCC. Después de todo es lo más conveniente (comenzar creando programas desde cero por uno mismo) porque de todas maneras uno va a tener que crear un loader capaz de entender el formato.

Otros también recomendarían más darle soporte a los ejecutables ELF, y también sería una buena idea, dado que muchos programas open source y librerías probablemente corran mejor (sin bugs extraños) si simplemente se compilan a ELF.


Cita de: lweb20 en  4 Marzo 2013, 14:01 PMEn cuanto a lo del syscall ya entendí. Déjenme solucionar algunos problemas con mi so. He detectado varios al desactivar la optimización: problemas con el stack, etc..

Ahora la última pregunta hasta que regrese de solucionar estos problemillas: ¿es posible ejecutar una interrupción que ejecute operaciones administrativas del IVT desde el modo usuario (RING3)? Tengo claro que sí es posible ejecutar una interrupción desde el modo usuario, lo que no tengo muy claro es por ejemplo si mediante esa interrupción puedo por ejemplo cargar una nueva GDT, o reiniciar el sistema (administración del sistema, por decirlo así). Ahora esto lo puedo detectar cuando solucione problemas que tengo al entrar al modo usuario, que extrañamente ya no me funciona (se queda colgado o sino error de protección general). Hasta pronto.
Claro que es posible, como se explicaba en el tutorial de JamesM y en muchas otras referencias.

Pero es seguro que esos problemas no sean tan pequeños como parecen.

El problema es que entrar en modo usuario y en multitareas puede ser educativo, pero si estamos en la misma situación en la que yo estuve, sería un error, sería contraproducente pasar tan rápido a modo usuario sin tener un sistema sólido sobre el cual trabajar.

Esto hará mucho más difícil, casi imposible, aprender a usar paginación e implementar permisos apropiadamente, o administrar el hardware, o usar algoritmos ya avanzados sobre administración de memoria.

Es mejor seguir en modo kernel, y documentar todo el diseño y trucos de hardware, y seguir aprendiendo. Esto es lo que yo he estado haciendo, y sé que de esta forma no puedo perder el tiempo reescribiendo todo cuando se vuelva un desastre.

En este momento, es más útil implementar las capas de bajo nivel, no sirve de nada para llevar a cabo ese tipo de programación, y dejar el modo de usuario hasta el final (puede ser más difícil y ligeramente educativo, pero puede ser un enorme estorbo para consolidar un kernel).
#34
Un detalle del que me di cuenta al correr este programa en Firefox y en Chrome, es que Chrome e Internet Explorer no soportan argumentos de funciones con valores predeterminados, pero Firefox sí, y es posible que esta característica sea parte de ECMAScript 6.

Por detalles como estos es que siempre prefiero usar características y sintaxis más al estilo de C, para que el programa sea más portable a otros lenguajes, y no propenso a estos detalles técnicos.

Así que lo siguiente no corre bien en Chrome, pero sí en Firefox (en otras palabras, es mejor no usarlo, tal como si usáramos la simplicidad de C):function test(var1, var2=0xFFFF)
{
alert("var2 "+var2);
}

test();


javascript Functions and default parameters, not working in IE and Chrome

No deja de ser algo muy inesperado, especialmente de Chrome (que se supone que incluso tiene una implementación completa de HTML5), porque esto claramente puede dar problemas a mucho código existente.


Incluso PHP soporta valores de argumentos de función predeterminados: <?php
    
function doFoo($Name "Paul") {
        return 
"Foo $Name!\n";
    }

    
doFoo();
    
doFoo("Paul");
    
doFoo("Andrew");
?>
#35
Tal vez Tor Browser Bundle (como Firefox Portable).

Lo único es que no tiene soporte para flash y prácticamente no soporta HTML5 (tal vez realmente se ha quedado en la versión 10 de Firefox).
#36
ASM / Re: Modo protegido, dolor de cabeza
4 Marzo 2013, 04:09 AM
Tal vez sería mejor desarrollar el tutorial de JamesM (James Molloy) para tener una base más sólida de conocimiento. Sé que lo voy a estudiar debido a preguntas como esta.

Especialmente la sección de llamadas de sistema:

10. User Mode

Aquí una porción de información importante:

CitarLlamadas de Sistema

El código que corre en modo usuario no puede correr código que está en, o accede, un área solo de supervisor (mira las banderas de entrada de tabla de páginas) ni ningún código que se instrucciones privilegiadas tales como HLT. Por lo tanto, la mayoría de kernels proporcionan una interfaz por la cual funciones comunes pueden ejecutarse. Una llamada del kernel a través de esta interfaz es llamada "llamada de sistema" o "system call".

La manera histórica, simple, y aún usada ampliamente para implementar llamadas de sistema en x86 es usar interrupciones de software. El programa de usuario establecerá un registro para indicar qué función de sistema desearía ejecutar, luego establecer parámetros en otros registros. Entonces ejecutaría una interrupción de software a un vector específico - Linux usa 0x80. La interrupción de software causa un cambio de modo al ring 0 - el kernel tendrá un manejador para este vector de interrupción, y despachará la llamada de sistema apropiadamente.

Algo importante a notar es que el kernel, cuando ejecuta código de manejo de interrupciones, requiere una pila válida con la cual trabajar. Si no tiene una, el procesador generará un double fault (y eventualmente un triple fault porque el manejador de double fault también necesita una pila válida). Esta sería obviamente una manera muy fácil por la que un usuario malintencionado podría bloquear tu sistema, así que es práctica normal cambiar a una nueva pila, en el momento de cambio demodo de ring 3 a ring 0, diseñada solamente para ser usada por el kernel, y que se garantiza que es válida.

Obviamente si deseas que tu kernel sea multitareas/preemptible (es decir, si deseas ser capaz de cambiar de tareas mientras ejecutas código dentro del kernel), necesitarás una de estas pilas de kernel por tarea, o terminarás sobreescribiendo los datos de una tarea cuando ejecutes otra tarea.


Es natural que casi solo haya material sobre Windows y Linux, no para un kernel personalizado, así que hay que partir de esa información y de los manuales de Intel.

Pero sí es algo bastante difícil de lograr, especialmente si no se tiene más base de código que la de unos pocos meses (y casi imposible de lograr de forma sostenible con dudas de información incluso de cómo programar los puertos de Entrada/Salida).

Los manuales de Intel son todo lo que se necesita para entender de manera detallada esas cosas. Y después vienen muchos tutoriales y otros estándares:

Manuales de Intel (comenzar por 25366617_2A.pdf, 25366717_2B.pdf y 25366517_1.pdf)

___________________
___________________
Y sobre terminar de golpe un sistema operativo, no se trata de eso, pero la actitud de tener que avanzar "poco a poco" es inadecuada (la actitud de "si es algo difícil debe tardarse tanto como sea posible" en lugar de "las cosas más difíciles deben buscarse por los medios más abundantes y comenzar a implementarse por lo que se necesita de forma más inmediata, más que hundirse en un mar de teoría que no es inmediatamente aplicable").

Es mejor decidirse a avanzar con mayor rapidez y en cosas inmediatamente aplicables tanto en sistemas operativos y en la programación normal, y recibir ayuda de otros (además de dar cualquier ayuda que se pueda a otros como colectivo), y hacer un esfuerzo concreto en el presente, pero inteligente y reusable, en lugar de pasar años en los fundamentos, en lugar de usar ese tiempo en lograr implementar algo usable que permita avanzar de forma mucho más eficiente. Algo difícil en extremo de lograr para la mayoría de principiantes.

Esto es lo que puedo decir de muchos años de estar en el esfuerzo de tratar de aprender sobre el desarrollo de sistemas operativos.
#37
Aquí tengo una muestra de codificación de Base64 en javascript con funciones personalizadas que yo escribí, para mostrar el algoritmo. Traducirla a PHP no debería ser difícil.

Muestra de Base64 en javascript

Aunque también se puede usar atob (decodificar) o btoa (codificar) en javascript, o sus equivalentes de PHP.





Este algoritmo que tengo aquí funciona para absolutamente cualquier valor, de texto o binario. También se mantiene dentro de estándar, por basarse en el algoritmo de Base64.

Se podría usar Base64 para tener un nivel de seguridad mínimo, o tal vez casi intermedio, si se usa una tabla no estándar de caracteres.

Normalmente los caracteres que usa Base64 son estos:

var Base64_chars="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";


Pero si usamos otro orden y lo mantenemos secreto en todo momento, podría funcionar casi como una contraseña de 64 bytes (512 bits). Por ejemplo:

var Base64_nostd="9ryoAuwh4RvMDPYKNltWUTaEqnBmbcSZFxfGg7iJj6QsVkOXzdH0C1p2e35L8I+/=";



Con las siguientes funciones que he escrito se puede "cifrar" y "descifrar" en Base64, usando una tabla no estándar:


/*****************************************************************************
Base64: My_btoa y My_atob

2012-07-02


Implementación de Base64 en javascript.

http://devel.cable-modem.org/

Este código es de dominio público (sin derechos de autor).
Puedes hacer lo que desees con él.


*****************************************************************************/



function My_atob(base64_data, table)
{
var binary64="";
var binary="";

if(typeof(base64_data)!="string")return "";
if(!(base64_data.length>0))return "";


var tmp="";
var c="";
for(var x=0;x<base64_data.length;x++)
{
  c=base64_data.charAt(x);

  //These padding characters should be ignored:
  ///
    if(c=="=" || c==" " || c=="\t" || c=="\r" || c=="\n")continue;

  //Otherwise, look for the index in which the specified character is.
  //That will be the value of the Base64 current character.
  else
   c=table.indexOf(c);

  binary64+=StrPad(str=c.toString(2),padchar="0",paddedstrlen=6,direction="l");
 
  if(binary64.length>=8)
  {
   binary+=String.fromCharCode(parseInt(binary64.substring(0,8),2));

   binary64=binary64.substring(8,binary64.length);
  }
}

return binary;
}






//Converts a raw "binary" data string into Base64 data:
///
function My_btoa(binary_data, table)
{
var Base64_TBL=table.split("");

//This will contain our resulting converted data
///
  var base64="";

//Check that the data is a string and that its length is not 0:
///
  if(typeof(binary_data)!="string")return "";
  if(!(binary_data.length>0))return "";


//Temporary 32-bit DWORD:
///
  var tmp="";


//4-byte temporary Base64 chunk for each 3 bytes of data, and/or plus padding if necessary:
///
  var tm2="";

//Number of '=' padding characters, to avoid generating Base64 charactes that should be padding to begin with:
//Number of '=' characters if there is no further data in size divisible exactly by 3:
///
  var padcount="";


//This loop advances in groups of 3 because 3 "binary" letters
//produce 4 Base64 letters:
///
  for(var x=0;x<binary_data.length;x+=3)
  {
   //INIT: Read a DWORD safely byte by byte, with "memory boundary checking"
   //INIT: Read a DWORD safely byte by byte, with "memory boundary checking"
   //INIT: Read a DWORD safely byte by byte, with "memory boundary checking"
   //INIT: Read a DWORD safely byte by byte, with "memory boundary checking"
   ///
    tmp=binary_data.charCodeAt(x)<<24;         //bits 31-24

    if(x+1<binary_data.length)
    tmp|=binary_data.charCodeAt(x+1)<<16;   //bits 23-16
    else padcount++;                    //If there's no data, increase padding and this bit group is 0 automatically

    if(x+2<binary_data.length)
    tmp|=binary_data.charCodeAt(x+2)<<8;    //bits 15-8
    else padcount++;                    //If there's no data, increase padding and this bit group is 0 automatically

                                               //bits 7-0 are 0 always

                                             
   //END:  Read a DWORD safely byte by byte, with "memory boundary checking"
   //END:  Read a DWORD safely byte by byte, with "memory boundary checking"
   //END:  Read a DWORD safely byte by byte, with "memory boundary checking"
   //END:  Read a DWORD safely byte by byte, with "memory boundary checking"


    //Shift 8 bits left to re-align all bit values in an order
    //proper for the 6 bits of Base64.
    //
    //This will NOT discard the first 2 bits of the DWORD, but anyway
    //the bits of the next byte of data, if present (byte 4),
    //belong to the next group of 3 bytes and are useless for the
    //current 32-bit run:
    ///
     tmp>>=8;   //tmp is a 32-bit DWORD here


    //"Flush" past, now useless data, before using the buffer again (might not be necessary
    //in C or assembly since the data in those languages will always be overwritten either by
    //data or padding; but it is required in javascript because the string cannot be handled
    //so freely in such language).
    ///
     tm2="";


     var sshl=6;  //I thought that this bit shifting was going to be dynamic, but after the adjusting above
                  //and the bit masking below inside the loop, it isn't necessary at all.


     for(var y=0;y<4;y++)
     {
      //Get bits 31-24, then 23-16 and 15-8 and use them as index for the third, second
      //and first Base64 characters, respectively:
      ///

      //Save the corresponding Base64 character, backwards, or if we are in a range in which
      //we previously detected that there was no data available for bytes 2 of 3 and/or 3 of 3,
      //just add padding.
      //
      //In other words, if the count of required padding characters is 0 (3 original bytes were
      //present for this loop run), or if the count of padding characters is not 0 and y is
      //in a range above/outside that of the padding characters to generate, then save a Base64
      //indexed character.
      //
      //Otherwise, we are in a byte range for padding, and we must generate and save a padding character
      //(it could and should only happen at the very end of the whole data buffer):
      ///
       if(padcount==0 || (padcount!=0 && y>padcount-1))
       {
        tm2=Base64_TBL[tmp&63]+tm2;
       }
        else tm2=tm2+"=";


      //Keep shifting bits. We have saved backwards because in this way we
      //reduce the amount of bit shifting and bit masking required to get
      //the 6 bits required for each Base64 character, and still we can get
      //each Base64 character as soon as possible, as soon as its offset
      //is available to us.
      ///
       tmp>>=6;
     }

   //Save this chunk of Base64 characters:
   ///
    base64+=tm2;
  }

return base64;
}






function StrPad(str,padchar,paddedstrlen,direction)
{
//If no direction was specified, the default action is
//to pad leftmost:
///
  if(!direction)direction="l";


//Don't allow empty padding character variable or a bad final padded length
//because it would cause an infinite loop:
///
  if(typeof(paddedstrlen)!="number" || typeof(padchar)!="string")return str;
  if(!(padchar.length>0) || paddedstrlen<=0)return str;


if(direction.toLowerCase()=="r")
{
   while(str.length<paddedstrlen)
   {
    str=str+padchar.charAt(0);
   }
}
  else
  {
    while(str.length<paddedstrlen)
    {
     str=padchar.charAt(0)+str;
    }
  }


return str;
}



Para cifrar:

var encrypt=My_btoa("Hola123456", Base64_nostd)

El resultado es: twIVqWAHD0N1PF==



My_atob(encrypt, Base64_nostd)


El resultado es de vuelta: Hola123456


Recordemos que se usó la tabla no estándar de caracteres. Si alguien trata de descifrar esto sin ver o recordar la tabla que se usó, se dará cuenta que es extremadamente difícil de descifrar.

La seguridad, siempre y cuando se mantenga secreta la tabla no estándar de Base64, se puede aumentar considerablemente si se cifra la cadena cifrada sobre sí misma un mínimo de 4 veces, y un número aleatorio de veces mayor a 4.
#38
ASM / Re: Modo protegido, dolor de cabeza
4 Marzo 2013, 02:51 AM
Por experiencia puedo decir que este es un punto de entrada muy inadecuado para tratar de implementar un sistema operativo.

Por este camino, uno podría tardarse años literalmente (más de 5) para apenas comenzar a crear un sistema operativo.

Lo más que se podría lograr es crear uno simple.

Pero el peligro es desilusionarse después de varias iteraciones de reescribir todo desde cero, y todos estos consejos que doy son para evitar eso. Por supuesto yo no me he desanimado porque realmente quiero aprender las cosas más avanzadas.

También estoy en el foro de OSDev.org y OSDever.net, y he llegado a aprender que por más ayuda que uno reciba ahí, necesitará otras fuentes con información más abundante y a un nivel más manejable (además de que lastimosamente la Wiki parece haber sido escrita originalmente por gente con una actitud de que todo es difícil en extremo, pero sin intentar dar realmente suficiente código o pistas para solucionarlo en la práctica).

De esa forma es como yo creé un sistema operativo extremadamente simple de 32 bits, tal vez el sistema de 32 bits didáctico pero útil más entendible que se podría encontrar. Le di el nombre de LowEST Kernel.

Discusiones del Proyecto de LowEST Kernel, Nivel 1 (32 bits)

Código Fuente del Kernel de LowEST, Nivel 1 (32 bits)





Realmente he tenido mucha diversión experimentando controlar y detectar discos IDE y ATAPI, y SATA, controlar floppies, el teclado, el speaker, la VGA estándar, etc.

Yo aprendí Ensamblador en 6 meses con el libro de Charte Ojeda, y después de eso aprendí a usar las interrupciones de modo protegido en 1 semana. Después otra semana para comenzar a manejar el teclado, y muchos meses más para manejar el CD-ROM (pero sin leerlo), y en muchos años más para aprender sobre muchas otras cosas, aunque son tantas que en su mayoría solo a nivel superficial.

Lo que mi kernel puede hacer formalmente (y que puedo ayudar a explicar con mayor simplicidad que otras fuentes) es:


  • Bootear desde un floppy con FAT16.
  • Driver del teclado que reconoce Ctr+Alt+Del y Shift.
  • Consola de texto tipo MS-DOS, con soporte para agregarle comandos internos.
  • El kernel puede correr programas del floppy leyendo el directorio raíz del FAT12 y usar funciones importadas del kernel, lo que lo hace ideal para extender pruebas adicionales de bajo nivel en tiempo de ejecución.
Estoy seguro de que puedo ayudar, siempre y cuando se trate de preguntas y un estudio suficientemente extendido, en lugar del típico enfoque de los principiantes de "¿Cómo hago X cosa?" pero sin realmente darse a entender de forma más completa, en el resto de aspectos, casi como en un tutorial formal.

De lo contrario, a menos que uno tenga conocimiento y acceso excepcional a información enriquecida, cualquier otro enfoque puede tardarse años, si es que el aspirante no se rinde antes de tantos años.
#39
Hace un tiempo, cerca de hace 6 meses, en el 2012, aproximadamente de Junio a Agosto, estuve trabajando en un emulador de VGA y CPU x86 como manera tanto de entender HTML5 como de llevar mi conocimiento a un lugar más accesible, tal como es el navegador.

Versión Pre-Alpha "Final"

Historial de Versiones Pre-Alpha:
Versión 0.1
Versión 0.2
Versión 0.3
Versión 0.4
Versión 0.5





El código de emulador de CPU x86 que estoy intentando rediseñar para una emulación realmente adecuada, escalable y con código mantenible (por ahora en javascript y HTML5) es este (que recupera un código de boot x86 de floppy, pero obviamente todavía no hay BIOS ni soporte para las instrucciones del CPU globalmente):

Emulador de CPU con visor VGA provisional (2013-03-02).





Durante el repaso del código antiguo me he dado cuenta de varios requerimientos que debería satisfacer.

El requerimiento más importante a superar en este momento es cómo manejar grupos de instrucciones, ya que muchísimas de estas difieren solo en sus opcodes y en la operación que efectúan, pero se rigen por la misma tabla de decisiones. Por ahora, uso como ejemplo la versión de opcode 0x31 de la instrucción xor.

Hay muchas más que igual que esta, usan la misma tabla de operadores en 16 bits (y más adelante, otra para 32 bits, y otra mucho más adelante, para 64 bits).



El código básico para manejar las ramificaciones de esta tabla de la forma más básica es la siguiente (en javascript):

function mod(modrm){
var mod=parseInt("11000000b", 2);
var mod_val=(modrm&mod)>>6;
return mod_val;
}



function reg(modrm){
var reg=parseInt("00111000", 2);
var reg_val=(modrm&mod)>>3;
return reg_val;
}


function rm(modrm){
var rm=parseInt("00000111", 2);
var rm_val=(modrm&rm);
return rm_val;
}



function scale(sib){
var scale = parseInt("11000000b", 2);
var scale_val=(sib&scale)>>6;
return scale_val;
}


function index(sib){
var index = parseInt("00111000b", 2);
var index_val=(sib&index)>>3;
return index_val;
}


function base(sib){
var base  = parseInt("00000111b", 2);
var base_val=sib&base;
return base_val;
}



function xor_16()
{
    var ctr=0x00;

    var str="";
    for(var modrm=0; modrm<256; modrm++)
    {
     str+="db 0x31,"+modrm;

     if(mod(modrm)==0)
     {
      if(rm(modrm)==6)
       str+=",0,0\n";
      else
      {
       str+="\n";
       continue;
      }
     }
      else if(mod(modrm)==1)
      {
       str+=",0\n";
       continue;
      }
       else if(mod(modrm)==2)
       {
        str+=",0,0\n";
        continue;
       }
        else if(mod(modrm)==3)
        {
         str+="\n";
         continue;
        }
    }

return str;
}



Como vemos, lo más importante aquí es el campo Mod del byte Mod/RM, que puede tener un valor de 0 a 3, y dependiendo de este, se usan diferentes desplazamientos de memoria o registros.

Una vez que tenemos este código, lo difícil es decidir cómo reutilizar este código eficientemente para absolutamente todas las demás instrucciones que esperan dicho byte Mod/RM, usando punteros a funciones, y probablemente funciones simples crudas para todas las operaciones básicas (AND, OR, XOR, NOT, NEG, suma, resta, muliplicación, división, desplazamientos de bits, rotaciones de bits, etc.).





Otro aspecto importante es crear un cuerpo de rutinas para recuperar adecuadamente las instrucciones. Para esto en primer lugar necesitamos una tabla con los tamaños esperados de todas las instrucciones existentes. Esto también puede ayudarnos para aislar instrucciones que actualmente no podemos manejar y para que podamos saltarlas y/o recuperarlas por separado.

Esto se complica porque una instrucción puede estar precedida por un número variable de bytes de prefijo, y estos también deben considerarse parte integral de la instrucción. Muchas veces estos cambian el número de bytes esperados a continuación (por ejemplo un número de bytes adecuado para operaciones de 32 bits en código de 16 bits).

En pocas palabras, necesitamos escribir un buen "fetcher" de instrucciones, que sea manejable a pesar de la gran cantidad de instrucciones existentes y combinaciones de dichas instrucciones, y operadores.





Voy a seguir escribiendo lo que haga a medida avance. Podría avanzar rápidamente con el código actual, pero sé que es suficientemente fragil como para volverse un problema de escalabilidad o de una correcta emulación, así que voy a irme por el camino más largo e implementar todo pensando en iniciar con las funciones más reutilizables, y una vez que estén bien desarrolladas, construir las capas superiores de la emulación.
#40
Cita de: x64Core en 25 Febrero 2013, 19:13 PM
Hola,

Me ha llamado la atencion algo que mencionas, y es el hecho que el ensamblador podría ser "portable", podrias explicar mejor lo que piensas?
como el lenguaje ensamblador podría ser portable para ti? solo basándonos en estas arquitecturas: x86, x86-64.
Más bien impossible que el lenguaje ensamblador sera portable por más macros que tenga, es como decir que estas instrucciones se puedan ejecutar nativamente
dentro de un x86:

MOV RAX,0x800000000
MOV [R8],RAX

Yo no desvaloro lo que tienes en mente pero no crees que seria mejor crear un lenguaje ( basado en C ) y mejorarlo, Hablo acerca de mejorarlo debido a que
sabemos que al veces nos es necesario usar ensamblador en linea o linkear modulos .asm con projectos C/C++ a diferencia de tratar de hacer el ensamblador portable?
Lo mismo que hace portables a C o C++ (hacer ciertas conversiones entre distintas arquitecturas, o dejar las instrucciones como están) es lo que haría al Ensamblador portable en los mismos términos, pero por una u otra razón nadie se ha interesado hasta ahora en resolver ese problema.

Pero por ejemplo para ingeniería inversa, en los casos en los que no se puede representar código con C, un lenguaje como este sería la mejor opción, además de hacer que sea más fácil leer desensamblajes enormes.


La realidad es que hay tareas de programación que solo se ven elegantes con código de tan bajo nivel como este; y hay otras tareas que solo se ven elegantes con lenguajes a nivel de C o C++. Por eso sería mejor un compilador capaz de usar diferentes lenguajes de forma nativa, para que cada tarea sea lo más elegante y legible posible en su dimensión.

Por ejemplo, sea dentro o fuera de Windows, si lo que queremos es programar registros gráficos VGA estándar o no estándar, lo mejor es usar un lenguaje claro y portable, de más bajo que C. Eso hará el código extremadamente elegante.

Librería VGA Estándar (para 16, 32 y 64 bits, con RealC)

Por ejemplo este es código de RealC para cambiar modos en VGA:
//This function makes the VGA card enter in
//standard 320x200 256-color MCGA mode,
//without using the BIOS.
//
//It follows the following sequence:
//
// 1. Turns off monitor output to completely avoid flicker,
//    frequency/synchrony issues while setting the video mode,
//    and apparently making the video mode switching much faster
//    from text to graphics.
//
// 2. Configures the Miscellaneous Output Register.
//
// 3. Configures the Feature Control Register.
//
// 4. Write the 5 Sequencer registers.
//
// 5. Unprotect CRTC registers 0-7.
//
// 6. Writes the 25 CRTC registers.
//
// 7. Protects CRTC registers 0-7.
//
// 8. Writes the 9 Graphics Controller Registers.
//
// 9. Writes the Attribute Controller Registers.
//    Here's an optimization: From its 21 registers, only
//    registers 16 to 20 are written. Registers 0 to 15
//    are left unchanged because those palette registers are
//    not useful for MCGA 320x200x256, so we skip writing those.
//
//10. Enable reads to the palette by setting bit 5 of the
//    Attribute Controller Index Register at 0x3C0, to 1.
//
//11. Turn back on monitor output to make use of the graphics mode.
//
//This sequence takes the best from all of the other programming sequences floating around
//for manually entering mode 13h, and thus has original-research optimizations
//from me (skipping configuration the 16-color palette of the Attribute Controller),
//and avoiding inferior ways of doing things (wait for vertical retrace, instead of
//doing it much better by disabling hardware video output, while reconfiguring
//the registers), etc. And it is much, much more documented and easy to read/understand,
//with nice and elegant public domain low level standard VGA "API" functions.
//
//This program does NOT reconfigure BIOS video mode metrics in the BDA, nor clears
//the screen after setting the video mode, just because those tasks are entirely
//apart from the low level hardware programming shown here. That would just add to the
//bulk of this demo program, and it will be programmed by me, but in a different, yet
//equally reusable/combinable "API" package.
///
function set_mode_MCGA_320x200x256()
{
push $WIDEAX;
push $WIDEBX;
push $WIDECX;
push $WIDESI;

//Write to Color register address in all cases
//that apply to our interfacing functions:
///
 $BX=0000000001000000b;


//Turn monitor output off to be nicer with older
//and delicate monitors while fiddling with the configuration:
///
 stdVGA_Sequencer_Monitor_Off();


//We want to set this register to clear its bit 0,
//to enforce activating I/O register addresses for
//color video modes, and the rest of its configurations:
///
 $AL=[CGA_320x200x256Gen__MiscOutputReg];
 stdVGA_set_Miscellaneous_Output_Register();


//Write the Feature Control Register:
///
 $AL=[CGA_320x200x256Gen__FeatureControlReg];
 //$BX=0000000001000000b;  //Write to Color register address
 stdVGA_set_Feature_Control_Register();


//Write the Sequencer registers:
///
 $CH=0;  //Index of first register to write
 $CL=5;  //Number of registers to write
 $WIDESI=CGA_320x200x256SequencerRegs;
 stdVGA_set_Sequencer_regs();


///INIT: Write the CRTC registers
///INIT: Write the CRTC registers
///INIT: Write the CRTC registers
///INIT: Write the CRTC registers
///INIT: Write the CRTC registers

 //First unprotect registers 0-7:
 ///
  $CL=00000000b;          //Do Unprotect
  //$BX=0000000001000000b;  //Use Color I/O port address
  stdVGA_protect_unprotect_CRTC_regs();



 //Write the CRTC registers:
 ///
  $CH=0;   //Index of first register to write
  $CL=25;  //Number of registers to write
  //$BX=0000000001000000b;  //Use Color I/O port address
  $WIDESI=CGA_320x200x256CRTCRegs;
  stdVGA_set_CRTC_regs();



 //Finally, protect registers 0-7 again:
 ///
  $CL=10000000b;          //Do Protect
  //$BX=0000000001000000b;  //Use Color I/O port address
  stdVGA_protect_unprotect_CRTC_regs();


///END:  Write the CRTC registers
///END:  Write the CRTC registers
///END:  Write the CRTC registers
///END:  Write the CRTC registers
///END:  Write the CRTC registers



//Write the Graphics Controller registers:
///
 $CH=0;  //Index of first register to write
 $CL=9;  //Number of registers to write
 $WIDESI=CGA_320x200x256GraphicsRegs;
 stdVGA_set_Graphics_regs();



//Write the Attribute Controller registers. We will
//try skipping the 16-color palette registers here,
//since MCGA 320x200@256 colors uses the DAC instead
//of those registers, and these registers don't care
//in this mode:
///
 //Address bits of attribute flip/flop to reset it by reading it
 ///
  //$BX=0000000001000000b;
  $CH=16;     //Index of first register to write
  $CL=21-16;  //Number of registers to write
  $WIDESI=CGA_320x200x256AttributeRegs+16;
  stdVGA_set_Attribute_regs();



 //Enable the capability of reading the palette.
 //Otherwise, the screen will stay absolutely black!:
 ///
  stdVGA_set_Attribute_regs__EnablePalette();


//Turn monitor output on (this assumes that the register
//set values we are using keep the Sequencer's Screen Off bit
//off at all times, to not defeat the very purpose of this
//"protection"):
///
 stdVGA_Sequencer_Monitor_On();


pop $WIDESI;
pop $WIDECX;
pop $WIDEBX;
pop $WIDEAX;
}


Y este es en Ensamblador (bastante menos legible y modificable, especialmente si leemos el resto del código en el ZIP):
set_mode_MCGA_320x200x256:
push eax
push ebx
push ecx
push esi



mov bx,0000000001000000b;



call stdVGA_Sequencer_Monitor_Off;


mov al,[CGA_320x200x256Gen__MiscOutputReg];
call stdVGA_set_Miscellaneous_Output_Register;


mov al,[CGA_320x200x256Gen__FeatureControlReg];
call stdVGA_set_Feature_Control_Register;



mov ch,0;
mov cl,5;
mov esi,CGA_320x200x256SequencerRegs
call stdVGA_set_Sequencer_regs;



mov cl,00000000b;
call stdVGA_protect_unprotect_CRTC_regs;


mov ch,0;
mov cl,25;
mov esi,CGA_320x200x256CRTCRegs
call stdVGA_set_CRTC_regs;



mov cl,10000000b;
call stdVGA_protect_unprotect_CRTC_regs;



mov ch,0;
mov cl,9;
mov esi,CGA_320x200x256GraphicsRegs
call stdVGA_set_Graphics_regs;



mov ch,16;
mov cl,5;
mov esi,CGA_320x200x256AttributeRegs+16;
call stdVGA_set_Attribute_regs;


call stdVGA_set_Attribute_regs__EnablePalette;



call stdVGA_Sequencer_Monitor_On;



pop esi
pop ecx
pop ebx
pop eax
ret







Si hay registros que no están disponibles se pueden implementar internamente usando otros registros o variables (y optimizaciones), tal como estructuras altamente complejas se implementan siempre en software, en memoria. El resultado es código de Ensamblador fácil de reusar, tanto como C.

Al final, la herramienta nos ha permitido compilar ese código exacto en una variedad de arquitecturas, aunque en algunas se refleje exactamente, en otras parcialmente, y en otras sea internamente diferente. Pero lo importante es que un esfuerzo de depuración único nos permite usar el mismo código fuente de forma portable, desde el punto de vista de usabilidad inmediata del mismo código (puede ser beneficioso para abstraer adicionalmente código específico de plataformas, para que sea mucho más reusable).

Emular elementos pequeños como registros es tan o más eficiente que usar estructuras, porque son datos más simples y pequeños.

Sería un tipo de paravirtualización simple (para tareas de nivel de sistema se necesita crear convenciones más cuidadosamente, pero es posible cubrir compromisos razonables que normalicen diferentes arquitecturas), y no es cuestión de macros sino de crear un compilador propiamente dicho que se encargue de traducir todo lo que sea necesario para el verdadero código Ensamblador final. Es difícil (tanto como crear un GCC), pero técnicamente es posible.

En el link anterior explicaba una de muchas convenciones que se pueden usar para hacer que el código sea más portable.

Por ejemplo, si la convención de nuestro programa es usar el registro más grande posible, se puede usar $WIDEAX, lo que sería $EAX en 32 bits y RAX en 64 bits, y mayores en el futuro. Si deseamos usar $RAX en todos los modos menores a 32 bits, debería implementarse como variable además de optimizaciones (que pueden ser opcionales).


Aquí tengo un proyecto que creé hace un tiempo que tiene la inteción de enviarle comandos a los periféricos desde una consola nativa de DOS (y emular registros del CPU para poder enviarle interrupciones paso a paso):

Proyecto LowEST (Inglés)

El código fuente está escrito en RealC, y también incluye el código de Ensamblador equivalente. Una función en RealC es menos confusa que su misma versión en Ensamblador, pero realmente es Ensamblador más fácil de leer con potencial de ser totalmente portable:

RealCfunction sys_get_args()
{
asm pushad

 $ESI = 0x81;       ;//Start of command-line string
 $EDI = sys_argv;
 $CL  = byte[0x80]; ;//Size of command-line string


 while($CL != 0)
 {
   if( byte[$ESI]!=0x20 && byte[$EDI]!=0x0D )
   {
    dword[sys_argc]++;

    dword[$EDI]=$ESI;
    $EDI+=4;

    sys_args_last_arg_char();
    dword[$EDI]  = $EAX;
    dword[$EDI] -= $ESI;
    $CL-=[$EDI];


    $ESI=$EAX;
    $EDI+=4;
    continue;
   }


  ;//These 2 instructions are only in case
  ;//the characters are 0x20 or 0x0D, and the
  ;//"continue" just above avoids it from being
  ;//run if we processed actual characters in this
  ;//iteration:
  ;;
   $ESI++;
   $CL--;
 }

asm popad
}



align 16                               ;//DEBUG STRING
db "int argc void *argv             "  ;//DEBUG STRING
sys_argc dd 0
sys_argv times 128 dd 0

align 16                               ;//DEBUG STRING
db "       int argc void *argv {END}"  ;//DEBUG STRING




function /*$EAX */sys_args_last_arg_char()
{
asm push ecx


 $EAX=$ESI;

 while($CL != 0)
 {
   if( byte[$EAX]==0x20 || byte[$EAX]==0x0D )
   break;

   $EAX++;
   $CL--;
 }

asm pop ecx
}


Ensambladorsys_get_args:
 pushad
   mov esi,0x81
   mov edi,sys_argv
   mov cl,[0x80]




  .while_ln28:
  cmp cl,0
  jz .while_ln28_end
      cmp byte[esi],0x20
      je .not_if_ln28
      cmp byte[esi],0x0D
      je .not_if_ln28

      inc dword[sys_argc]

      mov dword[edi],esi
      add edi,4


      call sys_args_last_arg_char

      mov dword[edi],eax
      sub dword[edi],esi
      sub cl,[edi]

       mov esi,eax
       add edi,4

      jmp .while_ln28

    .not_if_ln28:

  ;//These 2 instructions are only in case
  ;//the characters are 0x20 or 0x0D, and the
  ;//"continue" just above avoids it from being
  ;//run if we processed actual characters in this
  ;//iteration:
  ;;
   inc esi
   dec cl

  jmp .while_ln28
  .while_ln28_end:

 popad
ret



align 16
db "int argc void *argv             "
sys_argc dd 0
sys_argv times 128 dd 0

align 16
db "       int argc void *argv {END}"



sys_args_last_arg_char:
 push ecx
   mov eax,esi

  .while_ln74:
  cmp cl,0
  jz .while_ln74_end
     cmp byte[eax],0x20
     je .while_ln74_end
     cmp byte[eax],0x0D
     je .while_ln74_end


     inc eax
     dec cl

  jmp .while_ln74
  .while_ln74_end:

 pop ecx
ret



Si se tiene experiencia en crear programas de Windows en Ensamblador desde el formato del Portable Ejecutable, entonces sería mucho más fácil con un lenguaje como RealC, sin necesidad de linkers o compiladores, solo un ensamblador.

Ensamblador
;NOTA: Ensamblar con el comando:
;
;        NASM pehead.asm -o pehead.exe


EXE_start:

bits 32

alig equ 16


%define round(n, r) (((n+(r-1))/r)*r)



DOS_stub_start:
 db 'MZ'


 times 0x3A db 0


 dd PEhead


DOS_stub_end:
DOS_stub_SZ equ (DOS_stub_end-DOS_stub_start)


PE_head_start:
 PEhead:

 PEhead.Signature            db 'P','E',0,0

 PEhead.Machine              dw 0x14C

 PEhead.NumberOfSections     dw 1

 PEhead.TimeDateStamp        dd 0

 PEhead.PointerToSymbolTable dd 0

 PEhead.NumberOfSymbols      dd 0

 PEhead.SizeOfOptionalHeader dw 4

 PEhead.Characteristics      dw 0x0103

PE_head_end:
PE_head_SZ equ (PE_head_end-PE_head_start)



Opt_Head_start:
 OptHead:

 OptHead.Magic                   dw 0x10B
 OptHead.MajorLinkerVersion      db 8
 OptHead.MinorLinkerVersion      db 0
 OptHead.SizeOfCode              dd round(codesize,alig) ;1024 ;Code_SZ
 OptHead.SizeOfInitializedData   dd 0
 OptHead.SizeOfUninitializedData dd codesize ;1024
 OptHead.AddressOfEntryPoint     dd PrgEntry
 OptHead.BaseOfCode              dd codesize ;1024
 OptHead.BaseOfData              dd PrgEntry
 OptHead.ImageBase               dd 0x400000
 OptHead.SectionAlignment        dd alig
 OptHead.FileAlignment           dd alig
 OptHead.MajorOSVersion          dw 4
 OptHead.MinorOSVersion          dw 0
 OptHead.MajorImageVersion       dw 0
 OptHead.MinorImageVersion       dw 0
 OptHead.MajorSubsystemVersion   dw 4
 OptHead.MinorSubsystemVersion   dw 0
 OptHead.Win32VersionValue       dd 0
 OptHead.SizeOfImage             dd round(hdrsize,alig)+round(codesize,alig) ;1024 ;EXE_SZ
 OptHead.SizeOfHeaders           dd round(hdrsize,alig) ;Headers_SZ
 OptHead.Checksum                dd 0
 OptHead.Subsystem               dw 2
 OptHead.DllCharacteristics      dw 0
 OptHead.SizeOfStackReserve      dd 0
 OptHead.SizeOfStackCommit       dd 0
 OptHead.SizeOfHeapReserve       dd 0
 OptHead.SizeOfHeapCommit        dd 0
 OptHead.LoaderFlags             dd 0
 OptHead.NumberOfRvaAndSizes     dd 3
   OptHead.ExportTable.VirtualAddress dd 0
   OptHead.ExportTable.Size           dd 0

   OptHead.ImportTable.VirtualAddress dd ImportDirectoryTable
   OptHead.ImportTable.Size           dd ImportDirectoryTable_SZ

   OptHead.ResourceTable.VirtualAddress dd ResourceTable
   OptHead.ResourceTable.Size           dd 4096 ;ResourceTable_SZ

   dd 0
   dd 0

   dd 0
   dd 0

   dd 0
   dd 0

   dd 0
   dd 0

   dd 0
   dd 0

   dd 0
   dd 0

   dd 0
   dd 0

   dd 0
   dd 0

   dd 0
   dd 0

   dd 0
   dd 0

   dd 0
   dd 0

   dd 0
   dd 0

   dd 0
   dd 0

   dd 0
   dd 0
Opt_Head_end:
Opt_Head_SZ equ (Opt_Head_end-Opt_Head_start)



hdrsize equ $ - $$



Headers_SZ equ $-$$ ;(Opt_Head_SZ+PE_head_SZ+DOS_stub_SZ)






db ".code"
times 3 db 0

_virtualsize dd 2048
_virtualaddress dd PrgEntry
_sizeofrawdata dd 2048
_pointertorawdata dd PrgEntry
_pointertorelocations dd 0
_pointertolinenumbers dd 0
_numberofrelocations dw 0
_numberoflinenumbers dw 0
_characteristics dd 0x60000020



db ".rsrc"
times 3 db 0

virtualsize dd 2048
virtualaddress dd ResourceTable
sizeofrawdata dd 2048
pointertorawdata dd ResourceDirectoryEntry
pointertorelocations dd 0
pointertolinenumbers dd 0
numberofrelocations dw 0
numberoflinenumbers dw 0
characteristics dd 0x50000040





align 16
PrgEntry:
align 16
   push dword 0x20
   push dword str1+0x400000
   push dword str2+0x400000
   push dword 0
   call dword[ImportAddressTable.1_array0+0x400000]

   push dword 0x20
   push dword str1+0x400000
   push dword str2+0x400000
   push dword 0
   call dword[ImportAddressTable.1_array0+0x400000]

   push dword 0x20
   push dword str1+0x400000
   push dword str2+0x400000
   push dword 0
   call dword[ImportAddressTable.1_array0+0x400000]

   push dword 0x20
   push dword str1+0x400000
   push dword str2+0x400000
   push dword 0
   call dword[ImportAddressTable.1_array0+0x400000]
ret


align 4
str1 db "Stringz 1",0
align 4
str2 db "Srins 2",0
align 4
str3 db "Strins a3",0


;align 1048576



align 16
db "[EOF]",0










align 16
ResourceTable:




;align 8
;






ResourceDirectoryEntry:


ResourceDirectoryTable0:
 ResourceDirectoryTable0.Characteristics     dd 0
 ResourceDirectoryTable0.Time_DateStamp      dd 0x49DDFF5E
 ResourceDirectoryTable0.MajorVersion        dw 0
 ResourceDirectoryTable0.MinorVersion        dw 0
 ResourceDirectoryTable0.NumberOfNameEntries dw 0
 ResourceDirectoryTable0.NumberOfIDEntries   dw 1

   ResourceDirectoryEntry1:
     ResourceDirectoryEntry1.IntegerID           dd 3
     ResourceDirectoryEntry1.EntryRVA            dd 0x80000000+ResourceDirectoryTable2 ;(ResourceDirectoryTable2-ResourceTable)

       ResourceDirectoryTable2:
         ResourceDirectoryTable2.Characteristics     dd 0
         ResourceDirectoryTable2.Time_DateStamp      dd 0x49DDFF5E
         ResourceDirectoryTable2.MajorVersion        dw 0
         ResourceDirectoryTable2.MinorVersion        dw 0
         ResourceDirectoryTable2.NumberOfNameEntries dw 0
         ResourceDirectoryTable2.NumberOfIDEntries   dw 1


           ResourceDirectoryEntry3:
             ResourceDirectoryEntry3.IntegerID           dd 1
             ResourceDirectoryEntry3.EntryRVA            dd ResourceDirectoryTable4 ;(ResourceDirectoryTable4-ResourceTable)


               ResourceDirectoryTable4:
                 ResourceDirectoryTable4.Characteristics     dd 0
                 ResourceDirectoryTable4.Time_DateStamp      dd 0x49DDFF5E
                 ResourceDirectoryTable4.MajorVersion        dw 0
                 ResourceDirectoryTable4.MinorVersion        dw 0
                 ResourceDirectoryTable4.NumberOfNameEntries dw 0
                 ResourceDirectoryTable4.NumberOfIDEntries   dw 1

                   ResourceDirectoryEntry5:
                     ResourceDirectoryEntry5.IntegerID           dd 0
                     ResourceDirectoryEntry5.EntryRVA            dd ResourceDataEntry6 ;(ResourceDataEntry6-ResourceTable)


                       ResourceDataEntry6:
                         ResourceDataEntry6.DataRVA                  dd vsico
                         ResourceDataEntry6.Size                     dd 1024 ;vsico_SZ
                         ResourceDataEntry6.CodePage                 dd 0
                         ResourceDataEntry6.Reserved                 dd 0



;ResourceDirectoryEntry:
;  ResourceDirectoryEntry.NameRVA             dd namerva
;  ResourceDirectoryEntry.IntegerID           dd 3
;  ResourceDirectoryEntry.DataEntryRVA        dd ResourceDirectoryTable
;  ResourceDirectoryEntry.SubdirectoryRVA     dd 0
;
;
;
;ResourceDirectoryTable:
;  ResourceDirectoryTable.Characteristics     dd 0
;  ResourceDirectoryTable.Time_DateStamp      dd 0
;  ResourceDirectoryTable.MajorVersion        dw 8
;  ResourceDirectoryTable.MinorVersion        dw 0
;  ResourceDirectoryTable.NumberOfNameEntries dw 1
;  ResourceDirectoryTable.NumberOfIDEntries   dw 1
;
;
;
;ResourceDataEntry:
;  ResourceDataEntry.DataRVA                  dd vsico
;  ResourceDataEntry.Size                     dd vsico_SZ
;  ResourceDataEntry.CodePage                 dd 0
;  ResourceDataEntry.Reserved                 dd 0



align 16
vsico:
;incbin "bc.ico"

vsico_SZ equ $-vsico


ResourceTable_SZ equ $-ResourceTable

















ImportDirectoryTable:
ImportDirectoryTable_start:
  ImportTable.ImportLookupTableRVA_array0  dd ImportLookupTable_array0
  ImportTable.Time_DateStamp_array0        dd 0
  ImportTable.ForwarderChain_array0        dd 0
  ImportTable.NameRVA_array0               dd DLL_name_array0
  ImportTable.ImportAddressTableRVA_array0 dd ImportAddressTable_array0

  ImportTable.ImportLookupTableRVA_array1  dd 0
  ImportTable.TimeDateStamp_array1         dd 0
  ImportTable.ForwarderChain_array1        dd 0
  ImportTable.NameRVA_array1               dd 0
  ImportTable.ImportAddressTableRVA_array1 dd 0





ImportDirectoryTable_end:
ImportDirectoryTable_SZ equ (ImportDirectoryTable_end-ImportDirectoryTable_start)


ImportAddressTable_array0:
  ImportAddressTable.1_array0 dd Hint_NameTable_array0 ;0x80000000+477
  ImportAddressTable.2_array0 dd 0


ImportLookupTable_array0:
  ImportLookupTable.Ordinal_Name_OrdinalNum_array0 dd Hint_NameTable_array0 ;0x80000000+477
  ImportLookupTable.Hint_NameTableRVA_array0       dd 0




Hint_NameTable_array0:
  dw 0x1DC
  db "MessageBoxA",0



DLL_name_array0:
db "USER32.dll",0



codesize equ $ - PrgEntry


namerva db "ICON",0


EXE_end:
EXE_SZ equ round((EXE_end-EXE_start),alig)

Code_SZ equ round(($-PrgEntry),alig)


Y nada impide usar el código resultante enlazado con código objeto de C o C++, y habríamos programado en Ensamblador con un nivel de claridad y mantenibilidad comparable al de C.





Hay muchas otras convenciones en las que pensar, pero esto requiere un nivel dramáticamente alto en creación de compiladores optimizadores, convenciones de llamada estándar, y lenguajes como C y C++ (para integrarlos), además de muchos estándares para poder hacer algo realmente utilizable que interactúe bien con el resto del mundo real. Eso es lo que habría que aprender a implementar desde cero para que este tipo de ideas de un Ensamblador portable pudiera extenderse.

Y pensándolo bien, si esto llegara por ejemplo al navegador web, además de usar javascript y HTML5, como para correr ese Ensamblador o emular código x86 ya binario, el Ensamblador tomaría un auge increíble y entonces, gracias a la emulación en algo tan usado como el navegador web, en aplicaciones de usuario, tendría más que sentido preocuparse por mantener y revivir librerías buenas de Ensamblador en un lenguaje/sintaxis legibles y sobre todo tan portable como Java, pero más extensible por ser principalmente código x86 y/o ARM (y después incluyendo Ensambladores del tipo del que se usa en lenguajes/entornos como ACPI o de aceleradores gráficos, eventualmente).