No quieres meterte con un intel, no. No es un buen comienzo. A menos que tal vez ejecute uno de los simuladores 8086/88, pero incluso entonces yo no empezaría allí.
En cuanto a aprender ensamblador, pdp11 (no es broma), msp430, arm thumb, arm, y algunos otros son buenos puntos de partida, construir una buena base entonces cada conjunto de instrucciones posterior es más fácil. Usted puede comenzar con las simulaciones y que no es un mal camino, hay simuladores pdp11 (simh), msp430 hay un núcleo abierto, armv2/3 núcleo abierto, además de un sinnúmero de mips y risc-v's que haría más tarde. Usted quiere algo apoyado por herramientas gnu por lo que todo lo anterior (sí hay un pdp-11 backend mantenido en binutils y gcc). msp430 y un sinnúmero de microcontroladores basados en el brazo están disponibles y los que sería el mejor lugar de hardware para empezar, que o un pi-cero (pero no una pi de tamaño completo ni beagle hueso negro no todavía). sí hay avr, etc, pero puede ser más difícil de usar dependiendo de lo que uno se obtiene.
Siendo realistas, hay muchas tarjetas mcu en el $10 to $ 20 que sería un buen punto de partida desde el punto de vista de los costes, algunos pueden requerir otro o unos pocos $2 - $ 20 tablas. Voy a elegir un camino primero.
st es una de las empresas líderes con mcus basados en cortex-m (arm) que funcionan en modo thumb, que es uno de los conjuntos de instrucciones de arm más sencillos. El conjunto de instrucciones con mayor compatibilidad en todo el mundo basado en el brazo.
Correr en linux te hará la vida mucho más fácil pero no es necesario, se pueden encontrar herramientas para Windows u OSX.
Un programa mínimo para sumar dos números para un cortex-m sería:
.thumb
.word 0x20001000
.word reset
.thumb_func
reset:
mov r0,#1
mov r1,#2
add r2,r1,r0
b .
eso es todo, el 100% del software
arm-linux-gnueabi-as flash.s -o flash.o
arm-linux-gnueabi-ld -Ttext=0x08000000 flash.o -o flash.elf
arm-linux-gnueabi-objcopy -O binary flash.elf flash.bin
arm-linux-gnueabi-objdump -d flash.elf > flash.list
El código en esta respuesta no importa entre arm-none-eabi- y arm-linux-gnueabi- construido herramientas gnu.
cat flash.list
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000
8000004: 08000009
08000008 <reset>:
8000008: 2001 movs r0, #1
800000a: 2102 movs r1, #2
800000c: 180a adds r2, r1, r0
800000e: e7fe b.n 800000e <reset+0x6>
Estoy llevando a una placa / chip / familia en particular el núcleo en realidad busca la tabla de vectores en 0x00000000 pero esta marca se dirige a la flash de usuario en 0x08000000 luego lo refleja a 0x00000000 si el chip está configurado para arrancar desde la flash de usuario. La primera palabra va en el puntero de la pila y la segunda es la dirección del vector de reinicio orred con uno (una cosa pulgar legado, rodar con ella).
Y entonces puedes ver que un registro se carga con un 1, otro con un 2 y luego se suman y se almacenan en un tercero.
Estoy empezando con un NUCLEO-F446RE que actualmente está disponible en amazon, mouser, digikey, st, etc. Más barato de lugares que no sean amazon, pero entonces usted paga el envío. Hay placas nucleo de $10 que funcionan bien tambien. Algunas de las placas nucleo no enganchan el uart de la mcu de destino a través del extremo de depuración de la placa (otra mcu), por lo que querrás hacer tu investigación primero. Si usted consigue uno de estos tableros, entonces usted dont por ahora necesita comprar cualquier otro hardware esto es todo.
Una vez conectada, la placa se muestra como una unidad virtual y basta con copiar el archivo .bin para cargar el programa en la memoria flash.
cp flash.bin /media/user/NODE_F446RE/
Ahora el programa está cargado el procesador se reinicia y el programa se ejecuta.
Usando otra herramienta gratuita, openocd puedo conectarme al depurador de la mcu:
openocd -f /usr/share/openocd/scripts/interface/stlink-v2-1.cfg -f /usr/share/openocd/scripts/target/stm32f4x.cfg
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 2000 kHz
adapter_nsrst_delay: 100
none separate
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : clock speed 1800 kHz
Info : STLINK v2 JTAG v36 API v2 SWIM v26 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 3.266535
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
en otra ventana telnet en el depurador openocd, detener el núcleo y volcar los registros
telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> halt
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x0800000e msp: 0x20001000
> reg
===== arm v7m registers
(0) r0 (/32): 0x00000001
(1) r1 (/32): 0x00000002
(2) r2 (/32): 0x00000003
(3) r3 (/32): 0x00000000
(4) r4 (/32): 0x00000000
Puede ver que los tres registros tienen los valores que programamos.
Así como el puntero de pila cargado en la tabla de vectores
(13) sp (/32): 0x20001000
Si quiero hacer esto en C y hacerlo un poco más complicado, Ill seguir adelante y hacer un proyecto más completo. 100% del código:
flash.s
.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
.word 0x20001000
.word reset
.thumb_func
reset:
bl main
b .
main.c
unsigned int fun ( unsigned int a, unsigned int b );
int main ( void )
{
return(fun(1,2));
}
fun.c
unsigned int fun ( unsigned int a, unsigned int b )
{
return(a+b);
}
flash.ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
construya
arm-linux-gnueabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m0 -mthumb -c main.c -o main.o
arm-linux-gnueabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m0 -mthumb -c fun.c -o fun.o
arm-linux-gnueabi-ld -nostdlib -nostartfiles -T flash.ld flash.o main.o fun.o -o main.elf
arm-linux-gnueabi-objdump -D main.elf > main.list
arm-linux-gnueabi-objcopy -O binary main.elf main.bin
Tu programa de ejemplo es código muerto tal y como está escrito si optimizado no generaría una instrucción add en su lugar sería:
main:
mov r0,#3
bx lr
hay otras soluciones pero ocultando las constantes de la optimización
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000009 stmdaeq r0, {r0, r3}
08000008 <reset>:
8000008: f000 f802 bl 8000010 <main>
800000c: e7fe b.n 800000c <reset+0x4>
...
08000010 <main>:
8000010: b510 push {r4, lr}
8000012: 2102 movs r1, #2
8000014: 2001 movs r0, #1
8000016: f000 f801 bl 800001c <fun>
800001a: bd10 pop {r4, pc}
0800001c <fun>:
800001c: 1840 adds r0, r0, r1
800001e: 4770 bx lr
y la suma ocurre. r0 obtiene el 1, r1 el 2, entonces la suma convierte a r0 en el 3. Así que copia y ejecuta
> reg
===== arm v7m registers
(0) r0 (/32): 0x00000003
(1) r1 (/32): 0x00000002
(2) r2 (/32): 0x00000000
Último ejemplo
añadir esto a flash.s
.globl dummy
.thumb_func
dummy:
bx lr
y usando este main.c
void dummy ( unsigned int );
int main ( void )
{
unsigned int ra;
for(ra=0;;ra++) dummy(ra);
}
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000
8000004: 08000009
08000008 <reset>:
8000008: f000 f802 bl 8000010 <main>
800000c: e7fe b.n 800000c <reset+0x4>
0800000e <dummy>:
800000e: 4770 bx lr
08000010 <main>:
8000010: b510 push {r4, lr}
8000012: 2400 movs r4, #0
8000014: 0020 movs r0, r4
8000016: f7ff fffa bl 800000e <dummy>
800001a: 3401 adds r4, #1
800001c: e7fa b.n 8000014 <main+0x4>
800001e: 46c0 nop ; (mov r8, r8)
así que tanto r4 como r0 contendrán el valor de conteo, si paramos y arrancamos y paramos podemos ver esos conteos
> halt
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x0800000e msp: 0x20000ff8
> reg r0
r0 (/32): 0x0088B275
> resume
> halt
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x0800000e msp: 0x20000ff8
> reg r0
r0 (/32): 0x009B0F27
> reg r4
r4 (/32): 0x009B0F27
>
A continuación, puede pasar a parpadear leds, conseguir el uart inicializado y así sucesivamente.
Aquí es donde entiendes que la programación baremetal es 99% lectura e investigación, menos del 1% de tu tiempo se gasta en la aplicación real. Se lee mucho y se escribe una cierta cantidad de código desechable para averiguar lo que realmente quería decir el manual. También te das cuenta de que la mayor parte del trabajo tiene que ver con los periféricos y no con la arquitectura/conjunto de instrucciones. Pasas más tiempo configurando los registros de los periféricos que configurando el procesador, si es que lo haces.
Así que hay un montón de placas nucleo que soportan esta cosa "mbed" lo que implica que usted puede hacer la copia de la cosa archivo bin. También las hay basadas en nxp, ya que las primeras mbeds estaban basadas en nxp. (nxp es otra empresa que vende chips basados en cortex-m). Si inviertes en una placa nucleo como la mencionada anteriormente el extremo de depuración de la placa se puede utilizar para depurar otros mcus basados en cortex-m incluso de otros vendedores, o por unos pocos dólares puedes conseguir un depurador basado en swd o un breakout ftdi genérico que soporte mpsse (adafruit tiene uno para $15). If you are willing to gamble on ebay you can get a usb uart board for a couple of bucks and an swd debugger for five bucks. Or spend around $ 15 por cada uno a través de amazon, adafruit o sparkfun, cosas que querrás en tu caja de herramientas.
Otro camino que también se basa brazo es la frambuesa pi cero y un programa simple en que es:
mov r0,#1
mov r1,#2
add r2,r1,r0
b .
sin tabla vectorial.
Disassembly of section .text:
00000000 <.text>:
0: e3a00001 mov r0, #1
4: e3a01002 mov r1, #2
8: e0812000 add r2, r1, r0
c: eafffffe b c <.text+0xc>
a continuación, copia el archivo .bin en una tarjeta sd y llámalo kernel.img. junto con bootcode.bin y start.elf que obtienes de los proyectos github de la gente de raspberry pi. inserta la tarjeta sd en la placa y enciéndela.
puedes hacer depuracion jtag usando openocd pero necesitaras hardware y no podras depurar el programa anterior. El chip esta centrado en la gpu el jtag expuesto es la gpu, para conectar con el depurador jtag de arm necesitas reconfigurar algunos pines gpio para exponer el jtag lo que significa algun codigo extra de arm que configura esos pines gpio, entonces el ftdi breakout correcto u otra placa y cuatro cables jumper para las señales basicas del jtag y puedes usar openocd para conectar con el procesador y pararlo y tal. Dado que se ejecuta desde ram, una vez detenido, entonces usted podría cargar un programa
load_image main.elf
resume 0x00000000
luego se detiene la carga se reanuda, se repite hasta que se cuelga algún periférico o se descifra el programa, necesitando ocasionalmente reiniciar para poner el chip en un estado conocido.
Los otros pis son mucho mas complicados, no querras ir con baremetal por mucho tiempo. Sin embargo, lo bueno de las Pi es que hay un excelente foro de baremetal con mucha gente que conoce a fondo el núcleo arm y los chips, además de código de ejemplo de cómo hacer varias cosas, incluida la magia que necesitarás si quieres incursionar en los núcleos armv7 o armv8 multinúcleo en esa plataforma.
Hay una placa basada en risc-v por $ 9 en amazon, que tiene un esquema de carga basado en flash falso similar risc-v es un poco más difícil de empezar que el brazo / pulgar. así que yo no empezaría aquí, y no recuerdo lo que la depuración si hay algo disponible en este tablero, por lo que un poco a ciegas objetivo para el parpadeo del led a continuación, más tarde uart, entonces usted puede comenzar a utilizar los de depuración.
Ciertamente puedes comprar placas arduino y deshacerte del arduino sandbox y escribir programas y cargarlos, y hacer cosas similares a las anteriores. Hay un simulador del conjunto de instrucciones avr que puedes usar. La naturaleza harvard-ish del conjunto de instrucciones y algunas otras cosas que yo no empezaría allí, migth ir allí más tarde después de algún otro conjunto instruciton.
msp430 es un buen punto de partida sencillo claramente inspirado en el pdp11/lsi11 que es uno de los mejores primeros conjuntos de instrucciones. Las placas solían costar unos pocos dólares, pero ahora probablemente cuesten más como $10 to $ 20 y puede al menos programar el chip y los leds parpadeantes por sólo el costo de la placa con el software adecuado. Si quieres hacer uart, etc es posible que necesite una placa uart usb por unos pocos a 15 dólares.
opencores tiene algunos núcleos que puedes simular con herramientas como verilator o icarus, pero puedes terminar gastando mucho tiempo en conseguir que todo funcione especialmente si no tienes experiencia con estas herramientas o estos lenguajes de hardware. Pero coge el núcleo amber arm2/3 o uno de los otros, averigua cómo alimentar el núcleo, y podrás tener una experiencia completa de ver el código ejecutarse en un núcleo, ver las instrucciones obtenidas, decodificadas, etc, etc... Una vez que desarrollas código para un chip en una simulación es difícil cuando llega el silicio porque ahora no tienes visibilidad de lo que está pasando. (Es cierto que el silicio funciona órdenes de magnitud más rápido).
Otro enfoque es escribir su propio simulador de conjunto de instrucciones. msp430 probablemente puede golpear a cabo en una larga tarde. pulgar mínimo tal vez un día completo. a continuación, aprender a dominar la cadena de herramientas (sólo tiene que utilizar gnu binutils y gcc) para crear binarios como se ha indicado anteriormente, a continuación, alimentar su sim estos programas y hacer / verlos correr. Escribiendo un simulador y/o desensamblador aprenderás los matices del conjunto de instrucciones mejor que algunos profesionales que han estado usando ese conjunto de instrucciones durante años, ya que muy pocos realmente profundizan en los matices. Usar una cadena de herramientas depurada idealmente significa que el código es bueno y las cosas que no siempre están bien documentadas como el direccionamiento pc-relativo o la bifurcación se generan en el código máquina y puedes averiguar cómo decodificarlo.
Al fin y al cabo, no te limites a una sola plataforma; el planteamiento anterior contiene sugerencias de enfoques que, con suerte, maximizarán tu éxito inicial, limitarán tu frustración y tu deseo de abandonar y no volver a intentarlo. Con algunas marcas en la columna de la victoria que impulsa la motivación para seguir adelante y moler a través de los fracasos para llegar a más victorias. x86 es un poco de una bestia, que sin duda puede trabajar su camino hasta ella, pero ¿por qué? No vas a arrancar uno desde cero, ciertamente puedes empezar desde donde algo como windows o linux arranca desde los medios e ir desde allí. Una caja x86 tiene más procesadores no x86 que procesadores x86, hay más procesadores arm en esa caja que x86s, y/o un puñado de otros, 8051s, z80s, etc. x86 se está limitando rápidamente a servidores y por ahora ARM's para todo lo demás (teléfonos y tablets y pronto portátiles). Es interesante ver cómo es y cómo funciona un CISC en toda regla, y personalmente yo empezaría con un emulador de 8088/86 y tendría una experiencia manejable y segura con altas probabilidades de éxito. Y luego leer sitios como stackoverflow y intel docs para ver cómo el actual conjunto de instrucciones evolucionado con el tiempo, así como los detalles sangrientos de todos los sistemas de protección, etc.
Te recomiendo encarecidamente que empieces este viaje con microcontroladores y/o simuladores/emuladores. Es barato, la gente sigue desarrollando a este nivel, si uno no funciona para ti o lo brickeas oh bueno, compra otro o compra uno diferente por otros 10 dólares. Dependiendo del camino que quieras tomar puedes usar herramientas gratuitas como kicad, y comprar algunas partes de mcu de $1, un pcb de unos pocos dolares, soldar el chip tu mismo y abrir incluso mas chips con los que jugar que no tienen placas de evaluacion baratas.
EDITAR
releyendo la quesiton, "ver" la salida que no sea la visibilidad depurador de arriba, para que nucleo bordo
flash.s
.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
.word 0x20001000
.word reset
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
.thumb_func
.globl dummy
dummy:
bx lr
notmain.c
void dummy ( unsigned int );
#define RCCBASE 0x40023800
#define RCC_AHB1ENR (*((volatile unsigned int *) (RCCBASE+0x30) ))
#define GPIOABASE 0x40020000
#define GPIOA_MODER (*((volatile unsigned int *) (GPIOABASE+0x00) ))
#define GPIOA_BSRR (*((volatile unsigned int *) (GPIOABASE+0x18) ))
//PA5
int notmain ( void )
{
unsigned int ra;
unsigned int rx;
ra=RCC_AHB1ENR;
ra|=1<<0; //enable GPIOA
RCC_AHB1ENR=ra;
ra=GPIOA_MODER;
ra&=~(3<<(5<<1)); //PA5
ra|= (1<<(5<<1)); //PA5
GPIOA_MODER=ra;
for(rx=0;;rx++)
{
GPIOA_BSRR=((1<<5)<< 0);
for(ra=0;ra<800000;ra++) dummy(ra);
GPIOA_BSRR=((1<<5)<<16);
for(ra=0;ra<800000;ra++) dummy(ra);
}
return(0);
}
flash.ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
construya
arm-linux-gnueabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
arm-linux-gnueabi-gcc -Wall -O2 -ffreestanding -mcpu=cortex-m0 -mthumb -c notmain.c -o notmain.o
arm-linux-gnueabi-ld -nostdlib -nostartfiles -T flash.ld flash.o notmain.o -o notmain.elf
arm-linux-gnueabi-objdump -D notmain.elf > notmain.list
arm-linux-gnueabi-objcopy -O binary notmain.elf notmain.bin
cópialo y el led parpadeará.
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000
8000004: 08000009
08000008 <reset>:
8000008: f000 f804 bl 8000014 <notmain>
800000c: e7ff b.n 800000e <hang>
0800000e <hang>:
800000e: e7fe b.n 800000e <hang>
08000010 <dummy>:
8000010: 4770 bx lr
...
08000014 <notmain>:
8000014: 2101 movs r1, #1
8000016: b5f0 push {r4, r5, r6, r7, lr}
8000018: 46c6 mov lr, r8
800001a: 4a12 ldr r2, [pc, #72] ; (8000064 <notmain+0x50>)
800001c: b500 push {lr}
800001e: 6813 ldr r3, [r2, #0]
8000020: 2780 movs r7, #128 ; 0x80
8000022: 430b orrs r3, r1
8000024: 4910 ldr r1, [pc, #64] ; (8000068 <notmain+0x54>)
8000026: 6013 str r3, [r2, #0]
8000028: 680b ldr r3, [r1, #0]
800002a: 4a10 ldr r2, [pc, #64] ; (800006c <notmain+0x58>)
800002c: 4e10 ldr r6, [pc, #64] ; (8000070 <notmain+0x5c>)
800002e: 401a ands r2, r3
8000030: 2380 movs r3, #128 ; 0x80
8000032: 00db lsls r3, r3, #3
8000034: 4313 orrs r3, r2
8000036: 600b str r3, [r1, #0]
8000038: 2320 movs r3, #32
800003a: 4698 mov r8, r3
800003c: 4d0d ldr r5, [pc, #52] ; (8000074 <notmain+0x60>)
800003e: 03bf lsls r7, r7, #14
8000040: 4643 mov r3, r8
8000042: 2400 movs r4, #0
8000044: 6033 str r3, [r6, #0]
8000046: 0020 movs r0, r4
8000048: 3401 adds r4, #1
800004a: f7ff ffe1 bl 8000010 <dummy>
800004e: 42ac cmp r4, r5
8000050: d1f9 bne.n 8000046 <notmain+0x32>
8000052: 2400 movs r4, #0
8000054: 6037 str r7, [r6, #0]
8000056: 0020 movs r0, r4
8000058: 3401 adds r4, #1
800005a: f7ff ffd9 bl 8000010 <dummy>
800005e: 42ac cmp r4, r5
8000060: d1f9 bne.n 8000056 <notmain+0x42>
8000062: e7ed b.n 8000040 <notmain+0x2c>
8000064: 40023830
8000068: 40020000
800006c: fffff3ff
8000070: 40020018
8000074: 000c3500