Intel8259汇编串口接收转C语言

1.实现目标

把汇编编写的Intel8259中断接收程序,转成C语言实现。

2.串口芯片PC16550初始化

直接适用嵌入式汇编实现。

cpp 复制代码
/* ; 16550寄存器偏移
UART_BASE EQU 100H
RBR_THR EQU UART_BASE + 0  ; 接收缓冲/发送保持寄存器
IER     EQU UART_BASE + 1  ; 中断使能寄存器
IIR_FCR EQU UART_BASE + 2  ; 中断标识/FIFO控制寄存器
LCR     EQU UART_BASE + 3  ; 线路控制寄存器
MCR     EQU UART_BASE + 4  ; 调制解调器控制寄存器
LSR     EQU UART_BASE + 5  ; 线路状态寄存器
DLL     EQU UART_BASE + 0  ; 除数锁存低字节 (DLAB=1)
DLM     EQU UART_BASE + 1  ; 除数锁存高字节 (DLAB=1) */

#define  UART_BASE     0x100
#define  RBR_THR     UART_BASE+0
#define  IER         UART_BASE+1
#define  IIR_FCR     UART_BASE+2
#define  LCR         UART_BASE+3
#define  MCR         UART_BASE+4
#define  LSR         UART_BASE+5
#define  DLL         UART_BASE+0
#define  DLM         UART_BASE+1
//#define  RBR_THR     UART_BASE+0


void   uart_pc16550_init(void)
{
  __asm 
  {
      ; 设置波特率除数 (9600 @ 18.432MHz)
    MOV DX, LCR
    MOV AL, 80H         ; 设置DLAB=1
    OUT DX, AL
    
    MOV DX, DLL         ; 除数锁存低字节
    MOV AL, 78H         ; 120 = 78H (18.432MHz / (16 * 9600))
    OUT DX, AL
    
    MOV DX, DLM         ; 除数锁存高字节
    MOV AL, 00H
    OUT DX, AL
    
    ; 设置线路参数: 8位数据, 1停止位, 无校验
    MOV DX, LCR
    MOV AL, 03H         ; 8N1, DLAB=0
    OUT DX, AL
    
    ; 启用并复位FIFO
    MOV DX, IIR_FCR
    MOV AL, 0C7H        ; 启用FIFO, 14字节触发点, 清除接收FIFO
    OUT DX, AL
    
    ; 设置调制解调器控制
    MOV DX, MCR
    MOV AL, 0BH         ; 启用OUT2(中断使能), RTS和DTR
    OUT DX, AL
    
    ; 启用接收数据中断
    MOV DX, IER
    MOV AL, 01H         ; 仅启用接收数据中断
    OUT DX, AL
  }
     
}

2.Intel8259设置

cpp 复制代码
//////////////////////////////////////////////////////////////////////////////////
//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)d
    outp(PIC1_DATA, 0x0f);  // 关键修改:设置自动EOI模式
    
    // OCW1: 只允许IR0中断 (11111110b)
   // outp(PIC1_DATA, 0xfe);
    outp(PIC1_DATA, 0xfd);  // OCW1: 只允许IR0,IR1中断 (111111100b)
}

3.完整汇编源程序

cpp 复制代码
; =============================================
; PC16550 UART接收中断处理示例
; 功能: 接收字符并回显,显示中断触发与释放状态
; 硬件配置:
;   - UART基地址: 100H
;   - 8259 PIC端口: 400H(命令), 402H(数据)
;   - 中断请求线: IRQ1 (8259 IR1)
;   - 中断向量号: 21H
;   - 时钟频率: 18.432MHz
;   - 波特率: 9600 bps (除数78H)
; =============================================
 
ORG 100H
JMP INITIALIZATION      ; 跳过数据区到初始化代码
 
; 数据段定义
int_triggered DB 0      ; 中断触发标志
int_released DB 0       ; 中断释放标志
recv_char DB 'A'          ; 接收的字符
 
; 状态消息
trigger_msg DB 0Dh, 0Ah, '>>> Interrupt TRIGGERED', 0
release_msg DB 0Dh, 0Ah, '<<< Interrupt RELEASED (EOI sent)', 0
recv_msg    DB 0Dh, 0Ah, 'Received char: ', 0
start_msg   DB 0Dh, 0Ah, 'UART Interrupt Demo Started', 0Dh, 0Ah
            DB 'Send characters to test interrupt...', 0Dh, 0Ah, 0
