October 25, 2015
#ifndef __BOOT_ASM_H__
#define __BOOT_ASM_H__

/* Assembler macros to create x86 segments */

/* Normal segment */
#define SEG_NULLASM												\
	.word 0, 0;													\
	.byte 0, 0, 0, 0

#define SEG_ASM(type,base,lim)									\
	.word (((lim) >> 12) & 0xffff), ((base) & 0xffff);			\
	.byte (((base) >> 16) & 0xff), (0x90 | (type)),				\
		(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)


/* Application segment type bits */
#define STA_X		0x8		
#define STA_E		0x4		
#define STA_C		0x4		
#define STA_W		0x2		
#define STA_R		0x2		
#define STA_A		0x1		

#endif /* !__BOOT_ASM_H__ */

*****************************************

#include <asm.h>

# Start the CPU: switch to 32-bit protected mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with %cs=0 %ip=7c00.

.set PROT_MODE_CSEG,		0x8						# kernel code segment selector
.set PROT_MODE_DSEG,		0x10					# kernel data segment selector
.set CR0_PE_ON,				0x1						# protected mode enable flag

.globl start
start:
.code16												# Assemble for 16-bit mode
	cli
	cld

	# Set up the important data segment registers (DS, ES, SS).
	xorw %ax, %ax									# Segment number zero
	movw %ax, %ds									# -> Data Segment
	movw %ax, %es									# -> Extra Segment
	movw %ax, %ss									# -> Stack Segment

seta20.1:
	inb $0x64, %al									# Wait for not busy
	testb $0x2, %al
	jnz seta20.1


	movb $0xd1, %al									# 0xd1 -> port 0x64
	outb %al, $0x64

seta20.2:
	inb $0x64, %al									# Wait for not busy
	testb $0x2, %al
	jnz seta20.2

	movb $0xdf, %al									# 0xdf -> port 0x60
	outb %al, $0x60

	lgdt gdtdesc

	movl %cr0, %eax
	orl $CR0_PE_ON, %eax
	movl %eax, %cr0

	ljmp $PROT_MODE_CSEG, $protcseg

.code32												# Assemble for 32-bit mode
protcseg:
	movw $PROT_MODE_DSEG, %ax						
	movw %ax, %ds									# -> DS: Data Segment
	movw %ax, %es									# -> ES: Extra Segment
	movw %ax, %fs									# -> FS
	movw %ax, %gs									# -> GS
	movw %ax, %ss									# -> SS: Stack Segment

	movl $0x0, %ebp
	movl $start, %esp
	call bootmain
spin:
	jmp spin

.p2align 2

gdt:
	SEG_NULLASM
	SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)
	SEG_ASM(STA_W, 0x0, 0xffffffff)

gdtdesc:
	.word 0x17
	.long gdt

****************************************



module bootmain;



import types;

import x86;





const COM1 = 0x3F8;

const CRTPORT = 0x3D4;

const LPTPORT = 0x378;

const COM_TX = 0;

const COM_LSR = 5;

const COM_LSR_TXRDY = 20;



extern (C){

 uint8_t  inb(uint16_t port);

 void outb(uint16_t port, uint8_t data);

 uint16_t *crt;		// CGA memory

}



static this(){

     uint16_t *crt = cast(uint16_t *)0xB8000;

}



/* stupid I/O delay routine necessitated by historical PC design flaws */

static void

delay() {

	inb(0x84);

	inb(0x84);

	inb(0x84);

	inb(0x84);

}



/* lpt_putc - copy console output to parallel port */

static void

lpt_putc(int c) {

	int i;

	for (i = 0; !(inb(LPTPORT + 1) & 0x80) && i < 12800; i ++) {

		delay();

	}

	outb(LPTPORT + 0, cast(uint8_t)c);

	outb(LPTPORT + 2, 0x08 | 0x04 | 0x01);

	outb(LPTPORT + 2, 0x08);

}



/* cga_putc - print character to console */

static void

