当前位置: 首页 > news >正文

8088单板机单步运行测试

1.汇编代码

.MODEL TINY .8086 .code ORG 100h ; COM 程序入口偏移 PUBLIC _nmi_handler ; 导出符号供C使用 PUBLIC _int1_isr extrn _main:proc extrn _display_intr:proc extrn _int1_echo:proc start: ; 手动设置堆栈(Tiny 模式要求) mov ax, cs mov ss, ax mov sp, 05fffh ;add ax,0100h mov ds,ax mov es,ax ; 调用 C 主函数 call near ptr _main ; 退出到 DOS(使用 AL 中的返回码) mov ah, 4Ch ; DOS 功能:程序退出 int 21h _nmi_handler PROC NEAR push ax ; 保存寄存器 inc _nmi_count ; nmi_count++ (C变量) pop ax ; 恢复寄存器 call near ptr _display_intr iret ; 中断返回 _nmi_handler ENDP _int1_isr PROC near push ax push bx push cx push dx push si push di push ds push es call _int1_echo ; 恢复寄存器 pop es pop ds pop di pop si pop dx pop cx pop bx pop ax iret _int1_isr ENDP .data EXTERN _nmi_count:WORD ; 声明外部C变量 public __acrtused __acrtused = 9876h .stack END start ; 指定入口点为 start

2.c代码

#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 cx='A'; unsigned int cs_adr=0,ds_adr=0,ss_adr=0; //////////////////////////////////////////////////////////////////////////////////// /// @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) { temp=inp(PC16550_LSR); if((temp&0x20)==0x20) { break; } } outp(PC16550_THR,x); } void uart_str_send(char *p) { //int i=0; //char str1[20]="Hello World!\r\n"; //char *p; //p=str1; while(*p!='\0') { uart_send(*p); p++; } /* for(i=0;i<14;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_disp=0; unsigned char digits[] = {1, 2, 3, 4, 5, 6, 7, 8}; // 要显示的数字 unsigned int cnt_run=0; 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_disp=0; // 设置位选 (选中当前位) 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_disp>7) {ip_disp=0;} } ////////////////////////////////////////////////////////////////////////////////// //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 // 命令端口 (A0=0) #define PIC1_DATA 0x402 // 数据端口 (A0=1) !!!!!!!!!! // 初始化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); extern void int1_isr(void); char buffer[80]; void int1_echo(void) { static int cnt=0; cnt++; tiny_sprintf(buffer,"INT1 count=%d \r\n",cnt); uart_str_send(buffer); } void main(void) /*检测按键状态并由LED发光二极管显示, 若按键闭合对应LED发光二极管点亮, 若按键断开对应LED发光二极管灭.*/ { int i=0; char buffer[80]; unsigned char key_code=0xff; // 使用安全格式化 //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(0x01, (void *)&int1_isr); set_int(0x08, (void *)&nmi_handler);//8259 与NMI共用一个中断服务函数 // set_int_csip(0x20, 0xf000, 0x3000); //f000:2000是disp_update函数的CS:IP地址,定时器中断服务函数 init_8255(); init_8253(); asm cli init_8259(); asm nop asm sti while (1) { //char button_state; //button_state=inp(ADR_244); //int i=0; //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_code=inp(PORT_8255_C)&0x0f; if(key_code!=0x0f) {cnt_run=0;} tiny_sprintf(buffer,"Key_code= 0X%x \r\n",key_code); uart_str_send(buffer); //asm int 8 //uart_send(cx); //display_intr(); for(i=0;i<5000;i++); for(i=0;i<5000;i++); outp(LED_PORT, 0xff); for(i=0;i<5000;i++); for(i=0;i<5000;i++); outp(LED_PORT, 0x00); __asm{ //mov ax,0f000h //mov ds,ax //mov // int 0x20 //mov ax,0000h //mov ds,ax //; 开启陷阱标志 TF,启用单步模式 pushf pop ax or ax, 0100h //; 置 TF 位(第 8 位) push ax popf } // int1_echo(); disp_update(); //display_numbers(); } } char end_flag[5]={0x55,0x55,0x55,0x55,0x55};

3.编译下载测试

http://www.jsqmd.com/news/919348/

相关文章:

  • 看完就会:盘点2026年人气爆表的AI论文工具
  • Android系统启动过程分析
  • 测试2-请忽略
  • 告别脚本地狱:用SeaTunnel 2.3.1 + Flink 1.16 搞定MySQL到ClickHouse的实时数据同步
  • 如何快速提升游戏效率:D3KeyHelper暗黑3终极自动化工具完整指南
  • ZLT X21 CPE的IP Passthrough模式实测:让你的NAS/软路由直接拿到公网IP,实现完美端口转发
  • ARM DS-5调试中共享库符号加载冲突解决方案
  • 未来可期
  • 告别蜂鸣器!用DY-SV17F语音模块给你的Arduino项目加上真人语音提示(附完整代码)
  • 告别“正在编译”:Nessus v10.9.4插件更新效率优化与资源监控实战
  • 3个常见问题,1个简单解决方案:OFD转PDF终极指南
  • 深入高通QMI的‘黑匣子’:用QXDM和日志分析一次失败的通信
  • 从 EXISTS 到 JOIN:PostgreSQL 子链接上拉优化的那些“坑”与避坑指南
  • 免费音频标注工具终极指南:3分钟快速上手的专业解决方案
  • 金融科技四大核心技术解析:区块链、AI、物联网与AR/VR如何重塑银行业
  • 如何用DouyinLiveWebFetcher零代码获取抖音直播实时数据:2025最新完整指南
  • 数据分析报告生成工具推荐:2026年AI报告自动化能力与企业适配性深度解析 - 科技焦点
  • 避开这5个Scratch编程思维误区,你的蓝桥杯省赛成绩还能再提50分 | 以2023中级组真题为例
  • 从游戏引擎到无人机:聊聊四元数解欧拉角为啥比直接算更靠谱
  • 备战蓝桥杯Java组别?先搞定这5类高频考点:进制转换、大数处理、组合数学、几何计算与动态规划
  • 企业指标管理系统排名:2026年指标治理能力与业务自助分析深度横评 - 科技焦点
  • 从HTTP报文到数据库查询:拆解TinyWebServer中用户登录注册的完整链路(C++/MySQL)
  • D2DX:让你的暗黑破坏神2在现代PC上焕然一新的终极指南
  • 打造四个九的在线CRM:从0到1构建99.99%可用性的核心架构
  • Simple Video Download Helper:终极免费视频下载解决方案深度探索
  • 5分钟免费解锁LOL国服所有皮肤:R3nzSkin换肤工具完整指南
  • 终极指南:3分钟为Windows换上macOS风格鼠标指针
  • 扎克伯格 Biohub 蛋白质生物学“世界模型“:AI 颠覆药物发现的全景解析
  • 戴尔G15笔记本散热控制终极指南:用开源工具彻底告别AWCC
  • AMD Ryzen SDT调试工具:专业硬件性能优化的终极指南