end_msg     DB 0Dh, 0Ah, 'Program terminated.', 0Dh, 0Ah, 0
 
; 16550寄存器偏移
UART_BASE EQU 100H
RBR_THR EQU UART_BASE + 0  ; 接收缓冲/发送保持寄存器
IER     EQU UART_BASE + 1  ; 中断使能寄存器
IIR_FCR EQU UART_BASE + 2  ; 中断标识/FIFO控制寄存器
LCR     EQU UART_BASE + 3  ; 线路控制寄存器
MCR     EQU UART_BASE + 4  ; 调制解调器控制寄存器
LSR     EQU UART_BASE + 5  ; 线路状态寄存器
DLL     EQU UART_BASE + 0  ; 除数锁存低字节 (DLAB=1)
DLM     EQU UART_BASE + 1  ; 除数锁存高字节 (DLAB=1)
 
; 8259 PIC端口
PIC_CMD  EQU 400H
PIC_DATA EQU 402H
 
; 中断向量号
UART_IRQ EQU 21H        ; IRQ1对应中断21H
 
; =============================================
; UART初始化子程序
; =============================================
INIT_UART:
    ; 设置波特率除数 (9600 @ 18.432MHz)
    MOV DX, LCR
    MOV AL, 80H         ; 设置DLAB=1
    OUT DX, AL
    
    MOV DX, DLL         ; 除数锁存低字节
    MOV AL, 78H         ; 120 = 78H (18.432MHz / (16 * 9600))
    OUT DX, AL
    
    MOV DX, DLM         ; 除数锁存高字节
    MOV AL, 00H
    OUT DX, AL
    
    ; 设置线路参数: 8位数据, 1停止位, 无校验
    MOV DX, LCR
    MOV AL, 03H         ; 8N1, DLAB=0
    OUT DX, AL
    
    ; 启用并复位FIFO
    MOV DX, IIR_FCR
    MOV AL, 0C7H        ; 启用FIFO, 14字节触发点, 清除接收FIFO
    OUT DX, AL
    
    ; 设置调制解调器控制
    MOV DX, MCR
    MOV AL, 0BH         ; 启用OUT2(中断使能), RTS和DTR
    OUT DX, AL
    
    ; 启用接收数据中断
    MOV DX, IER
    MOV AL, 01H         ; 仅启用接收数据中断
    OUT DX, AL
    
    RET
 
; =============================================
; 8259 PIC初始化
; =============================================
INIT_PIC:
    ; 初始化8259
    MOV DX, PIC_CMD
    MOV AL, 17H         ; ICW1: 边沿触发, 级联, 需要ICW4
    OUT DX, AL
    
    MOV DX, PIC_DATA
    MOV AL, UART_IRQ-1    ; ICW2: 中断向量基值
    OUT DX, AL
    
    MOV AL, 01H         ; ICW4: 8086模式, 非缓冲, 正常EOI
    OUT DX, AL
    
    ; 允许IRQ1中断
    IN AL, DX
    AND AL, 0FDH        ; 清除IRQ1屏蔽位(11111101)
    OUT DX, AL
    
    RET
 
; =============================================
; 设置中断向量
; =============================================
SET_INTERRUPT_VECTOR:
    CLI                 ; 关中断
    XOR AX, AX
    MOV ES, AX          ; ES = 0 (中断向量表段地址)
    
    ; 计算中断向量位置 (中断号 * 4)
    MOV AX, UART_IRQ
    SHL AX, 2           ; 乘以4
    
    ; 设置中断向量
    MOV DI, AX
    MOV AX, OFFSET UART_ISR
    CLD
    STOSW               ; 存储偏移地址
    MOV AX, CS
    STOSW               ; 存储段地址
    STI                 ; 开中断
    RET
 
; =============================================
; UART中断服务程序 (IRQ1)
; 演示中断触发和释放过程
; =============================================
UART_ISR PROC FAR
    ; 保存寄存器
    PUSH AX
    PUSH DX
    PUSH DS
    
    ; 设置DS为当前数据段
    MOV AX, CS
    MOV DS, AX
    
    ; 设置中断触发标志
    MOV [int_triggered], 1
    
    ; 发送中断触发消息
    MOV SI, OFFSET trigger_msg
    CALL SEND_STRING
    