cga_putc(int c) {

	int pos;



	// cursor position: col + 80*row.

	outb(CRTPORT, 14);

	pos = inb(CRTPORT + 1) << 8;

	outb(CRTPORT, 15);

	pos |= inb(CRTPORT + 1);



	if (c == '\n') {

		pos += 80 - pos % 80;

	}

	else {

		crt[pos ++] = (c & 0xff) | 0x0700;

	}



	outb(CRTPORT, 14);

	outb(CRTPORT + 1, cast(uint8_t)(pos >> 8));

	outb(CRTPORT, 15);

	outb(CRTPORT + 1, cast(uint8_t)pos);

}



/* serial_putc - copy console output to serial port */

static void

serial_putc(int c) {

    int i;

	for (i = 0; !(inb(COM1 + COM_LSR) & COM_LSR_TXRDY) && i < 12800; i ++) {

		delay();

	}

	outb(COM1 + COM_TX, cast(uint8_t)c);

}



/* cons_putc - print a single character to console*/

static void

cons_putc(int c) {

	lpt_putc(c);

	cga_putc(c);

	serial_putc(c);

}



/* cons_puts - print a string to console */

static void

cons_puts(const char *str) {

	int i;

	for (i = 0; *str != '\0'; i ++) {

		cons_putc(str[i]);

	}

}



/* bootmain - the entry of bootloader */

static void

bootmain() {

	cons_puts("This is a bootloader: Hello world!!");



	/* do nothing */

	while (true){}

}

****************************



module types;



extern (C):

alias int BOOL;



alias char int8_t;

//C     typedef unsigned char uint8_t;

alias ubyte uint8_t;

//C     typedef short int16_t;

alias short int16_t;

//C     typedef unsigned short uint16_t;

alias ushort uint16_t;

//C     typedef int int32_t;

alias int int32_t;

//C     typedef unsigned int uint32_t;

alias uint uint32_t;

//C     typedef long long int64_t;

alias long int64_t;

//C     typedef unsigned long long uint64_t;

alias ulong uint64_t;



********************************



module x86;



import types;



 extern (C){

 uint8_t  inb(uint16_t port);

 void outb(uint16_t port, uint8_t data);

 }



extern (C) uint8_t inb(uint16_t port) {

	uint8_t data;

	asm

	{

	    mov DX,port;

	    in  AL,DX;

		mov data,AL;

	}

	return data;

}



extern (C) void outb(uint16_t port, uint8_t data) {

	asm

	{

	    mov DX,port;

		mov AL,data;

		out DX,AL;

	}

}



*****************************


bootasm:bootasm.S asm.h
	rm obj\*.o -rf
	gcc -I. -fno-builtin -Wall -ggdb -m16 -nostdinc -fno-stack-protector -Os -nostdinc -c bootasm.S -o obj/bootasm.o
bootmain:bootmain.d types.d x86.d
	ldc2 -L=Os -c $^ -odobj
all:bootasm bootmain


******************************

bootblock:bootasm.o bootmain.o x86.o types.o
	ld -m elf_i386 -N -e start -Ttext 0x7C00 $^ -o bootblock.o
	objdump -S bootblock.o > bootblock.asm
	objcopy -S -O binary bootblock.o bootblock.out
	rm bootblock -f
	./writeHDD bootblock.out bootblock
	dd if=/dev/zero of=ucore.img count=10000
	dd if=bootblock of=ucore.img conv=notrunc

October 25, 2015
ld -m elf_i386 -N -e start -Ttext 0x7C00 bootasm.o bootmain.o x86.o types.o -o bootblock.o
bootasm.o:在函数‘protcseg’中:
/home/glg/桌面/bootloader_D/bootasm.S:60:对‘bootmain’未定义的引用
bootmain.o:在函数‘ldc.dso_ctor.8bootmain’中:
bootmain.d:(.text.ldc.dso_ctor.8bootmain[ldc.dso_ctor.8bootmain]+0x4d):对‘_d_dso_registry’未定义的引用
bootmain.o:在函数‘ldc.dso_dtor’中:
bootmain.d:(.text.ldc.dso_dtor[ldc.dso_dtor]+0x4d):对‘_d_dso_registry’未定义的引用
x86.o:在函数‘ldc.dso_ctor.3x86’中:
x86.d:(.text.ldc.dso_ctor.3x86[ldc.dso_ctor.3x86]+0x4d):对‘_d_dso_registry’未定义的引用
types.o:在函数‘ldc.dso_ctor.5types’中:
types.d:(.text.ldc.dso_ctor.5types[ldc.dso_ctor.5types]+0x4d):对‘_d_dso_registry’未定义的引用
makefile:2: recipe for target 'bootblock' failed
make: *** [bootblock] Error 1

