I have been following a tutorial called "The little book about OS development". I can write individual characters to the framebuffer, but not the next character. Here are the files:
kmain.c
#include "io.h"
#define FB_BLACK 0
#define FB_BLUE 1
#define FB_GREEN 2
#define FB_CYAN 3
#define FB_RED 4
#define FB_MAGENTA 5
#define FB_BROWN 6
#define FB_LIGHTGREY 7
#define FB_DARKGREY 8
#define FB_LIGHTBLUE 9
#define FB_LIGHTGREEN 10
#define FB_LIGHTCYAN 11
#define FB_LIGHTRED 12
#define FB_LIGHTMAGENTA 13
#define FB_LIGHTBROWN 14
#define FB_WHITE 15
#define FB_COMMAND_PORT 0x3D4
#define FB_DATA_PORT 0x3D5
#define FB_HIGH_BYTE_COMMAND 14
#define FB_LOW_BYTE_COMMAND 15
char *fb = (char*)0x000B8000;
void fb_move_cursor(unsigned short pos) {
outb(FB_COMMAND_PORT, FB_HIGH_BYTE_COMMAND);
outb(FB_DATA_PORT, ((pos >> 8) & 0x00FF));
outb(FB_COMMAND_PORT, FB_LOW_BYTE_COMMAND);
outb(FB_DATA_PORT, pos & 0x00FF);
}
void fb_write_cell(unsigned int i, char c, unsigned char fg, unsigned char bg)
{
fb[i] = c;
fb[i + 1] = ((fg & 0x0F) << 4) | (bg & 0x0F);
}
void kmain(void) {
fb_write_cell(0, 'H', FB_WHITE, FB_BLACK);
fb_move_cursor(2);
fb_write_cell(1, 'i', FB_WHITE, FB_BLACK);
}
io.h
#ifndef INCLUDE_IOH
#define INCLUDE_IOH
void outb(unsigned short port, unsigned char data);
#endif
io.s
global outb
global hang
;Sends a byte to an io port
; [esp + 8] data byte
; [esp + 4] the io port
outb:
mov al, [esp + 8]
mov dx, [esp + 4]
out dx, al
ret
hang:
jmp hang; so that program can hang
loader.s
global loader
MAGIC_NUMBER equ 0x1BADB002 ;Multiboot constant
FLAGS equ 0x0 ;Multiboot flags
CHKSUM equ -MAGIC_NUMBER ;Multiboot checksum. Valid if CHKSUM + FLAGS + MAGIC_NUMBER == 0
KERNEL_STACK_SIZE equ 4096
section .bss
align 4
kernel_stack:
resb KERNEL_STACK_SIZE
section .text
align 4
dd MAGIC_NUMBER
dd FLAGS
dd CHKSUM
loader:
mov esp,kernel_stack+KERNEL_STACK_SIZE
extern kmain
call kmain
hang:
jmp hang
link.ld
ENTRY(loader) /* the name of the entry label */
SECTIONS {
. = 0x00100000; /* the code should be loaded at 1 MB */
.text ALIGN (0x1000) : /* align at 4 KB */
{
*(.text) /* all text sections from all files */
}
.rodata ALIGN (0x1000) : /* align at 4 KB */
{
*(.rodata*) /* all read-only data sections from all files */
}
.data ALIGN (0x1000) : /* align at 4 KB */
{
*(.data) /* all data sections from all files */
}
.bss ALIGN (0x1000) : /* align at 4 KB */
{
*(COMMON) /* all COMMON sections from all files */
*(.bss) /* all bss sections from all files */
}
}
Makefile
OBJECTS = loader.o kmain.o io.o
CC = gcc
CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \
-nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c
LDFLAGS = -T link.ld -melf_i386
AS = nasm
ASFLAGS = -f elf
all: kernel.elf
kernel.elf: $(OBJECTS)
ld $(LDFLAGS) $(OBJECTS) -o kernel.elf
os.iso: kernel.elf
cp kernel.elf iso/boot/kernel.elf
genisoimage -R \
-b boot/grub/stage2_eltorito \
-no-emul-boot \
-boot-load-size 4 \
-A os \
-input-charset utf8 \
-quiet \
-boot-info-table \
-o os.iso \
iso
run: os.iso
bochs -f bochsrc.txt -q
%.o: %.c
$(CC) $(CFLAGS) $< -o $#
%.o: %.s
$(AS) $(ASFLAGS) $< -o $#
clean:
rm -rf *.o kernel.elf os.iso
You can download the folder the project is in here
I expect to see "Hi" in the upper left corner, but instead I see 2 weird characters: Printing the first character and moving the cursor works fine, but when I attempt to print the second character, it messes up.
Edit: I realized that the first character is an incorrectly colored H.
#Margaret Bloom's comment solved my issue. The issue was that a character is 16 bits, and I needed to increment I by 2, but I was increment it by 1, causing it to overlap like this:
Intended: char1Data char1Color char2Data char2Color
Problem: char1Data char2Data char2Color
Related
I'm building a simple payload to execute on an ARM64 system that will print a "Hello, world!" string over UART.
hello-world-payload.c:
#include <stdint.h>
typedef uint32_t u32;
int _start() {
const char* txt = "Hello, world!\n";
volatile u32* uart_wfifo = (volatile u32*)0xc81004c0;
volatile u32* uart_status = (volatile u32*)0xc81004cc;
u32 i = 0;
char c = txt[0];
while (c) {
// wait for UART availability
do {} while (! (*uart_status & (1 << 22)) );
// print 1 character
*uart_wfifo = (0x000000ff & c);
c = txt[++i];
}
while (1) {} // wait for watchdog
}
Makefile:
CROSS_COMPILE ?= aarch64-linux-gnu-
CC = $(CROSS_COMPILE)gcc
OBJCOPY = $(CROSS_COMPILE)objcopy
AFLAGS = -nostdlib
CFLAGS = -O0 -nostdlib
LDFLAGS = -Wl,--build-id=none
all: hello-world-payload.bin
%.elf: %.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $# $^
%.bin: %.elf
$(OBJCOPY) -O binary -S -g --strip-unneeded \
-j .text \
-j .rodata \
$< $#
.PHONY: clean
clean:
rm hello-world-payload.bin
For cross compiler I use the gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf (AArch64 ELF bare-metal target) toolchain from ARM Developer.
With code above I get a 159 bytes binary that works just fine.
Once I move the txt out of the function scope this way:
typedef uint32_t u32;
const char* txt = "Hello, world!\n";
int _start() {
, the payload doesn't run anymore. After loading the payload binary into Ghidra I notice that the code tries to access txt at DAT_000100a0 while in fact it's stored at 0x90.
Since txt is const and is already initialized it should belong to the .rodata section which I confirmed by inspecting the assembly output of ${CROSS_COMPILE}gcc -O0 -nostdlib -Wl,--build-id=none -o hello-world-payload.s hello-world-payload.c -S, here's an excerpt from it:
.arch armv8-a
.file "hello-world-payload.c"
.text
.global txt
.section .rodata
.align 3
.LC0:
.string "Hello, world!\n"
.data
.align 3
.type txt, %object
.size txt, 8
I made sure I didn't forget to include .rodata in Makefile:
%.bin: %.elf
$(OBJCOPY) -O binary -S -g --strip-unneeded \
-j .text \
-j .rodata \
$< $#
The environment this binary runs in puts some constraints such as the max payload size (approx 29000 bytes in my case) and as far as I understood the binary must begin with the .text section so my goal is to keep the payload size as small as possible but I want to access various objects from different functions.
I inspected the ${CROSS_COMPILE}readelf -S output for hello-world-payload.o (${CROSS_COMPILE}gcc -O0 -nostdlib -Wl,--build-id=none -o hello-world-payload.o hello-world-payload.c):
Section Headers:
[Nr] Name Type Address Offset Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000400000 00010000 0000000000000090 0000000000000000 AX 0 0 4
[ 2] .rodata PROGBITS 0000000000400090 00010090 000000000000000f 0000000000000000 A 0 0 8
[ 3] .data PROGBITS 00000000004100a0 000100a0 0000000000000008 0000000000000000 WA 0 0 8
[ 4] .comment PROGBITS 0000000000000000 000100a8 000000000000005d 0000000000000001 MS 0 0 1
[ 5] .symtab SYMTAB 0000000000000000 00010108 00000000000001e0 0000000000000018 6 9 8
[ 6] .strtab STRTAB 0000000000000000 000102e8 000000000000006f 0000000000000000 0 0 1
[ 7] .shstrtab STRTAB 0000000000000000 00010357 0000000000000038 0000000000000000
I see there's a .data section so I tried to add it to the objcopy command in my Makefile:
%.bin: %.elf
$(OBJCOPY) -O binary -S -g --strip-unneeded \
-j .text \
-j .rodata \
-j .data \
$< $#
The binary size grows to whopping 65704 bytes but even with the .data section Ghidra shows the same DAT_000100a0 reference with nothing like the `"Hello, world!\n" string at that position:
The actual string is at 0x90 as it was before adding the .data section.
It is clear to me that the compiler messes up addresses of .rodata section where the string resides but I don't know how to fix it. Adding .data section didn't help.
Commonly with microcontrollers, the content of the .data section needs to be initialized by the start-up code from a section in non-volatile memory of the same size. Apparently your start-up code does not fulfill this requirement to run a C application.
In contrast to your belief, txt is an separate non-constant variable, because it is a modifiable pointer to the constant text. Your C code specifies to initialize this global variable with the address of the unnamed string. But no code does this.
You can make the global pointer variable constant, if you change your code to:
const char * const txt = "Hello, world!\n";
Now txt is located in .rodata.
You can avoid the global pointer variable at all, if you change your code to:
const char txt[] = "Hello, world!\n";
Now txt names the array of characters, which is located in .rodata.
In your first version of your program, txt was a dynamic variable on the stack. The code initialized it with the address of the unnamed string after entering the function _start().
I am following the tutorial from https://github.com/chipsetx/Simple-Kernel-in-C-and-Assembly. I am building it on macOS 12.4 using virtualised Ubuntu. I cannot get the kernel to compile and receive the unrecognised emulation mode: elf_i386 error.
GCC command does not work with -m32 option, but runs after I remove it.
The commands that I used are at the bottom of this post.
Thanks for help.
kernel.asm
;;kernel.asm
bits 32 ;nasm directive
section .text
;multiboot spec
align 4
dd 0x1BADB002 ;magic
dd 0x00 ;flags
dd - (0x1BADB002 + 0x00) ;checksum. m+f+c should be zero
global start
extern kmain ;kmain is defined in the c file
start:
cli ;block interrupts
call kmain
hlt ;halt the CPU
kernel.c
#define WHITE_TXT 0x07 /* light gray on black text */
void k_clear_screen();
unsigned int k_printf(char *message, unsigned int line);
/* simple kernel written in C */
void k_main()
{
k_clear_screen();
k_printf("Hello, world! Welcome to my kernel.", 0);
};
/* k_clear_screen : to clear the entire text screen */
void k_clear_screen()
{
char *vidmem = (char *) 0xb8000;
unsigned int i=0;
while(i < (80*25*2))
{
vidmem[i]=' ';
i++;
vidmem[i]=WHITE_TXT;
i++;
};
};
/* k_printf : the message and the line # */
unsigned int k_printf(char *message, unsigned int line)
{
char *vidmem = (char *) 0xb8000;
unsigned int i=0;
i=(line*80*2);
while(*message!=0)
{
if(*message=='\n') // check for a new line
{
line++;
i=(line*80*2);
*message++;
} else {
vidmem[i]=*message;
*message++;
i++;
vidmem[i]=WHITE_TXT;
i++;
};
};
return(1);
}
linker.ld
OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
SECTIONS
{
. = 0x100000;
.text : {*(.text)}
.data : {*(.data)}
.bss : {*(.bss)}
}
The commands I have used to build the project.
nasm -f elf32 kernel.asm -o kasm.o => WORKS
gcc -m32 -c kernel.c -o kc.o
=> unrecognized command line option -m32 (works when I remove it)
ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o
=> ERROR HERE: ld: unrecognised emulation mode: elf_i386
qemu-system-i386 -kernel kernel
I have the following test setup below. When I compile the code, I get the following error: The initUSART is not recognized, but I have included the file in the appropriate places. Did I miss out on anything?
AVR Development Stack
Get input main.c file...
Compile code and return elf file...
main.o: In function `main':
/Users/sebastianscharf/Documents/Hardware/AVR/main.c:14: undefined reference to `initUSART'
collect2: error: ld returned 1 exit status
avr-objcopy: 'main.elf': No such file
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: Expected signature for ATmega328 is 1E 95 14
Double check chip, or use -F to override this check.
avrdude done. Thank you.
main file
#include "config.h"
#include <avr/io.h>
#include <util/delay.h>
#include "USART.h"
int main(void) {
char serialCharacter;
// --- INITS --- //
DDRB = 0xff;
// Set up LED for output
initUSART();
return 0;
}
USART.h
/* These are defined for convenience */
#define USART_HAS_DATA bit_is_set(UCSR0A, RXC0)
#define USART_READY bit_is_set(UCSR0A, UDRE0)
uint8_t receiveByte(void);
/* Takes the defined BAUD and F_CPU, calculates the bit-clock multiplier, and configures the hardware USART*/
void initUSART(void);
USART.c
#include "config.h"
#include <avr/io.h>
#include <util/setbaud.h>
#include "USART.h"
void initUSART(void) {
// Requires BAUD
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
#if USE_2X
UCSR0A |= (1 << U2X0);
#else
UCSR0A &= ~(1 << U2X0);
#endif
// Enable USART
UCSR0B = (1 << TXEN0) | (1 << RXEN0);
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // 8 data bits, 1 stop bit
}
bash file
#!/bin/bash
source bashColors.sh
echo -e "${IGreen}AVR Development Stack${Reset}"
echo -e "${BIBlue}Get input main.c file...${Reset}"
avr-gcc -g -Os -mmcu=atmega328p -c main.c USART.c &&
echo -e "${BIBlue}Compile code and return elf file...${Reset}"
avr-gcc -g -mmcu=atmega328p -o main.elf main.o &&
echo -e "${BIBlue}Convert elf file to hex...${Reset}"
# avr-objcopy converts into hex file. -j indicates that we want the information from the .text and .data segment extracted.
avr-objcopy -j .text -j .data -O ihex main.elf out.hex &&
echo -e "${BIBlue}Uploading data to microcontroller...${Reset}"
avrdude -c usbtiny -p m328
You compiled both source files properly to .o with:
avr-gcc -g -Os -mmcu=atmega328p -c main.c USART.c
Now the main.o file contains an external reference to initUSART (partly thanks to the #include "USART.h" directive which provides the proper prototype)
but this link line:
avr-gcc -g -mmcu=atmega328p -o main.elf main.o &&
only references main.o. You need to add USART.o so the symbol is resolved by the linker (the linker doesn't care about .h include files), like this:
avr-gcc -g -mmcu=atmega328p -o main.elf main.o USART.o &&
I'm trying to make a kernel, and I cannot link the C output with the assembly. The ld. I'm getting the error:
unrecognized emulation mode: elf_i386
I'm using Windows 10 professional with the MinGW32 and MSYS. The code I am using:
link.ld
/*
* link.ld
*/
OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
SECTIONS
{
. = 0x100000;
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
kernel.c
/*
* kernel.c
*/
void kmain(void)
{
const char *str = "my first kernel";
char *vidptr = (char*)0xb8000; //video mem begins here.
unsigned int i = 0;
unsigned int j = 0;
/* this loops clears the screen
* there are 25 lines each of 80 columns; each element takes 2 bytes */
while(j < 80 * 25 * 2) {
/* blank character */
vidptr[j] = ' ';
/* attribute-byte - light grey on black screen */
vidptr[j+1] = 0x07;
j = j + 2;
}
j = 0;
/* this loop writes the string to video memory */
while(str[j] != '\0') {
/* the character's ascii */
vidptr[i] = str[j];
/* attribute-byte: give character black bg and light grey fg */
vidptr[i+1] = 0x07;
++j;
i = i + 2;
}
return;
}
kernel.asm
;;kernel.asm
bits 32 ;nasm directive - 32 bit
section .text
global start
extern kmain ;kmain is defined in the c file
start:
cli ;block interrupts
mov esp, stack_space ;set stack pointer
call kmain
hlt ;halt the CPU
section .bss
resb 8192 ;8KB for stack
stack_space:
To Compile and link I use:
nasm -f elf32 kernel.asm -o kasm.o
gcc -m32 -c kernel.c -o kc.o
ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o
I'm Using:
Gcc 4.8.1
Ld 2.25.1
Nasm 2.11.09rc1
Why am I getting this error, and how can I fix it?
The standard MinGW/32 LD linker doesn't output ELF binaries. Preferably you would be using an i686 cross-compiler, but if you're not you may be able to get away with the tips below.
It appears you are using Arjun's Let's Write a Kernel tutorial. If you are following that tutorial you have missed a step to make kernel.asm compatible with the GRUB boot loader and QEMU's -kernel option. Before we start you should read the rest of the tutorial. The following code adds a Multiboot header to kernel.asm to make it GRUB compatible:
;;kernel.asm
bits 32 ;nasm directive - 32 bit
global entry
extern _kmain ;kmain is defined in the c file
section .text
entry: jmp start
;multiboot spec
align 4
dd 0x1BADB002 ;magic
dd 0x00 ;flags
dd -(0x1BADB002 + 0x00) ;checksum. m+f+c should be zero
start:
cli ;block interrupts
mov esp, stack_space ;set stack pointer
call _kmain
hlt ;halt the CPU
section .bss
resb 8192 ;8KB for stack
stack_space:
Besides adding a header I've also put an entry label in the file and a jmp start to jump over the Multiboot header. I've done this to make it easy to set a breakpoint at 0x100000 in the future if you start debugging.
One other change is that on MinGW, GCC adds an underscore to function names by default. I've changed references to the C function kmain to _kmain. This differs from the Linux convention.
Since the entry point of our code is now entry instead of start I've modified link.ld to be:
/*
* link.ld
*/
OUTPUT_FORMAT(pei-i386)
ENTRY(entry)
SECTIONS
{
. = 0x100000;
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
Another important change in the file above is the usage of OUTPUT_FORMAT(pei-i386) . This will output a Portable Executable Image (32-bit) rather than an ELF (which isn't supported).
In order to build the kernel and produce an ELF image from the PEI-I386 we can use these commands:
nasm -f elf32 kernel.asm -o kasm.o
gcc -m32 -c kernel.c -o kc.o -ffreestanding -nostdlib -nostdinc
ld -T link.ld -o kernel kasm.o kc.o -build-id=none
objcopy -O elf32-i386 kernel kernel.elf
The LD command has been modified to not write out the build-id to the executable to avoid the Multiboot header from being shifted outside the first 8k of the executable. The GCC options have been modified to produce freestanding code (without the standard library and includes) using the options -ffreestanding -nostdlib -nostdinc. We use objcopy to convert the PEI-I386 file (kernel) to an ELF32 image called kernel.elf. You will want to be using kernel.elf with GRUB and/or QEMU.
I'm trying to get a 'hello world' type program running on my Beagleboard-xm rev. C, by calling a C puts function from assembly.
So far I've been using this as a reference: http://wiki.osdev.org/ARM_Beagleboard
Here's what I have so far, but there's no output.
hello.c
volatile unsigned int * const UART3DR = (unsigned int *)0x49020000;
void puts(const char *s) {
while(*s != '\0') {
*UART3DR = (unsigned int)(*s);
s++;
}
}
void hello() {
puts("Hello, Beagleboard!\n");
}
boot.asm
.global start
start:
ldr sp, =stack_bottom
bl hello
b .
linker.ld
ENTRY(start)
MEMORY
{
ram : ORIGIN = 0x80200000, LENGTH = 0x10000
}
SECTIONS
{
.hello : { hello.o(.text) } > ram
.text : { *(.text) } > ram
.data : { *(.data) } > ram
.bss : { *(.bss) } > ram
. = . + 0x5000; /* 4kB of stack memory */
stack_bottom = .;
}
Makefile
ARMGNU = arm-linux-gnueabi
AOPS = --warn --fatal-warnings
COPS = -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding
boot.bin: boot.asm
$(ARMGNU)-as boot.asm -o boot.o
$(ARMGNU)-gcc-4.6 -c $(COPS) hello.c -o hello.o
$(ARMGNU)-ld -T linker.ld hello.o boot.o -o boot.elf
$(ARMGNU)-objdump -D boot.elf > boot.list
$(ARMGNU)-objcopy boot.elf -O srec boot.srec
$(ARMGNU)-objcopy boot.elf -O binary boot.bin
Using just the asm file like this works.
.equ UART3.BASE, 0x49020000
start:
ldr r0,=UART3.BASE
mov r1,#'c'
Here are some Beagleboard/minicom related info: http://paste.ubuntu.com/829072/
Any pointers? :)
I also tried
void hello() {
*UART3DR = 'c';
}
I'm using minicom and send the file via ymodem, then I try to run it with:
go 0x80200000
Hardware and software control flow in minicom are off.
that should have worked for you. Here is some code I dug up from way back when, did not try it on a beagleboard tonight just made sure it compiled, it had worked at one time...
startup.s:
.code 32
.globl _start
_start:
bl main
hang: b hang
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.globl GET32
GET32:
ldr r0,[r0]
bx lr
hello.c :
extern void PUT32 ( unsigned int, unsigned int );
extern unsigned int GET32 ( unsigned int );
void uart_send ( unsigned char x )
{
while((GET32(0x49020014)&0x20)==0x00) continue;
PUT32(0x49020000,x);
}
void hexstring ( unsigned int d )
{
//unsigned int ra;
unsigned int rb;
unsigned int rc;
rb=32;
while(1)
{
rb-=4;
rc=(d>>rb)&0xF;
if(rc>9) rc+=0x37; else rc+=0x30;
uart_send(rc);
if(rb==0) break;
}
uart_send(0x0D);
uart_send(0x0A);
}
int main ( void )
{
hexstring(0x12345678);
return(0);
}
memmap (linker script):
MEMORY
{
ram : ORIGIN = 0x82000000, LENGTH = 256K
}
SECTIONS
{
ROM : { startup.o } > ram
}
Makefile :
CROSS_COMPILE = arm-none-eabi
AOPS = --warn --fatal-warnings
COPS = -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding
all : hello.bin
hello.bin : startup.o hello.o memmap
$(CROSS_COMPILE)-ld startup.o hello.o -T memmap -o hello.elf
$(CROSS_COMPILE)-objdump -D hello.elf > hello.list
$(CROSS_COMPILE)-objcopy hello.elf -O binary hello.bin
startup.o : startup.s
$(CROSS_COMPILE)-as $(AOPS) startup.s -o startup.o
hello.o : hello.c
$(CROSS_COMPILE)-gcc -c $(COPS) hello.c -o hello.o
clean :
rm -f *.o
rm -f *.elf
rm -f *.bin
rm -f *.list
Looks like I just left the stack pointer wherever the bootloader had it. Likewise, as you, assumed the bootloader had initialized the serial port.
I assume you have serial port access working, you see uboot and you are able to type commands in order to download this program (xmodem, or whatever) into the boards ram? If you cant do that then it may be you are not connected to the serial port right. the beagleboards serial port is screwy, might need to make your own cable.
You can't just blindly write a string of characters to a UART - you need to check status on each character - it works in the single character example because the UART is always going to be ready for the first character, but for the second and subsequent characters you need to poll (or better yet use an ISR, but let's walk before we run).
There's some good example code here: http://hardwarefreak.wordpress.com/2011/08/30/some-experience-with-the-beagleboard-xm-part-2/
I've not enough repetation to comment..
But my answere to
Works either way. Now the weird thing is that I can print individual
characters with with uart_send('c') for example, but cannot print
strings print_string(char *str){ while (*str != '\0') uart_send
(*str++); } print_string("Test"); . Any thoughts on this?
is:
You write faster in the output buffer, as UART is able to send..
So you've to check, if the output buffer is empty, before you send a new character.
I've done this in the code on my blog (http://hardwarefreak.wordpress.com/2011/08/30/some-experience-with-the-beagleboard-xm-part-2/)