当前位置: 首页 > 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.gsyq.cn/news/1430671.html

相关文章:

  • 看完就会:盘点2026年人气爆表的AI论文工具
  • 未来可期
  • ARM DS-5调试中共享库符号加载冲突解决方案
  • 免费音频标注工具终极指南:3分钟快速上手的专业解决方案
  • 备战蓝桥杯Java组别?先搞定这5类高频考点:进制转换、大数处理、组合数学、几何计算与动态规划
  • 终极指南:3分钟为Windows换上macOS风格鼠标指针
  • AMD Ryzen SDT调试工具:专业硬件性能优化的终极指南
  • 基于 MATLAB 的电力系统动态分析研究【IEEE9、IEEE68系节点】
  • ChatGPT登录流程全解析:从浏览器F12到Python脚本,一步步拆解‘套娃’式认证
  • 别再死记硬背!一张表理清SAP MDG所有主数据类型的工作流任务代码(物料/客户/供应商/财务)
  • Python算法基础篇之动态规划
  • 不只是安装:用MMDetection3D的Demo快速验证你的3D感知算法想法(KITTI/NuScenes实战)
  • Vue 3 + Three.js 新手也能搞定的全景看房Demo:从一张图到可交互场景
  • 免费在线法线贴图生成器:3分钟学会为3D模型添加逼真细节
  • Vue2项目里用AntV X6搞流程图?这份保姆级配置指南帮你搞定拖拽、导出和右键菜单
  • 2026义乌黄金回收靠谱商家推荐|铂金白银K金金条首饰回收价格与门店指南 - 同城好物推荐官
  • 2026 年了,还是忍不住做了一个浏览器翻译工具 [特殊字符]|免费体验!
  • 【Gemini生产环境运维铁律】:基于127家客户落地数据验证的8条不可妥协的SLA守护准则
  • Lindy效应遇上AI编码:3步构建自进化代码生成流水线(附GitHub开源模板)
  • 从‘gzip: stdin: not in gzip format’到成功解压:一个真实案例拆解Linux tar命令的格式陷阱
  • 避坑指南:用ESP32-IDF驱动SES/微雪墨水屏,这些寄存器细节和Busy引脚逻辑千万别搞错
  • 从STM32转战TMS320F28377D:手把手教你搞定CLA内存分配与CMD文件配置(避坑指南)
  • 从‘校验位’到‘检错位’:用Logisim拆解偶校验电路的数据‘安检’全过程
  • 【系统学AI】12 GraphRAG深度解析:当RAG遇上知识图谱
  • Blender - Study Notes 3
  • STM32F103C8T6硬件SPI驱动LCD屏幕,为什么HAL库的HAL_SPI_Transmit()函数反而拖慢了刷新率?
  • S2.0系列开篇:从抖音到Notion,上瘾设计的底层逻辑
  • Arm架构CPU挂起问题调试指南:使用DS-5与Arm DS
  • 从零构建AI聊天机器人:架构解析与Rasa实战指南
  • 别再手动算潮汐了!用Linux+OTPS工具箱+TPXO9模型,5分钟搞定批量水位预报