October 25, 2015
I need excample bootloader(asm+D).
(dmd,gdc,ldc build flag)
(linker flag)
to now,D no have bootloader(asm+D).
October 26, 2015
It's a missing function _d_dso_registry. Not sure if you need it or not, but you can stub it:
extern(C) void _d_dso_registry(void* data) { }

Also don't use static constructors yet. They are invoked during initialization of modules, which you probably don't run.
October 26, 2015
How build .bin?

eg.

ldc -c bootmain.d of bin

October 26, 2015
You mean a CD image with your loader in MBR?
October 26, 2015
file asm.h

/*
是bootasm.S汇编文件所需要的头文件,主要是一些与X86保护模式的段访问方式相关的宏定义
*/

#ifndef __BOOT_ASM_H__
#define __BOOT_ASM_H__

/* Assembler macros to create x86 segments */

/* Normal segment */
#define SEG_NULLASM												\
	.word 0, 0;													\
	.byte 0, 0, 0, 0

#define SEG_ASM(type,base,lim)									\
	.word (((lim) >> 12) & 0xffff), ((base) & 0xffff);			\
	.byte (((base) >> 16) & 0xff), (0x90 | (type)),				\
		(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)


/* Application segment type bits */
#define STA_X		0x8		// 可执行
#define STA_E		0x4		// 向下扩展段(非可执行段)
#define STA_C		0x4		// 一致性代码段(只执行)
#define STA_W		0x2		// 段可写(非可执行段)
#define STA_R		0x2		// 段可读 (可执行段)
#define STA_A		0x1		// 可访问

#endif /* !__BOOT_ASM_H__ */

**********************************************************
file bootasm.S

# 定义并实现了bootloader最先执行的函数start,此函数进行了一定的初始化,完成了
# 从实模式到保护模式的转换,并调用bootmain.c中的bootmain函数

#include <asm.h>

# Start the CPU: switch to 32-bit protected mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with %cs=0 %ip=7c00.

# gdt 全局描述符表内的数组索引
.set PROT_MODE_CSEG,		0x8						# kernel code segment selector
.set PROT_MODE_DSEG,		0x10					# kernel data segment selector
.set CR0_PE_ON,				0x1						# protected mode enable flag

.globl start
start:
.code16												# Assemble for 16-bit mode
	cli												# 禁用中断
	cld												# 字符串操作设定为递增 si++ di++ ,cld的作用是将direct flag标志位清零

	# Set up the important data segment registers (DS, ES, SS).
	xorw %ax, %ax									# Segment number zero
	movw %ax, %ds									# -> Data Segment
	movw %ax, %es									# -> Extra Segment
	movw %ax, %ss									# -> Stack Segment

	# A20地址线控制打开工作
	# Enable A20:
	# 为了向后兼容早期的PC机,让物理地址线20接低电平
	# 如果A20是关闭的,16bit的寻址范围2^20是1M,如果是打开的,那么就是2^21次方,
	# 但是寻址还是FFFFh:FFFFh=FFFF0h+FFFFh=10FFEFh=1M+64K-16Bytes
seta20.1:
	inb $0x64, %al									# Wait for not busy
	testb $0x2, %al
	jnz seta20.1      #测试 bit 1 是不是为0,如果不是跳回去继续执行

	# 对于键盘的8042控制芯片 0x64是命令端口 0xd1 代表写命令
	movb $0xd1, %al									# 0xd1 -> port 0x64
	outb %al, $0x64

seta20.2:
	inb $0x64, %al									# Wait for not busy
	testb $0x2, %al
	jnz seta20.2

	# 设置写命令后 给0x60端口 发送命令数据0xdf就是打开A20地址线,0xdd就是关闭
	movb $0xdf, %al									# 0xdf -> port 0x60
	outb %al, $0x60

	# 转入保护模式,这里需要指定一个临时的GDT,来翻译逻辑地址。
	# 这里使用的GDT通过gdtdesc段定义,它翻译得到的物理地址和虚拟地址相同,
	# 所以转换过程中内存映射不会改变
	lgdt gdtdesc									# 启动保护模式前建立好的段描述符合段描述符表
	
	# 打开保护模式标志位,相当于按下了保护模式的开关。
	# cr0寄存器的第0位就是这个开关,通过CR0_PE_ON或cr0寄存器,将第0位置1
	movl %cr0, %eax
	orl $CR0_PE_ON, %eax
	movl %eax, %cr0

	# 由于上面的代码已经打开了保护模式了,所以这里要使用逻辑地址,
	# 而不是之前实模式的地址了。这里用到了PROT_MODE_CSEG,
	# 他的值是0x8。根据段选择子的格式定义,0x8就翻译成:
  #      INDEX         TI     CPL
  #      0000 0000 0000 1          00      0
    # INDEX代表GDT中的索引,TI代表使用GDTR中的GDT, CPL代表处于特权级。
	# PROT_MODE_CSEG选择子选择了GDT中的第1个段描述符。
	# 这里使用的gdt就是变量gdt,下面可以看到gdt的第1个段描述符的基地址是0x0000,
	# 所以经过映射后和转换前的内存映射的物理地址一样。0000:7C00=0x00007C00 0000:protcseg 都是相对于物理内存0000基址的
	ljmp $PROT_MODE_CSEG, $protcseg

.code32												# Assemble for 32-bit mode
protcseg:
	# 重新初始化各个段寄存器。也就是采用平坦式内存方式,
	# 代码段同其它段都采用一个内存空间
	movw $PROT_MODE_DSEG, %ax						# 自定义数据段选择子,因为段选择子是16位的
	movw %ax, %ds									# -> DS: Data Segment
	movw %ax, %es									# -> ES: Extra Segment
	movw %ax, %fs									# -> FS
	movw %ax, %gs									# -> GS
	movw %ax, %ss									# -> SS: Stack Segment

	# 栈顶设定在start处,也就是地址0x7c00处,        # 低地址 0x0000    ^  此地址为栈基址 0000
	# call函数将返回地址入栈,将控制权交给bootmain   #                 /|\
	movl $0x0, %ebp                                  #                  |
	movl $start, %esp                                #                  |
	call bootmain                                    # 地址   0x7C00    |  栈顶指针

	# bootmain 如果返回,就会在此处死循环,但是目前的bootmain的函数不会返回了,因为他内部就会死循环
spin:
	jmp spin

# 注意以下数据结构
# Bootstrap GDT
.p2align 2											# 调整为4字节对齐
# 3个段描述符,每个段描述符占8字节,共24字节
# gdtdesc指出了全局描述符表(可以看成是段描述符组成的一个数组)的起始位置在gdt符号处,
# 而gdt符号处放置了三个段描述符的信息

# 第一个是NULL段描述符,没有意义,表示全局描述符表的开始
# 紧接着是代码段描述符(位于全局描述符表的0x8处的位置),具有可读(STA_R)和可执行(STA_X)的属性,
#   并且段起始地址为0,段大小为4GB;
# 接下来是数据段描述符(位于全局描述符表的0x10处的位置),具有可读(STA_R)和可写(STA_W)的属性,
#   并且段起始地址为0,段大小为4GB。
gdt:
	SEG_NULLASM										# null seg NULL段
	SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)			# code seg for bootloader and kernel CODE段
	SEG_ASM(STA_W, 0x0, 0xffffffff)					# data seg for bootloader and kernel DATA段

gdtdesc:
	.word 0x17										# sizeof(gdt) - 1 类似于数组 数组是0开始的,所以数组长度就要减1
	.long gdt										# address gdt

*****************************************************************************
file bootmain.c

/*
定义并实现了bootmain函数实现了通过屏幕、串口和并口显示字符串
*/

//#include <types.h>
//#include <x86.h>