;ISR_LOOP:
;    ; 检查中断源
;    MOV DX, IIR_FCR
;    IN AL, DX
;    TEST AL, 01H        ; 检查是否有待处理中断 (bit0=1表示无中断)
;    JNZ ISR_EXIT        ; 无中断则退出
;    
;    ; 检查是否为接收数据中断
;    TEST AL, 04H        ; 检查中断类型位 (bit1-2)
;    JNZ CHECK_OTHER     ; 不是接收中断则检查其他
;    
;    ; 处理接收数据中断
;    MOV DX, RBR_THR
;    IN AL, DX           ; 读取接收到的字符
;    MOV [recv_char], AL ; 保存字符
;    
;    ; 发送接收消息
;    MOV SI, OFFSET recv_msg
;    CALL SEND_STRING
;    MOV AL, [recv_char]
;    CALL SEND_CHAR
;    
;    ; 继续检查其他中断
;    JMP ISR_LOOP
;    
;CHECK_OTHER:
;    ; 可以添加其他中断类型的处理
;    ; ...
;    
ISR_EXIT:
    ; 发送中断释放消息
    MOV SI, OFFSET release_msg
    CALL SEND_STRING
    
   ; ; 发送EOI到8259
;    MOV AL, 20H
;    MOV DX, PIC_CMD
;    OUT DX, AL
;    
;    ; 设置中断释放标志
;    MOV [int_released], 1
    
    
    CALL CLEAR_ALL_INTERRUPTS
    
    ; 恢复寄存器
    POP DS
    POP DX
    POP AX
    IRET
UART_ISR ENDP
 
; =============================================
; 串口发送字符子程序
; 输入: AL = 要发送的字符
; =============================================
SEND_CHAR:
    PUSH AX
    PUSH DX
    
    ; 保存字符
    MOV AH, AL
    
SEND_WAIT:
    ; 检查发送保持寄存器是否为空
    MOV DX, LSR
    IN AL, DX
    TEST AL, 20H        ; 检查THRE位(bit5)
    JZ SEND_WAIT        ; 不为空则等待
    
    ; 发送字符
    MOV DX, RBR_THR
    MOV AL, AH
    OUT DX, AL
    
    POP DX
    POP AX
    RET
 
; =============================================
; 串口发送字符串
; 输入: SI = 字符串偏移地址
; =============================================
SEND_STRING:
    PUSH AX
    PUSH SI
    
SEND_STR_LOOP:
    LODSB               ; 加载字符到AL
    OR AL, AL           ; 检查是否结束(0)
    JZ SEND_STR_DONE    ; 是则结束
    
    CALL SEND_CHAR      ; 发送字符
    JMP SEND_STR_LOOP   ; 继续发送
    
SEND_STR_DONE:
    POP SI
    POP AX
    RET
 
 
; =============================================
; 强制清除所有中断状态
; =============================================
CLEAR_ALL_INTERRUPTS:
    ; 读取所有状态寄存器以清除中断
 ;   MOV DX, LSR
;    IN AL, DX           ; 清除线路状态中断
    
   MOV DX, MCR
   IN AL, DX           ; 清除调制解调器状态中断         
   
   READ_AGAIN:
    
        MOV DX, RBR_THR
        IN AL, DX           ; 清除接收数据中断      
        MOV [recv_char], AL ; 保存字符     
        CALL SEND_CHAR                               
        
        
        MOV DX, LSR
        IN AL, DX           ; 清除线路状态中断
        TEST AL,01H       ;   1---fifo  不空   0---FIFO  空
        
        JNZ  READ_AGAIN      ;不空,继续读取
;    
;    MOV DX, IIR_FCR
;    IN AL, DX           ; 清除中断标识
;    
    ; 发送EOI到8259
    MOV AL, 20H
    MOV DX, PIC_CMD
    OUT DX, AL
    
    RET
 
 
; =============================================
; 主初始化程序
; =============================================
INITIALIZATION:
    ; 初始化标志               
     CLI
     MOV  AX,01F0H       ;老版BIOS重新设置CS寄存器地址为01F0H         
     MOV DS,AX
     MOV SS,AX    
     MOV ES,AX
     MOV AX,5FFFH
     MOV SP,AX              
     
     ;MOV  AX,0000H       ;CS 老版BIOS重新设置CS寄存器地址为01F0H     
     ;PUSH AX
     ;MOV AX,OFFSET START123         ;IP
     ;PUSH AX
   ;  IRET
     
     
     ;MOV  SS,AX
     ;MOV  DS,AX
     ;MOV AX, 01F0H
