Created
December 21, 2024 18:48
-
-
Save tschak909/8a60d1654a62c0df43c23ad81aada8e6 to your computer and use it in GitHub Desktop.
first bios experiment.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* | |
| * Listing 3 - COM.C - "RS-232 Interrupts The C Way" | |
| * | |
| * Copyright (C) Mark R. Nelson 1990 | |
| * | |
| * This file contains all the code to implement a complete | |
| * interrupt driven interface to one of the RS-232 COM | |
| * ports on an IBM compatible PC. | |
| */ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <dos.h> | |
| #include <conio.h> | |
| #include "com.h" | |
| /* | |
| * This group of defines creates all the definitions used | |
| * to access registers and bit fields in the 8250 UART. | |
| * While the names may not be the best, they are the ones | |
| * used in the 8250 data sheets, so they are generally not | |
| * changed. Since the definitions are only used in COM.C, | |
| * they are not included in the COM.H header file where | |
| * they might normally be expected. | |
| */ | |
| #define RBR 0 /* Receive buffer register */ | |
| #define THR 0 /* Transmit holding reg. */ | |
| #define IER 1 /* Interrupt Enable reg. */ | |
| #define IER_RX_DATA 1 /* Enable RX interrupt bit */ | |
| #define IER_THRE 2 /* Enable TX interrupt bit */ | |
| #define IIR 2 /* Interrupt ID register */ | |
| #define IIR_MODEM_STATUS 0 /* Modem stat. interrupt ID*/ | |
| #define IIR_TRANSMIT 2 /* Transmit interrupt ID */ | |
| #define IIR_RECEIVE 4 /* Receive interrupt ID */ | |
| #define IIR_LINE_STATUS 6 /* Line stat. interrupt ID */ | |
| #define LCR 3 /* Line control register */ | |
| #define LCR_DLAB 0x80 /* Divisor access bit */ | |
| #define LCR_EVEN_PARITY 0x8 /* Set parity 'E' bits */ | |
| #define LCR_ODD_PARITY 0x18 /* Set parity 'O' bits */ | |
| #define LCR_NO_PARITY 0 /* Set parity 'N' bits */ | |
| #define LCR_1_STOP_BIT 0 /* Bits to set 1 stop bit */ | |
| #define LCR_2_STOP_BITS 4 /* Bits to set 2 stop bits */ | |
| #define LCR_5_DATA_BITS 0 /* Bits to set 5 data bits */ | |
| #define LCR_6_DATA_BITS 1 /* Bits to set 6 data bits */ | |
| #define LCR_7_DATA_BITS 2 /* Bits to set 7 data bits */ | |
| #define LCR_8_DATA_BITS 3 /* Bits to set 8 data bits */ | |
| #define MCR 4 /* Modem control register */ | |
| #define MCR_DTR 1 /* Bit to turn on DTR */ | |
| #define MCR_RTS 2 /* Bit to turn on RTS */ | |
| #define MCR_OUT1 4 /* Bit to turn on OUT1 */ | |
| #define MCR_OUT2 8 /* Bit to turn on OUT2 */ | |
| #define LSR 5 /* Line Status register */ | |
| #define MSR 6 /* Modem Status register */ | |
| #define DLL 0 /* Divisor latch LSB */ | |
| #define DLM 1 /* Divisor latch MSB */ | |
| /* | |
| * Various constants used only in this program. | |
| */ | |
| #define INT_CONTROLLER 0x20 /* The address of the 8259*/ | |
| #define EOI 0x20 /* The end of int command */ | |
| #define BREAK_VECTOR 0x23 /* The CTRL-BREAK vector */ | |
| /* | |
| * These are two static variables used in COM.C. com is | |
| * the pointer to the port that will be serviced by the | |
| * ISR. The old break handler points to the CTRL-BREAK | |
| * handler that was in place before the port was opened. | |
| * It will be restored when the port is closed. | |
| */ | |
| static PORT *com = NULL; | |
| static void ( interrupt far * old_break_handler )(); | |
| /* | |
| * This routine intercepts the CTRL-BREAK vector. This | |
| * prevents the program from terminating before having a | |
| * chance to turn off COM interrupts. This handler does | |
| * nothing, but it could be used to set a flag indicating | |
| * it is time to abort. | |
| */ | |
| void interrupt far break_handler() | |
| { | |
| } | |
| /* | |
| * This is the interrupt service routine for the COM port. | |
| * It sits in a loop reading the interrrupt ID register, then | |
| * servicing one of the four different types of interrupts. | |
| * Note that we shouldn't even get Modem Status and Line | |
| * interrupts in this implementation, but they are left | |
| * in for later enhancements. | |
| */ | |
| static void interrupt far interrupt_service_routine() | |
| { | |
| unsigned char c; | |
| enable(); | |
| for ( ; ; ) { | |
| switch ( inportb( com->uart_base + IIR ) ) { | |
| /* | |
| * If the present interrupt is due to a modem status line | |
| * change, the MSR is read to clear the interrupt, but | |
| * nothing else is done. | |
| */ | |
| case IIR_MODEM_STATUS : | |
| inportb( com->uart_base + MSR ); | |
| break; | |
| /* | |
| * If the interrupt is due to the transmit holding register | |
| * being ready for another character, I first check to see | |
| * if any characters are left in the output buffer. If | |
| * not, Transmit interrupts are disabled. Otherwise, the | |
| * next character is extracted from the buffer and written | |
| * to the UART. | |
| */ | |
| case IIR_TRANSMIT : | |
| if ( com->out.read_index == com->out.write_index ) | |
| outportb( com->uart_base + IER, IER_RX_DATA ); | |
| else { | |
| c = com->out.buffer[ com->out.read_index++ ]; | |
| outportb( com->uart_base + THR, c ); | |
| } | |
| break; | |
| /* | |
| * When a new character comes in and generates an | |
| * interrupt, it is read in. If there is room in the input | |
| * buffer, it is stored, otherwise it is discarded. | |
| */ | |
| case IIR_RECEIVE : | |
| c = (unsigned char) inportb( com->uart_base+RBR ); | |
| if ((com->in.write_index+1 ) != com->in.read_index) | |
| com->in.buffer[ com->in.write_index++ ] = c ; | |
| break; | |
| /* | |
| * All this code does is read the Line Status register, to | |
| * clear the source of the interrupt. | |
| */ | |
| case IIR_LINE_STATUS : | |
| inportb( com->uart_base + LSR ); | |
| break; | |
| /* | |
| * If there are no valid interrupts left to service, an EOI | |
| * is written to the 8259 interrupt controller, and the | |
| * routine exits. | |
| */ | |
| default : | |
| outportb( INT_CONTROLLER, EOI ); | |
| return; | |
| } | |
| } | |
| } | |
| /* | |
| * This routine opens an RS-232 port up. This means it | |
| * allocates space for a PORT strcture, initializes the | |
| * input and output buffers, stores the uart address and | |
| * the interrupt number. It then gets and stored the | |
| * interrupt vector presently set up for the UART, then | |
| * installs its own. It also sets up a handler to | |
| * intercept the CTRL-BREAK handler. Finally, it tells the | |
| * 8259 interrupt controller to begin accepting interrupts | |
| * on the IRQ line used by this COM port. | |
| */ | |
| PORT *port_open( int address, int int_number ) | |
| { | |
| unsigned char temp; | |
| PORT *port; | |
| if ((port = malloc( sizeof( PORT ))) == NULL) | |
| return( NULL ); | |
| com = port; | |
| port->in.write_index = port->in.read_index = 0; | |
| port->out.write_index = port->out.read_index = 0; | |
| port->uart_base = address; | |
| port->irq_mask = (char) 1 << (int_number % 8 ); | |
| port->interrupt_number = int_number; | |
| port->old_vector = getvect( int_number ); | |
| setvect( int_number, interrupt_service_routine ); | |
| old_break_handler = getvect( BREAK_VECTOR ); | |
| setvect( BREAK_VECTOR, break_handler ); | |
| temp = (char) inportb( INT_CONTROLLER + 1 ); | |
| outportb( INT_CONTROLLER + 1, ~port->irq_mask & temp ); | |
| return( port ); | |
| } | |
| /* | |
| * This routine establishes the operating parameters for a | |
| * port after it has been opened. This means it sets the | |
| * baud rate, parity, number of data bits, and number of | |
| * stop bits. Interrupts are disabled before the routine | |
| * starts changing registers, and are then reenabled after | |
| * the changes are complete. | |
| */ | |
| void port_set( PORT *port, | |
| long speed, | |
| char parity, | |
| int data, | |
| int stopbits ) | |
| { | |
| unsigned char lcr_out; | |
| unsigned char mcr_out; | |
| unsigned char low_divisor; | |
| unsigned char high_divisor; | |
| /* | |
| * First disable all interrupts from the port. I also read | |
| * RBR just in case their is a char sitting there ready to | |
| * generate an interupt. | |
| */ | |
| outportb( port->uart_base + IER, 0 ); | |
| inportb( port->uart_base ); | |
| /* | |
| * Writing the baud rate means first enabling the divisor | |
| * latch registers, then writing the 16 bit divisor int | |
| * two steps, then disabling the divisor latch so the other | |
| * registers can be accessed normally. | |
| */ | |
| low_divisor = (char) (115200L / speed ) & 0xff; | |
| high_divisor = (char) ((115200L / speed ) >> 8); | |
| outportb( port->uart_base + LCR, LCR_DLAB ); | |
| outportb( port->uart_base + DLL, low_divisor ); | |
| outportb( port->uart_base + DLM, high_divisor ); | |
| outportb( port->uart_base + LCR, 0 ); | |
| /* | |
| * Setting up the line control register establishes the | |
| * parity, number of bits, and number of stop bits. | |
| */ | |
| if ( parity== 'E' ) | |
| lcr_out = LCR_EVEN_PARITY; | |
| else if ( parity == 'O' ) | |
| lcr_out = LCR_ODD_PARITY; | |
| else | |
| lcr_out = LCR_NO_PARITY; | |
| if ( stopbits == 2 ) | |
| lcr_out |= LCR_2_STOP_BITS; | |
| if ( data == 6 ) | |
| lcr_out |= LCR_6_DATA_BITS; | |
| else if ( data == 7 ) | |
| lcr_out |= LCR_7_DATA_BITS; | |
| else if ( data == 8 ) | |
| lcr_out |= LCR_8_DATA_BITS; | |
| outportb( port->uart_base + LCR, lcr_out ); | |
| /* | |
| * I turn on RTS and DTR, as well as OUT2. OUT2 is needed | |
| * to allow interrupts on PC compatible cards. | |
| */ | |
| mcr_out = MCR_RTS | MCR_DTR | MCR_OUT2 ; | |
| outportb( port->uart_base + MCR, mcr_out ); | |
| /* | |
| * Finally, restart receiver interrupts, and exit. | |
| */ | |
| outportb( port->uart_base + IER, IER_RX_DATA ); | |
| } | |
| /* | |
| * In order to close the port, I first disable interrupts | |
| * at the UART, then disable interrupts from the UART's IRQ | |
| * line at the 8259 interrupt controller. DTR, RTS, and | |
| * OUT2 are all dropped. The UART's previous interrupt | |
| * handler is restored, and the old break handler | |
| * is restored. Finally, the port data structure is freed, | |
| * and things should be completely back to normal. | |
| */ | |
| void port_close( PORT *port ) | |
| { | |
| unsigned char temp; | |
| outportb( port->uart_base + IER, 0 ); | |
| temp = (unsigned char) inportb( INT_CONTROLLER + 1 ); | |
| outportb( INT_CONTROLLER + 1, port->irq_mask | temp ); | |
| setvect( port->interrupt_number, port->old_vector ); | |
| setvect( BREAK_VECTOR, old_break_handler ); | |
| outportb( port->uart_base + MCR, 0 ); | |
| free( port ); | |
| } | |
| /* | |
| * This routine is used to send a single character out to | |
| * the UART. If there is room in the output buffer, the | |
| * character is inserted in the buffer. Then the routine | |
| * checks to see if Transmit interrupts are presently | |
| * enabled for this UART. If they aren't, they are turned | |
| * on so the ISR will see this new character. | |
| */ | |
| int port_putc( unsigned char c, PORT *port ) | |
| { | |
| if (( port->out.write_index+1 ) == port->out.read_index) | |
| return( -1 ); | |
| port->out.buffer[ port->out.write_index ] = c; | |
| port->out.write_index += 1; | |
| if (( inportb( port->uart_base + IER ) & IER_THRE) == 0 ) | |
| outportb( port->uart_base+IER,IER_THRE | IER_RX_DATA); | |
| return( c ); | |
| } | |
| int port_available(PORT *port) | |
| { | |
| return port->in.write_index - port->in.read_index; | |
| } | |
| /* | |
| * This routine checks to see if there is a character | |
| * available in the input buffer for the specified port. | |
| * If there is, it is pulled out and returned to the | |
| * caller. | |
| */ | |
| int port_getc(PORT *port) | |
| { | |
| if ( port->in.write_index == port->in.read_index ) | |
| return( -1 ); | |
| else | |
| return( port->in.buffer[ port->in.read_index++ ] ); | |
| } | |
| /** | |
| * @brief Get next character, wait if not available. | |
| * @param PORT pointer to port structure. | |
| * @return Character, or -1 if none waiting. | |
| */ | |
| int port_getc_sync(PORT *port) | |
| { | |
| int i; | |
| while (!port_available(port)); | |
| return port_getc(port); | |
| } | |
| /** | |
| * @brief set DTR to desired state | |
| * @param port Pointer to initialized port. | |
| * @param t 0 = off, 1 = on | |
| */ | |
| void port_set_dtr(PORT *port, unsigned char t) | |
| { | |
| if (t) | |
| outportb(port->uart_base+MCR, | |
| inportb(port->uart_base+MCR) | 0x01); | |
| else | |
| outportb(port->uart_base+MCR, | |
| inportb(port->uart_base+MCR) & 0xFE); | |
| } | |
| /** | |
| * @brief Send buffer of given len to serial port | |
| * @param port Pointer to initialized serial port | |
| * @param buf Pointer to buffer | |
| * @param len number of bytes to send, must be len or less. | |
| */ | |
| void port_put(PORT *port, unsigned char *buf, unsigned short len) | |
| { | |
| int i=0; | |
| while (len) | |
| { | |
| i = port_putc(*buf,port); | |
| if (i != -1) | |
| { | |
| len--; | |
| buf++; | |
| } | |
| delay(1); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * @brief Experimental FujiNet BIOS 1 | |
| * @author Thomas Cherryhomes | |
| * @email thom dot cherryhomes at gmail dot com | |
| * @license gpl v. 3, see LICENSE for details. | |
| */ | |
| #include <dos.h> | |
| #include <fujicom.h> | |
| #include <stdio.h> | |
| cmdFrame_t cmd; | |
| void get_adapter_config(unsigned es, unsigned bx) | |
| { | |
| cmd.ddev = 0x70; | |
| cmd.dcomnd=0xE8; | |
| fujicom_init(2); | |
| _AX = fujicom_command_read(&cmd,(unsigned char *)MK_FP(es,bx),140); | |
| fujicom_done(); | |
| } | |
| void interrupt intf5(unsigned bp, unsigned di, unsigned si, | |
| unsigned ds, unsigned es, unsigned dx, | |
| unsigned cx, unsigned bx, unsigned ax) | |
| { | |
| switch(ax >> 8) | |
| { | |
| case 0xE8: /* GET ADAPTER CONFIG */ | |
| get_adapter_config(es,bx); | |
| break; | |
| } | |
| } | |
| void main(void) | |
| { | |
| disable(); | |
| setvect(0xF5,intf5); | |
| enable(); | |
| keep(0,512); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * #FUJINET Low Level Routines | |
| */ | |
| #include "com.h" | |
| #include "fujicom.h" | |
| PORT *port; | |
| void fujicom_init(unsigned char p) | |
| { | |
| port = port_open(0x2f8,11); | |
| port_set(port,9600,'N',8,1); | |
| } | |
| unsigned char fujicom_cksum(unsigned char *buf, unsigned short len) | |
| { | |
| unsigned int chk = 0; | |
| int i=0; | |
| for (i=0;i<len;i++) | |
| chk = ((chk+buf[i]) >> 8) + ((chk + buf[i]) & 0xFF); | |
| return (unsigned char)chk; | |
| } | |
| /** | |
| * @brief Internal function, send command, get response. | |
| * | |
| * @param c ptr to command frame to send | |
| * @return 'A'ck, or 'N'ak. | |
| */ | |
| char _fujicom_send_command(cmdFrame_t *c) | |
| { | |
| int i=-1; | |
| unsigned char *cc = (unsigned char *)c; | |
| /* Calculate checksum and place in frame */ | |
| c->dcksum = fujicom_cksum(cc,4); | |
| /* Assert DTR to indicate start of command frame */ | |
| port_set_dtr(port,1); | |
| /* Write command frame */ | |
| port_put(port,cc,sizeof(cmdFrame_t)); | |
| /* Desert DTR to indicate end of command frame */ | |
| port_set_dtr(port,0); | |
| i=port_getc_sync(port); | |
| return (unsigned char)i; | |
| } | |
| char fujicom_command(cmdFrame_t *c) | |
| { | |
| _fujicom_send_command(c); | |
| return port_getc_sync(port); | |
| } | |
| char fujicom_command_read(cmdFrame_t *c, unsigned char *buf, unsigned short len) | |
| { | |
| int r; /* response */ | |
| int i; | |
| r = _fujicom_send_command(c); | |
| if (r == 'N') | |
| return r; /* Return NAK */ | |
| /* Get COMPLETE/ERROR */ | |
| r = port_getc_sync(port); | |
| if (r == 'C') | |
| { | |
| /* Complete, get payload */ | |
| for (i=0;i<len;i++) | |
| { | |
| buf[i]=port_getc_sync(port); | |
| } | |
| /* Get Checksum byte, we don't use it. */ | |
| port_getc_sync(port); | |
| } | |
| /* Otherwise, we got an error, return it. */ | |
| return (unsigned char)r; | |
| } | |
| char fujicom_command_write(cmdFrame_t *c, unsigned char *buf, unsigned short len) | |
| { | |
| unsigned char r; /* response */ | |
| int i; | |
| unsigned char ck; | |
| r = _fujicom_send_command(c); | |
| if (r == 'N') | |
| return r; | |
| /* Write the payload */ | |
| port_put(port,buf,len); | |
| /* Write the checksum */ | |
| ck=fujicom_cksum(buf,len); | |
| port_put(port,&ck,1); | |
| /* Wait for ACK/NACK */ | |
| r = port_getc_sync(port); | |
| if (r == 'N') | |
| return r; | |
| /* Wait for COMPLETE/ERROR */ | |
| return port_getc_sync(port); | |
| } | |
| void fujicom_done(void) | |
| { | |
| port_close(port); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| MOV AH,E8 | |
| MOV BX,0120 | |
| INT F5 | |
| RET | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment