// HAL serial output endpoint. This file owns polling COM1 writes and subscribes serial to HAL text streams. #include #include #include #include #include #include #include #include #define HAL_COM1_BASE 0x03F8u #define HAL_COM1_DATA_PORT (HAL_COM1_BASE + 0u) #define HAL_COM1_STATUS_PORT (HAL_COM1_BASE + 5u) #define HAL_COM1_SCRATCH_PORT (HAL_COM1_BASE + 7u) #define HAL_COM1_TX_READY 0x20u #define HAL_SERIAL_TX_TIMEOUT_US 2000u #define HAL_SERIAL_TIMER_SPIN_CAP 65536u #define HAL_SERIAL_EARLY_SPINS 4096u static volatile int32_t gSerialWriteLock; static volatile int32_t gSerialPresent; // Probes the 16550 scratch register without relying on line-status values from an absent port. static bool HalSerialProbeCom1(void) { uint8_t original = __inbyte(HAL_COM1_SCRATCH_PORT); __outbyte(HAL_COM1_SCRATCH_PORT, 0x5Au); uint8_t first = __inbyte(HAL_COM1_SCRATCH_PORT); __outbyte(HAL_COM1_SCRATCH_PORT, 0xA5u); uint8_t second = __inbyte(HAL_COM1_SCRATCH_PORT); __outbyte(HAL_COM1_SCRATCH_PORT, original); return first == 0x5Au && second == 0xA5u; } // Waits for COM1 transmit readiness with a real microsecond deadline once the HAL timer is live. static bool HalSerialWaitForTransmit(void) { uint64_t start = HalTimerQueryTicks(); if (start != 0) { uint64_t deadline = start > UINT64_MAX - HAL_SERIAL_TX_TIMEOUT_US ? UINT64_MAX : start + HAL_SERIAL_TX_TIMEOUT_US; for (uint32_t spin = 0; spin < HAL_SERIAL_TIMER_SPIN_CAP; ++spin) { if ((__inbyte(HAL_COM1_STATUS_PORT) & HAL_COM1_TX_READY) != 0) return true; if (HalTimerQueryTicks() >= deadline) return false; _mm_pause(); } return false; } // Serial is registered before timer calibration, so keep the early path small and finite. for (uint32_t spin = 0; spin < HAL_SERIAL_EARLY_SPINS; ++spin) { if ((__inbyte(HAL_COM1_STATUS_PORT) & HAL_COM1_TX_READY) != 0) return true; _mm_pause(); } return false; } // Writes one byte to COM1 when the device remains responsive. static bool HalSerialWriteByte(uint8_t value) { if (!HalSerialWaitForTransmit()) return false; __outbyte(HAL_COM1_DATA_PORT, value); return true; } // Acquires the normal serial writer lock. static void HalSerialAcquireWriteLock(void) { while (NtlAtomicCompareExchange32(&gSerialWriteLock, 1, 0) != 0) _mm_pause(); } // Releases the normal serial writer lock. static void HalSerialReleaseWriteLock(void) { NtlAtomicExchange32(&gSerialWriteLock, 0); } // Writes one output record through polling COM1. static void HalSerialWriteRecord(void* context, const HAL_OUTPUT_RECORD* record) { (void)context; if (!record || !record->Buffer || NtlAtomicLoad32(&gSerialPresent) == 0) return; bool emergency = (record->Flags & HAL_OUTPUT_WRITE_EMERGENCY) != 0; uintptr_t interruptFlags = 0; if (!emergency) { interruptFlags = HalDisableInterrupts(); HalSerialAcquireWriteLock(); } bool responsive = true; for (size_t i = 0; i < record->Length && responsive; i++) { char c = record->Buffer[i]; if (c == '\n') responsive = HalSerialWriteByte('\r'); if (responsive) responsive = HalSerialWriteByte((uint8_t)c); } if (!responsive) NtlAtomicExchange32(&gSerialPresent, 0); if (!emergency) { HalSerialReleaseWriteLock(); HalRestoreInterrupts(interruptFlags); } } // Registers the polling serial endpoint only when a 16550-compatible COM1 device is present. void HalSerialInitialize(HAL_OUTPUT_ROUTER* output) { if (!output || !output->RegisterEndpoint) return; gSerialWriteLock = 0; gSerialPresent = 0; if (!HalSerialProbeCom1()) return; gSerialPresent = 1; HAL_OUTPUT_ENDPOINT endpoint; endpoint.Name = "serial0"; endpoint.StreamMask = HAL_OUTPUT_STREAM_ALL; endpoint.Capabilities = HAL_OUTPUT_ENDPOINT_PANIC_SAFE; endpoint.Write = HalSerialWriteRecord; endpoint.Context = 0; uint32_t endpointId = HAL_OUTPUT_ENDPOINT_INVALID; if (KSUCCESS(output->RegisterEndpoint(&endpoint, &endpointId))) output->SerialEndpointId = endpointId; else gSerialPresent = 0; }