#define COM1			0x3F8
#define CRTPORT			0x3D4
#define LPTPORT			0x378
#define COM_TX			0			// Out: Transmit buffer (DLAB=0)
#define COM_LSR			5			// In:  Line Status Register
#define COM_LSR_TXRDY	20			// Transmit buffer avail

static uint16_t *crt = (uint16_t *) 0xB8000;		// CGA memory

/* stupid I/O delay routine necessitated by historical PC design flaws */
static void
delay(void) {
	inb(0x84);
	inb(0x84);
	inb(0x84);
	inb(0x84);
}

/*
考虑到简单性,在proj1中没有对并口设备进行初始化,通过并口进行输出的过程也很简单:
第一步:执行inb指令读取并口的I/O地址(LPTPORT + 1)的值,如果发现发现读出的值代表并口忙,
则空转一小会再读;
如果发现发现读出的值代表并口空闲,则执行outb指令把字符写到并口的I/O地址(LPTPORT ),
这样就完成了一个字符的并口输出。
*/
/* lpt_putc - copy console output to parallel port */
static void
lpt_putc(int c) {
	int i;
	for (i = 0; !(inb(LPTPORT + 1) & 0x80) && i < 12800; i ++) {
		delay();
	}
	outb(LPTPORT + 0, c);
	outb(LPTPORT + 2, 0x08 | 0x04 | 0x01);
	outb(LPTPORT + 2, 0x08);
}

/*
通过CGA显示控制器进行输出的过程也很简单:首先通过in/out指令获取当前光标位置;
然后根据得到的位置计算出显存的地址,直接通过访存指令写内存来完成字符的输出;
最后通过in/out指令更新当前光标位置。
*/
/* cga_putc - print character to console */
static void
cga_putc(int c) {
	int pos;

	// cursor position: col + 80*row.
	outb(CRTPORT, 14);
	pos = inb(CRTPORT + 1) << 8;
	outb(CRTPORT, 15);
	pos |= inb(CRTPORT + 1);

	if (c == '\n') {
		pos += 80 - pos % 80;
	}
	else {
		crt[pos ++] = (c & 0xff) | 0x0700;
	}

	outb(CRTPORT, 14);
	outb(CRTPORT + 1, pos >> 8);
	outb(CRTPORT, 15);
	outb(CRTPORT + 1, pos);
}

/*
通过串口进行输出的过程也很简单:第一步:执行inb指令读取串口的I/O地址(COM1 + COM_LSR)的值,
如果发现发现读出的值代表串口忙,则空转一小会(0x84是什么地址???);
如果发现发现读出的值代表串口空闲,则执行outb指令把字符写到串口的I/O地址(COM1 + COM_TX),
这样就完成了一个字符的串口输出。
*/
/* serial_putc - copy console output to serial port */
static void
serial_putc(int c) {
    int i;
	for (i = 0; !(inb(COM1 + COM_LSR) & COM_LSR_TXRDY) && i < 12800; i ++) {
		delay();
	}
	outb(COM1 + COM_TX, c);
}

/* 显示字符的函数接口*/
/* 一个cons_putc函数接口,完成字符的输出*/
/* cons_putc - print a single character to console*/
static void
cons_putc(int c) {
	lpt_putc(c);
	cga_putc(c);
	serial_putc(c);
}

/* 提供了一个cons_puts函数接口:完成字符串的输出*/
/* cons_puts - print a string to console */
static void
cons_puts(const char *str) {
	int i;
	for (i = 0; *str != '\0'; i ++) {
		cons_putc(*str ++);
	}
}

/* bootmain - the entry of bootloader */
void
bootmain(void) {
	cons_puts("This is a bootloader: Hello world!!");

	/* do nothing */
	while (1);
}

***************************************************
These codes(asm.h bootasm.S bootmain.c) trans to (asm.h bootasm.S bootmain.d).

ldc -c asm.h bootasm.S bootmain.d

ld bootasm.o bootmain.o of outbin.o


October 26, 2015
bootloader of [asm+D] version
October 26, 2015
In C version you compile bootmain.c to object file bootmain.o, in d version you again get bootmain.o, after that you do the same as in C version.
October 26, 2015
wrong,Unable to link