/**
 * uart.h  —  Simple UART driver interface
 *
 * Provides blocking (polling) and interrupt-driven stubs.
 * Students implement the bodies in uart.c as part of the I/O activity.
 *
 * Computer Architecture: Design and Analysis (2026 edition)
 * Krerk Piromsopa, Ph.D. · Chulalongkorn University
 */

#ifndef UART_H
#define UART_H

#include <stdint.h>

/* ------------------------------------------------------------------ */
/*  Initialization                                                      */
/* ------------------------------------------------------------------ */

/**
 * uart_init() — Initialise UART0 for 38400-8N1 polling operation.
 *
 * Steps to implement (see Exercise 1):
 *  1. Set DLAB=1 in LCR, write divisor (baud rate divider), clear DLAB.
 *  2. Set LCR = UART_LCR_8N1.
 *  3. Enable and reset FIFOs via FCR.
 *  4. Keep IER = 0 (interrupts disabled) for the polling driver.
 */
void uart_init(void);

/**
 * uart_init_irq() — Re-initialise UART0 for interrupt-driven operation.
 *
 * Call after uart_init().  Steps to implement (see Exercise 2):
 *  1. Enable RX interrupt: set UART_IER_ERBFI in IER.
 *  2. Configure PLIC: set UART0 source priority, enable bit, threshold.
 *  3. Enable machine external interrupts (MIE_MEIE in mie, MSTATUS_MIE).
 */
void uart_init_irq(void);

/* ------------------------------------------------------------------ */
/*  Polling I/O                                                         */
/* ------------------------------------------------------------------ */

/**
 * uart_putc() — Transmit one character (blocking poll on THRE).
 *
 * Algorithm:
 *  while (LSR & UART_LSR_THRE == 0) { /* wait */ }
 *  THR = c;
 */
void uart_putc(char c);

/**
 * uart_getc() — Receive one character (blocking poll on DR).
 *
 * Algorithm:
 *  while (LSR & UART_LSR_DR == 0) { /* wait */ }
 *  return RBR;
 */
char uart_getc(void);

/**
 * uart_puts() — Transmit a NUL-terminated string.
 */
void uart_puts(const char *s);

/* ------------------------------------------------------------------ */
/*  Interrupt-driven I/O  (ring-buffer backed)                         */
/* ------------------------------------------------------------------ */

#define RX_BUF_SIZE  64   /* Must be a power of 2 */

/**
 * uart_rx_isr() — Called from trap_handler() when UART RX interrupt fires.
 *
 * Steps to implement (see Exercise 2):
 *  1. Claim the interrupt from PLIC.
 *  2. Read RXDATA from RBR (clears the DR flag).
 *  3. Enqueue into rx_buf ring buffer (if not full).
 *  4. Complete the interrupt (write source ID back to PLIC claim register).
 */
void uart_rx_isr(void);

/**
 * uart_getc_irq() — Dequeue one character from the RX ring buffer.
 *
 * Returns 0 if the buffer is empty (non-blocking).
 * To block until data arrives use:  while ((c = uart_getc_irq()) == 0);
 */
char uart_getc_irq(void);

#endif /* UART_H */
