
1.设计系统被调用功能;------------------------------------------------------------------------------------------- ;2017.9.15 ;用nasm重新写原来的代码 ;推出配套的《在做中学微机原理》课程与所销售的硬件相配套 ;从第一行程序开教一步一步带你走入神秘的8086世界。 ; ;例程001 ;ex1.asm example_1 ;8088启动,点亮系统板上的LED ;重点在于正确使用程序编辑环境nasm汇编方法EEprom的烧写方法 ; ;ex001----用下载到RAM中的程序点亮系统板上的LED ;8088.asm是汇编源程序 ;8088.bin是编译生成的可下载执行的最终代码 ; ;编译很简单双击make即可自动完成 ; ;Notepad是我计算机上Notepad的快捷方式 ;--------------------------------------------------------------------------------------------- led_port equ 800h ;--------------------------------------------------------------------------------------------- org 2000h ;程序将由监控bios下载到内存RAM的地址0000:2000,ROM(F000:2000) start: ;------------------------------------------------------ ;init part ;------------------------------------------------------ ;mov ax,0 ;mov ds,ax ;mov ss,ax ;mov sp,7fffh NOP PUSH AX PUSH BX PUSH CX PUSH DX PUSH SI PUSH DI st001: nop ;----------------------------------------------------- ;点亮系统板上的LED ;----------------------------------------------------- ;MOV DX,800H ;800H是板子上8个LED的端口地址号 mov dx,led_port MOV AL,0aaH ;一亮一灭间隔点亮 OUT DX,AL call DELAY ;MOV DX,800H ;800H是板子上8个LED的端口地址号 mov dx,led_port MOV AL,00H ;一亮一灭间隔点亮 OUT DX,AL call DELAY mov ah,$ call SEND ; lea di,STR1 ;nasm编译器不认 mov di, STR1 CALL STR_OUT ;mov di, STR2 ;CALL STR_OUT ;mov al,01010101b ;mov al,0f0h; ;out 25H,al ;hlt POP DI POP SI POP DX POP CX POP BX POP AX iret jmp st001 jmp start ;------------------------------------------------------------ ; DELAY PROC NEAR ;------------------------------------------------------------ ;push DELAY: PUSH CX PUSH BX NOP NOP mov bx,10 del1: mov cx,5882 del2: loop del2 dec bx jnz del1 ;pop cx ;pop bx POP BX POP CX RET ;------------------------------------------- ;pc16550 uart send by AH ;------------------------------------------- LSTAT EQU 1F5H DAT_165 EQU 1F0H SEND: ; PROC NEAR MOV DX,LSTAT WAITx: IN AL,DX TEST AL,20H JZ WAITx MOV AL,AH MOV DX,DAT_165 OUT DX,AL RET ;SEND ENDP ;------------------------------------------- ;串口打印字符串 ;------------------------------------------- STR_OUT :; PROC NEAR nop; LEA DI,STR1 STR_LOOP: ;MOV AH,BYTE PTR [DI] mov ah,byte [di] CMP AH,$ JZ STR_EXIT CALL SEND INC DI JMP STR_LOOP STR_EXIT: NOP RET ;STR_OUT ENDP STR1 DB i8088-HELLO boy or girl! ,13,10,$ STR2 DB 0AH,0DH, ,0dh,0ah,$ db 55h,55h,55h,55h,55h,55h ;程序结束标志监控bios程序收到后自动跳转到内存0000:2000处执行 ;----------------------------------------------------------------------------------------------2.将被调用程序编译生成bin文件追加到BootLoader 2000H位置3.烧写BootLoader ROM被调用程序地址为F000:2000H4.在应用程序中软中断调用#include tiny_stdarg.h // 使用自定义可变参数实现 #define ADR_273 0x0200 #define ADR_244 0x0400 #define LED_PORT 0x800 #define PC16550_THR 0x1f0 #define PC16550_LSR 0x1f5 ///////////////////////////////////////////////////////////////////////////////// //基本的IO操作函数 ///////////////////////////////////////////////////////////////////////////////// char str[]Hello World! 20250531 Very Ok!!!\r\n; //char buff[60] char cxA; unsigned int cs_adr0,ds_adr0,ss_adr0; //////////////////////////////////////////////////////////////////////////////////// /// brief /// param addr /// param data void outp(unsigned int addr, char data) // 输出一字节到I/O端口 { __asm { mov dx, addr mov al, data out dx, al } } char inp(unsigned int addr) // 从I/O端口输入一字节 { char result; __asm { mov dx, addr in al, dx mov result, al } return result; } void register_read(void) { __asm { mov ax,CS mov cs_adr,ax mov ax,DS mov ds_adr,ax mov ax,SS mov ss_adr,ax } } //////////////////////////////////////////////////////////////////////////////////// //串口发送函数 //////////////////////////////////////////////////////////////////////////////////// void uart_send(char x) { int temp; while(1) { tempinp(PC16550_LSR); if((temp0x20)0x20) { break; } } outp(PC16550_THR,x); } void uart_str_send(char *p) { //int i0; //char str1[20]Hello World!\r\n; //char *p; //pstr1; while(*p!\0) { uart_send(*p); p; } /* for(i0;i14;i) { uart_send(str1[i]); } */ } /////////////////////////////////////////////////////////////////////////////////// /* sprintf()函数实现 */ /* tiny_sprintf.c */ #include tiny_stdarg.h static void itoa(unsigned num, int base, char *out) { char buf[6]; // 16位整数最大5位数字 结束符 char *p buf; int i 0; if (num 0) { *out 0; *out \0; return; } while (num 0) { int r num % base; *p (r 10) ? (r 0) : (r - 10 a); num / base; i; } while (i-- 0) { *out *--p; } *out \0; } int tiny_sprintf(char *buf, const char *fmt, ...) { va_list args; char *p buf; const char *s fmt; va_start(args, fmt); while (*s) { if (*s ! %) { *p *s; continue; } s; switch (*s) { case d: { int num va_arg(args, int); if (num 0) { *p -; num -num; } itoa(num, 10, p); while (*p) p; s; break; } case x: { unsigned num va_arg(args, unsigned); itoa(num, 16, p); while (*p) p; s; break; } case s: { char *str va_arg(args, char *); while (*str) *p *str; s; break; } case c: { char c (char)va_arg(args, int); *p c; s; break; } case %: { *p %; s; break; } default: { *p %; *p *s; break; } } } *p \0; va_end(args); return p - buf; } /////////////////////////////////////////////////////////////////////////////////// //NMI 中断 ////////////////////////////////////////////////////////////////////////////////// /* NMI 计数器 */ volatile unsigned char nmi_count 10; //设置中断失量表 void set_int(unsigned char int_no, void * service_proc) { _asm { push es xor ax, ax mov es, ax mov al, int_no xor ah, ah shl ax, 1 shl ax, 1 mov si, ax mov ax, service_proc mov es:[si], ax inc si inc si mov bx, cs mov es:[si], bx pop es } } void set_int_csip(unsigned char int_no, unsigned int cs00, unsigned int ip00) { _asm { push es xor ax, ax mov es, ax mov al, int_no xor ah, ah shl ax, 1 shl ax, 1 mov si, ax mov ax, ip00 mov es:[si], ax inc si inc si mov bx, cs00 mov es:[si], bx pop es } } //中断处理函数 /* void _interrupt near nmi_handler(void) { nmi_count; } */ ////////////////////////////////////////////////////////////////////////////////// //8255 ////////////////////////////////////////////////////////////////////////////////// // 定义8255端口地址 (根据原理图译码确定) #define PORT_8255_A 0x200 // PA端口地址 #define PORT_8255_B 0x201 // PB端口地址 #define PORT_8255_C 0x202 // PC端口地址 #define PORT_8255_CTRL 0x203 // 控制寄存器地址 // 数码管段码表 (共阴极) unsigned char seg_codes[] { 0x3F, // 0 0x06, // 1 0x5B, // 2 0x4F, // 3 0x66, // 4 0x6D, // 5 0x7D, // 6 0x07, // 7 0x7F, // 8 0x6F // 9 }; // 延时函数 void delay(unsigned int ms) { for (unsigned int i 0; i ms; i) { for (unsigned int j 0; j 100; j) { // 空循环延时 } } } // 初始化8255 void init_8255() { // 控制字: 10000001 (0x81) // A口输出, B口输出, C口输出 outp(PORT_8255_CTRL, 0x81); } // 显示8位数字 void display_numbers() { unsigned char digits[] {1, 2, 3, 4, 5, 6, 7, 8}; // 要显示的数字 while (1) { // 按任意键退出 for (int i 0; i 8; i) { // 设置位选 (选中当前位) outp(PORT_8255_B, ~(1 i)); // 设置段码 outp(PORT_8255_A, ~seg_codes[digits[i]]); // 延时保持显示 delay(1); // 关闭当前位显示 (消除鬼影) outp(PORT_8255_A, 0x00); } } } //定时中断8位数码管动态显示 //int ip_disp0; unsigned char digits[] {1, 2, 3, 4, 5, 6, 7, 8}; // 要显示的数字 unsigned int cnt_run0; void disp_update(void) { cnt_run; digits[0]digits[1]digits[2]0; digits[3]cnt_run/10000%10; digits[4]cnt_run/1000%10; digits[5]cnt_run/100%10; digits[6]cnt_run/10%10; digits[7]cnt_run%10; } void display_intr() { // unsigned char digits[] {1, 2, 3, 4, 5, 6, 7, 8}; // 要显示的数字 static int ip_disp0; // 设置位选 (选中当前位) outp(PORT_8255_B, ~(1 ip_disp)); // 设置段码 outp(PORT_8255_A, ~seg_codes[digits[ip_disp]]); //outp(PORT_8255_A, 0x06); // 延时保持显示 delay(1); // 关闭当前位显示 (消除鬼影) //outp(PORT_8255_A, 0x00); ip_disp; if(ip_disp7) {ip_disp0;} } ////////////////////////////////////////////////////////////////////////////////// //8253 part ////////////////////////////////////////////////////////////////////////////////// // 8253定时器端口定义 #define PORT_8253_CNT0 0x300 // 计数器0地址 #define PORT_8253_CNT1 0x301 // 计数器1地址 #define PORT_8253_CNT2 0x302 // 计数器2地址 #define PORT_8253_CTRL 0x303 // 控制寄存器地址 // 时钟频率定义 (根据原理图第4页) #define PCLK_FREQUENCY 1193182 // 标准8253时钟频率(1.193182MHz) //#define OUTPUT_FREQUENCY 200 // 目标输出频率(1kHz) #define OUTPUT_FREQUENCY 180 // 目标输出频率(1kHz) // 计算计数器初值 #define COUNTER_VALUE (unsigned int)(PCLK_FREQUENCY / OUTPUT_FREQUENCY) // 初始化8253定时器 void init_8253() { // 控制字: 00110110 (0x36) // 选择计数器0 | 写入高低字节 | 模式3(方波) | 二进制计数 outp(PORT_8253_CTRL, 0x36); // 写入计数器初值 (先低字节后高字节) outp(PORT_8253_CNT0, COUNTER_VALUE 0xFF); // 低字节 outp(PORT_8253_CNT0, (COUNTER_VALUE 8) 0xFF); // 高字节 } ////////////////////////////////////////////////////////////////////////////////// //8259 part ////////////////////////////////////////////////////////////////////////////////// #define PIC1_CMD 0x400 // 命令端口 (A00) #define PIC1_DATA 0x402 // 数据端口 (A01) !!!!!!!!!! // 初始化8259设置自动EOI void init_8259(void) { // ICW1: 边沿触发 | 单片 | 需要ICW4 outp(PIC1_CMD, 0x17); // ICW2: 中断向量基址20h outp(PIC1_DATA, 0x08); // ICW4: 8086模式 | 自动EOI (0x03) outp(PIC1_DATA, 0x0f); // 关键修改设置自动EOI模式 // OCW1: 只允许IR0中断 (11111110b) outp(PIC1_DATA, 0xfe); } ////////////////////////////////////////////////////////////////////////////////// //char end_flag[5]{0x55,0x55,0x55,0x55,0x55}; extern void nmi_handler(void); void main(void) /*检测按键状态并由LED发光二极管显示, 若按键闭合对应LED发光二极管点亮 若按键断开对应LED发光二极管灭.*/ { int i0; char buffer[80]; unsigned char key_code0xff; // 使用安全格式化 //tiny_sprintf(buffer, Hex: %x\n,255); // 使用安全格式化 asm cli tiny_sprintf(buffer, Decimal: %d \n Hex: %x \n String: %s \r\n, -123, 0xABCD, Hello); register_read(); //set_nmi_handler(); //set_int(0x02, (void *)nmi_handler); // set_int(0x08, (void *)nmi_handler);//8259 与NMI共用一个中断服务函数 set_int_csip(0x20, 0xf000, 0x2000); //f000:2000是disp_update函数的CS:IP地址定时器中断服务函数 init_8255(); init_8253(); asm cli init_8259(); asm nop asm sti while (1) { //char button_state; //button_stateinp(ADR_244); //int i0; //uart_str_send(str); uart_str_send(buffer); tiny_sprintf(buffer,******************************************\r\n); uart_str_send(buffer); tiny_sprintf(buffer,CS_ADR 0X%x \r\n,cs_adr); uart_str_send(buffer); tiny_sprintf(buffer,DS_ADR 0X%x \r\n,ds_adr); uart_str_send(buffer); tiny_sprintf(buffer,SS_ADR 0X%x \r\n,ss_adr); uart_str_send(buffer); tiny_sprintf(buffer,8259 Interrupt count%d \r\n,nmi_count); uart_str_send(buffer); tiny_sprintf(buffer,******************************************\r\n); uart_str_send(buffer); key_codeinp(PORT_8255_C)0x0f; if(key_code!0x0f) {cnt_run0;} tiny_sprintf(buffer,Key_code 0X%x \r\n,key_code); uart_str_send(buffer); //asm int 8 //uart_send(cx); //display_intr(); for(i0;i5000;i); for(i0;i5000;i); outp(LED_PORT, 0xff); for(i0;i5000;i); for(i0;i5000;i); outp(LED_PORT, 0x00); __asm{ mov ax,0f000h mov ds,ax //mov int 0x20 mov ax,0000h mov ds,ax } disp_update(); //display_numbers(); } } char end_flag[5]{0x55,0x55,0x55,0x55,0x55};