START123:
     
     NOP
     NOP
     
    MOV [int_triggered], 0
    MOV [int_released], 0
    
    ; 初始化UART
    CALL INIT_UART
    
    ; 初始化8259 PIC
    CALL INIT_PIC
    
    ; 设置中断向量
    CALL SET_INTERRUPT_VECTOR
    
    ; 通过串口发送启动消息
    MOV SI, OFFSET start_msg
    CALL SEND_STRING        
    
    STI
 
; =============================================
; 主程序循环
; =============================================
MAIN_LOOP:
    ; 可以在这里添加其他任务
    ; 例如: 检查中断标志状态等
    
    ; 简单的延时循环
    MOV CX, 0FFFFH
DELAY_LOOP:
    LOOP DELAY_LOOP
    
    ; 检查退出条件(可选)
    ; ...   
    
    ;CALL CLEAR_ALL_INTERRUPTS      
  ;  MOV AL, [recv_char]
;    CALL SEND_CHAR
    
    JMP MAIN_LOOP        
    
    
    
    DB  55H,55H,55H,55H,55H
 
; =============================================
; 退出程序(可选)
; =============================================
; 此示例程序将无限运行
; 在实际应用中可添加退出逻辑
 
 
 

4.完整C程序

cpp 复制代码
#include "tiny_stdarg.h"  // 使用自定义可变参数实现

#define ADR_273 0x0200
#define ADR_244 0x0400
#define  LED_PORT   0x800
#define  PC16550_THR   0x1f0
#define  PC16550_LSR   0x1f5

/* ; 16550寄存器偏移
UART_BASE EQU 100H
RBR_THR EQU UART_BASE + 0  ; 接收缓冲/发送保持寄存器
IER     EQU UART_BASE + 1  ; 中断使能寄存器
IIR_FCR EQU UART_BASE + 2  ; 中断标识/FIFO控制寄存器
LCR     EQU UART_BASE + 3  ; 线路控制寄存器
MCR     EQU UART_BASE + 4  ; 调制解调器控制寄存器
LSR     EQU UART_BASE + 5  ; 线路状态寄存器
DLL     EQU UART_BASE + 0  ; 除数锁存低字节 (DLAB=1)
DLM     EQU UART_BASE + 1  ; 除数锁存高字节 (DLAB=1) */

#define  UART_BASE     0x100
#define  RBR_THR     UART_BASE+0
#define  IER         UART_BASE+1
#define  IIR_FCR     UART_BASE+2
#define  LCR         UART_BASE+3
#define  MCR         UART_BASE+4
#define  LSR         UART_BASE+5
#define  DLL         UART_BASE+0
#define  DLM         UART_BASE+1
//#define  RBR_THR     UART_BASE+0

void   uart_pc16550_init(void)
{
  __asm 
  {
      ; 设置波特率除数 (9600 @ 18.432MHz)
    MOV DX, LCR
    MOV AL, 80H         ; 设置DLAB=1
    OUT DX, AL
    
    MOV DX, DLL         ; 除数锁存低字节
    MOV AL, 78H         ; 120 = 78H (18.432MHz / (16 * 9600))
    OUT DX, AL
    
    MOV DX, DLM         ; 除数锁存高字节
    MOV AL, 00H
    OUT DX, AL
    
    ; 设置线路参数: 8位数据, 1停止位, 无校验
    MOV DX, LCR
    MOV AL, 03H         ; 8N1, DLAB=0
    OUT DX, AL
    
    ; 启用并复位FIFO
    MOV DX, IIR_FCR
    MOV AL, 0C7H        ; 启用FIFO, 14字节触发点, 清除接收FIFO
    OUT DX, AL
    
    ; 设置调制解调器控制
    MOV DX, MCR
    MOV AL, 0BH         ; 启用OUT2(中断使能), RTS和DTR
    OUT DX, AL
    
    ; 启用接收数据中断
    MOV DX, IER
    MOV AL, 01H         ; 仅启用接收数据中断
    OUT DX, AL
  }
     
}



/////////////////////////////////////////////////////////////////////////////////
//基本的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 _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;
char buffer[80];
void  uart_fifo_read(void);
void display_intr()
{
    unsigned char digits[] = {1, 2, 3, 4, 5, 6, 7, 8}; // 要显示的数字
    static int ip_disp=0;
    tiny_sprintf(buffer,"%%%%%%%%%%%%%%%%%%\r\n");
    uart_str_send(buffer);
    // 设置位选 (选中当前位)
            outp(PORT_8255_B, ~(1 << ip_disp));
            
            // 设置段码
            outp(PORT_8255_A, ~seg_codes[digits[ip_disp]]);
            //outp(PORT_8255_A, 0x06);
            
            // 延时保持显示
            delay(1);
            uart_fifo_read();
            // 关闭当前位显示 (消除鬼影)
            //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 20     // 目标输出频率(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)d
    outp(PIC1_DATA, 0x0f);  // 关键修改:设置自动EOI模式
    
    // OCW1: 只允许IR0中断 (11111110b)
   // outp(PIC1_DATA, 0xfe);
    outp(PIC1_DATA, 0xfd);  // OCW1: 只允许IR0,IR1中断 (111111100b)
}

//////////////////////////////////////////////////////////////////////////////////


//char  end_flag[5]={0x55,0x55,0x55,0x55,0x55};
extern void nmi_handler(void);
//////////////////////////////////////////////////////////////////////
//timer clock
/////////////////////////////////////////////////////////////////////
//声明时钟结构体类型
typedef struct 
{
   int hour;
   int min;
   int sec;  
    /* data */
}TIMER_CLOCK;
TIMER_CLOCK  *t_clock;//定义时钟结构体变量
void t_clock_init(TIMER_CLOCK *p)//时钟变量初始化
{
     p->hour=12;
     p->min=59;
     p->sec=00;
}
void t_clock_uart_out(TIMER_CLOCK *p)
{
    char buffer[80];
    tiny_sprintf(buffer,"******************************************\r\n");
    uart_str_send(buffer);
     tiny_sprintf(buffer,"Clock Hour= %d     \r\n",p->hour);
     uart_str_send(buffer);
     tiny_sprintf(buffer,"Clock min= %d     \r\n",p->min);
     uart_str_send(buffer);
     tiny_sprintf(buffer,"Clock sec= %d     \r\n",p->sec);
     uart_str_send(buffer);
    tiny_sprintf(buffer,"******************************************\r\n");
    uart_str_send(buffer);
}
unsigned char r_data=0;
void  uart_fifo_read(void)
{
     __asm
     {
        
        READ_AGAIN:
    
        MOV DX, RBR_THR
        IN AL, DX           ; 清除接收数据中断      
        MOV r_data, AL ; 保存字符     
        ;CALL SEND_CHAR                               
        
        
        MOV DX, LSR
        IN AL, DX           ; 清除线路状态中断
        TEST AL,01H       ;   1---fifo  不空   0---FIFO  空
        
        JNZ  READ_AGAIN      ;不空,继续读取
     }
}

/////////////////////////////////////////////////////////////////////
/// @param  
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(0x08, (void *)&nmi_handler);//8259   与NMI共用一个中断服务函数
    set_int(0x09, (void *)&nmi_handler);//8259   与NMI共用一个中断服务函数
   
    init_8255();
    init_8253();
    uart_pc16550_init();
  
    asm  cli
    init_8259();
    asm nop
    asm  sti 
    
    t_clock_init(t_clock);


   while (1)
   {
     //char button_state;
     //button_state=inp(ADR_244);
     //int i=0;
     //uart_str_send(str);
     t_clock_uart_out(t_clock);

     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;
     tiny_sprintf(buffer,"Key_code= 0X%x     \r\n",key_code);
     uart_str_send(buffer);

     tiny_sprintf(buffer,"uart_get_data=%c     \r\n",r_data);
     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);

     //display_numbers();
   }
 }
char  end_flag[5]={0x55,0x55,0x55,0x55,0x55};

5.测试结果

相关推荐
掘根3 小时前
【Qt】布局管理器
开发语言·qt
半夏知半秋3 小时前
skynet-socket.lua源码分析
服务器·开发语言·学习·架构·lua
西猫雷婶4 小时前
random.shuffle()函数随机打乱数据
开发语言·pytorch·python·学习·算法·线性回归·numpy
来生硬件工程师4 小时前
CH582 GPIO
c语言·开发语言·单片机
fly-phantomWing5 小时前
在命令提示符页面中用pip命令行安装Python第三方库的详细步骤
开发语言·python·pip
东亚_劲夫5 小时前
汇编和反汇编
汇编
VBA63375 小时前
VBA数据库解决方案第二十三讲:向一个已有数据表中添加数据记录
开发语言
【ql君】qlexcel5 小时前
C语言形式参数和实际参数的区别(附带示例)
c语言·函数·形式参数·实际参数
杜子不疼.5 小时前
【C++】玩转模板:进阶之路
java·开发语言·c++