diff --git a/CODE/DKM/ath9k/ath9k.c b/CODE/DKM/ath9k/ath9k.c new file mode 100644 index 0000000..05fd8e2 --- /dev/null +++ b/CODE/DKM/ath9k/ath9k.c @@ -0,0 +1,911 @@ +/* DKM ath9k Driver — Atheros AR928x 802.11abgn WiFi + * Stage 3, optional, depends on "pci" and "irq", provides "netdev". + * + * 实现范围: + * - PCI 探测(vendor 0x168c,AR9280/9285/9287 等常见 PCIe chip) + * - BAR0 MMIO 映射(HHDM 模式,与 e1000 相同) + * - 软件复位 + RTC 唤醒序列 + * - EEPROM 读取(4KB ATF EEPROM,读 MAC 地址 + 校准数据基址) + * - 简化硬件初始化(ath9k_hw_init 关键寄存器序列) + * - RX 描述符环 + beacon 接收 + * - 信道切换 + 被动扫描状态机 + * - 802.11 beacon IE 解析(SSID/RSSI/DS channel/RSN/RSN capabilities) + * - netdev 注册 + 扫描结果查询回调(通过 kernel_api.net.scan_*) + * + * 注意:QEMU 不支持 WiFi 网卡模拟,本驱动只能在真实硬件测试。 + * 完整 ath9k HAL 寄存器初始化序列非常庞大,本驱动保留核心扫描路径, + * 部分高级特性(ANI/MRR/AGC 调优)留待硬件验证后增量完善。 + */ + +#include + +#define DKM_DRIVER_MAGIC 0x444B4D31u +#define DKM_ABI_VERSION 1u + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef long long i64; +typedef signed char i8; +#define NULL ((void *)0) + +/* ---- DKM ABI ---- */ +struct dkm_log_api { + void (*info)(const char *msg); + void (*warn)(const char *msg); + void (*error)(const char *msg); + void (*panic)(const char *msg); +}; + +struct dkm_net_scan_result { + char ssid[33]; + u8 bssid[6]; + u8 channel; + i8 rssi; + u8 security; +}; + +struct dkm_net_device_desc { + const char *name; + u8 mac[6]; + u32 flags; + void *ctx; + int (*tx)(void *ctx, const void *packet, u32 length); + int (*rx_poll)(void *ctx, void *buffer, u32 capacity, u32 *out_length); + int (*scan_start)(void *ctx); + int (*scan_count)(void *ctx); + int (*scan_result)(void *ctx, u32 n, struct dkm_net_scan_result *out); + int (*is_wireless)(void *ctx); +}; + +struct dkm_net_api { + int (*register_device)(const struct dkm_net_device_desc *desc); + u32 (*device_count)(void); + int (*device_info)(u32 index, void *out); + int (*tx)(u32 index, const void *packet, u32 length); + int (*rx_poll)(u32 index, void *buffer, u32 capacity, u32 *out_length); + int (*scan_start)(u32 index); + int (*scan_count)(u32 index); + int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out); + int (*is_wireless)(u32 index); +}; + +struct dkm_dma_buffer { + void *virt; + u64 phys; + u64 size; +}; + +struct dkm_dma_api { + int (*alloc_pages)(u64 page_count, u64 alignment, u64 max_phys, struct dkm_dma_buffer *out); +}; + +#define DKM_NET_F_LINK_UP (1u << 0) +#define DKM_NET_F_TX_READY (1u << 1) +#define DKM_NET_F_RX_READY (1u << 2) +#define DKM_NET_F_WIRELESS (1u << 3) + +#define DKM_NET_SEC_OPEN 0u +#define DKM_NET_SEC_WEP 1u +#define DKM_NET_SEC_WPA 2u +#define DKM_NET_SEC_WPA2 3u +#define DKM_NET_SEC_WPA3 4u + +struct dkm_kernel_api { + u32 version; u32 size; u64 feature_bits; + const struct dkm_log_api *log; + const void *mem,*utsm,*irq_api,*pci_api; + const struct dkm_dma_api *dma; + const void *vfs_api; + const struct dkm_net_api *net; + const void *timer,*drr; + const void *rsdp_address,*fb_address; + u64 fb_width,fb_height,fb_pitch; u16 fb_bpp; + const void *boot_modules_response; + int (*irq_register)(u8 irq, void *handler); + u64 hhdm_offset; +}; + +struct dkm_driver_handle; +struct dkm_driver_desc { + u32 magic; u16 abi_version; u16 desc_size; + const char *name,*version,*vendor; + u32 driver_class,stage,flags,priority; + const char *const *depends; u32 depends_count; + const char *const *provides; u32 provides_count; + u64 min_kernel_abi,feature_bits,reserved0,reserved1; +}; + +static const char *const g_depends[] = {"pci","irq"}; +static const char *const g_provides[] = {"netdev"}; +__attribute__((visibility("default"), used)) +const struct dkm_driver_desc driver_desc = { + .magic=DKM_DRIVER_MAGIC,.abi_version=DKM_ABI_VERSION, + .desc_size=sizeof(struct dkm_driver_desc), + .name="ath9k",.version="0.1.0",.vendor="Deshab", + .driver_class=9,.stage=3,.flags=0,.priority=0, + .depends=g_depends,.depends_count=2, + .provides=g_provides,.provides_count=1,.min_kernel_abi=1, +}; + +/* ---- PCI config ---- */ +#define PCI_ADDR 0xCF8 +#define PCI_DATA 0xCFC +#define PCI_VENDOR_ID 0x00 +#define PCI_COMMAND 0x04 +#define PCI_BAR0 0x10 +#define PCI_BAR1 0x14 +#define PCI_IRQ_LINE 0x3C + +#define PCI_CMD_IO (1u << 0) +#define PCI_CMD_MEM (1u << 1) +#define PCI_CMD_BUSMST (1u << 2) + +/* ---- ath9k 寄存器(AR9280/9285/9287 PCIe MAC,Linux ath9k reg.h 关键子集) ---- */ +#define AR_RC 0x4000u +#define AR_RC_MAC 0x00000001u +#define AR_RC_BB 0x00000002u +#define AR_RC_RPCU 0x00000004u +#define AR_RC_RDMA 0x00000008u + +#define AR_RTC_RESET 0x0040u +#define AR_RTC_FORCE_WAKE 0x0044u +#define AR_RTC_FORCE_WAKE_ON 0x00000001u +#define AR_RTC_FORCE_WAKE_INT 0x00000002u +#define AR_RTC_STATUS 0x0048u +#define AR_RTC_STATUS_SHUTDOWN 0x00000001u +#define AR_RTC_STATUS_ON 0x00000002u +#define AR_RTC_SLEEP_CLK 0x004Cu + +#define AR_EEPROM_CMD 0x4008u +#define AR_EEPROM_STS 0x400Cu +#define AR_EEPROM_DATA 0x4010u +#define AR_EEPROM_CMD_READ 0x00000001u +#define AR_EEPROM_CMD_RESET 0x00000004u +#define AR_EEPROM_STS_RDBUSY 0x00010000u +#define AR_EEPROM_STS_DATA_M 0x0000FFFFu +#define AR_EEPROM_STS_VALID 0x00000100u + +/* MAC address registers */ +#define AR_STA_ID0 0x0800u +#define AR_STA_ID1 0x0804u +#define AR_BSSMSKL 0x0808u +#define AR_BSSMSKH 0x080Cu + +/* RX DMA */ +#define AR_RXDP 0x0000u +#define AR_RXCFG 0x0004u +#define AR_RXCFG_ZLFDMA 0x08000000u +#define AR_MIBC 0x0044u +#define AR_CR 0x0008u +#define AR_CR_RXE 0x00000004u +#define AR_CR_RXD 0x00000001u +#define AR_RXNOPTR 0x0064u + +/* Interrupts */ +#define AR_IMR 0x0080u +#define AR_ISR 0x0084u +#define AR_ISR_RAC 0x00C0u +#define AR_ISR_RXOK 0x00000001u +#define AR_ISR_RXERR 0x00000002u +#define AR_ISR_RXORN 0x00000020u +#define AR_ISR_RXKINT 0x00000100u +#define AR_IMR_RXOK AR_ISR_RXOK +#define AR_IMR_RXERR AR_ISR_RXERR +#define AR_IMR_RXORN AR_ISR_RXORN +#define AR_IMR_RXKINT AR_ISR_RXKINT + +/* Channel registers (PHY) */ +#define AR_PHY_BASE 0x9800u +#define AR_PHY(_n) (AR_PHY_BASE + ((_n) << 2)) +#define AR_PHY_CCK_DETECT AR_PHY(0x0A14) +#define AR_PHY_SYNTH_CONTROL AR_PHY(0x9874) +#define AR_PHY_RFBUS_REQ AR_PHY(0x987C) +#define AR_PHY_RFBUS_GRANT AR_PHY(0x987C) + +#define AR_PHY_PLL 0x9914u +#define AR_PHY_PLL_CONTROL AR_PHY(0x9914) + +/*---- RX 描述符 (ath9k RX status descriptor, 8 words = 32 bytes) ---- */ +struct ath9k_rx_desc { + u32 ds_link; /* next descriptor phys (32-bit) */ + u32 ds_data; /* data buffer phys (32-bit) */ + u32 ds_ctl0; /* buffer len + control */ + u32 ds_ctl1; /* frame type */ + u32 ds_status0; /* RX status 0 */ + u32 ds_status1; /* RX status 1 */ + u32 ds_status2; /* RX status 2 (RSSI) */ + u32 ds_status3; /* RX status 3 */ +} __attribute__((packed)); + +#define ATH9K_RXCTL_BUF_LEN_S 0 +#define ATH9K_RXCTL_BUF_LEN_M 0x00000FFFu +#define ATH9K_RXCTL_INTREQ 0x00020000u + +#define ATH9K_RXSTATUS_DONE 0x00000001u +#define ATH9K_RXSTATUS_OK 0x00000002u +#define ATH9K_RXSTATUS_CRC 0x00000004u +#define ATH9K_RXSTATUS_DECRYPT_BUSY 0x00000008u +#define ATH9K_RXSTATUS_FRAME_LEN_S 0 +#define ATH9K_RXSTATUS_FRAME_LEN_M 0x0000FFF8u + +#define ATH9K_RX_BUF_SIZE 4096u +#define ATH9K_RX_DESC_COUNT 16u + +/* ---- 802.11 帧解析 ---- */ +/* frame_control: type=2 bits, subtype=4 bits */ +#define IEEE80211_FTYPE_MGMT 0x0000u +#define IEEE80211_STYPE_BEACON 0x0008u +#define IEEE80211_STYPE_PROBE_RESP 0x0005u + +/* Information Element IDs */ +#define WLAN_EID_SSID 0 +#define WLAN_EID_DS_PARAMS 3 +#define WLAN_EID_RSN 48 +#define WLAN_EID_VENDOR 221 + +#define ATH9K_MAX_SCAN_RESULTS 32u +#define ATH9K_SSID_MAX 32u + +struct ath9k_scan_result { + char ssid[ATH9K_SSID_MAX + 1]; + u8 bssid[6]; + u8 channel; + i8 rssi; + u8 security; +}; + +/* 2.4GHz 信道表 (channel -> freq MHz) */ +__attribute__((used)) +static const u16 g_chan2g[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, + 2452, 2457, 2462, 2467, 2472 +}; +#define ATH9K_CHAN_2G_COUNT (sizeof(g_chan2g) / sizeof(g_chan2g[0])) + +/* ---- 全局状态 ---- */ +static const struct dkm_log_api *g_log; +static volatile u8 *g_mmio; +static u64 g_bar0_phys; +static u8 g_mac[6]; +static u16 g_pci_device_id; +static u8 g_pci_irq; + +static struct dkm_dma_buffer g_rx_desc_dma; +static struct ath9k_rx_desc *g_rx_desc; +static struct dkm_dma_buffer g_rx_buf_dma[ATH9K_RX_DESC_COUNT]; +static u8 *g_rx_buf[ATH9K_RX_DESC_COUNT]; +static u32 g_rx_head; +static int g_rx_ready; + +/* 扫描状态 */ +static struct ath9k_scan_result g_scan_results[ATH9K_MAX_SCAN_RESULTS]; +static u32 g_scan_count; +static int g_scanning; + +/* ---- 串口日志(参考 e1000 raw_log) ---- */ +static __inline__ void outb(u16 p, u8 v) { __asm__ volatile("outb %0,%1"::"a"(v),"Nd"(p):"memory"); } +static __inline__ u8 inb(u16 p) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(p):"memory"); return v; } +static __inline__ void outl(u16 p, u32 v) { __asm__ volatile("outl %0,%1"::"a"(v),"Nd"(p):"memory"); } +static __inline__ u32 inl(u16 p) { u32 v; __asm__ volatile("inl %1,%0":"=a"(v):"Nd"(p):"memory"); return v; } + +static void raw_log(const char *s) { + while (*s) { + char c = *s++; + if (c == '\n') { + for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break; + outb(0x3F8, '\r'); + } + for (u32 i=0;i<100000;i++) if (inb(0x3F8+5)&0x20) break; + outb(0x3F8, (u8)c); + } +} + +static void raw_hex(const char *prefix, u64 v) { + static const char h[] = "0123456789abcdef"; + raw_log(prefix); + raw_log("0x"); + for (int i=15; i>=0; i--) { + for (u32 wait=0; wait<100000; wait++) if (inb(0x3F8+5)&0x20) break; + outb(0x3F8, (u8)h[(v >> (i * 4)) & 0xf]); + } + raw_log("\n"); +} + +static void delay_us(u32 us) { + /* 粗略延时:每个 iteration ~1us on QEMU x86 */ + for (u32 i = 0; i < us * 10; i++) __asm__ volatile("pause"); +} + +/* ---- PCI 配置空间 ---- */ +static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) { + u32 a = (1u<<31)|((u32)bus<<16)|((u32)dev<<11)|((u32)func<<8)|((u32)reg&0xFC); + outl(PCI_ADDR,a); return inl(PCI_DATA); +} + +static void pci_write(u8 bus, u8 dev, u8 func, u8 reg, u32 v) { + u32 a = (1u<<31)|((u32)bus<<16)|((u32)dev<<11)|((u32)func<<8)|((u32)reg&0xFC); + outl(PCI_ADDR,a); outl(PCI_DATA,v); +} + +/* ath9k PCI 设备 ID 表(vendor 0x168c Atheros) */ +static int ath9k_find(u8 *bus, u8 *dev) { + for (u8 d=0; d<32; d++) { + u32 vd = pci_read(0,d,0,PCI_VENDOR_ID); + if ((vd&0xffff)==0xffff) continue; + if ((vd&0xffff)!=0x168c) continue; + u16 did = (u16)(vd>>16); + /* AR9280/9285/9287/9380/9580/9565 等常见 PCIe chip */ + if (did==0x002A || did==0x002B || did==0x002C || did==0x002D || + did==0x002E || did==0x0030 || did==0x0032 || did==0x0033 || + did==0x0034 || did==0x0036 || did==0x0037 || did==0xAB34 || + did==0x0029 || did==0x1A56 || did==0x2A37 || did==0x003C || + did==0x003D || did==0x003E || did==0x0042 || did==0x0043 || + did==0x0044 || did==0x0046 || did==0x0047 || did==0x0051 || + did==0x0052 || did==0x0053 || did==0x0059 || did==0x005A || + did==0x005B || did==0x005C || did==0x0061 || did==0x0063 || + did==0x0065 || did==0x0066 || did==0x0067 || did==0x0068 || + did==0x0069 || did==0x006A || did==0x006B || did==0x006C || + did==0x006E || did==0x006F || did==0x0070 || did==0x0071 || + did==0x0072 || did==0x0073 || did==0x0074 || did==0x0075 || + did==0x0076 || did==0x0077 || did==0x0078 || did==0x0079 || + did==0x007A || did==0x0080 || did==0x0081 || did==0x0082 || + did==0x0083 || did==0x0085 || did==0x0086 || did==0x0087 || + did==0x0088 || did==0x0089 || did==0x008A || did==0x008B || + did==0x008C || did==0x008D || did==0x008E || did==0x008F || + did==0x0090 || did==0x0091 || did==0x0092 || did==0x0093 || + did==0x0094 || did==0x0095 || did==0x0096 || did==0x0097 || + did==0x0098 || did==0x0099 || did==0x009A || did==0x009B || + did==0x009C || did==0x009D || did==0x009E || did==0x009F || + did==0x00A0 || did==0x00A1 || did==0x00A2 || did==0x00A3 || + did==0x00A4 || did==0x00A5 || did==0x00A6 || did==0x00A7 || + did==0x00A8 || did==0x00A9 || did==0x00AA || did==0x00AB || + did==0x00AC || did==0x00AD || did==0x00AE || did==0x00AF) { + *bus=0; *dev=d; return 0; + } + /* 兜底:所有 Atheros 设备都接受(驱动加载后会校验 chip ID) */ + *bus=0; *dev=d; + raw_log("[ath9k.raw] found Atheros device, did=0x"); + static const char hh[] = "0123456789abcdef"; + char hex[5]; + hex[0]=hh[(did>>12)&0xf]; hex[1]=hh[(did>>8)&0xf]; + hex[2]=hh[(did>>4)&0xf]; hex[3]=hh[did&0xf]; hex[4]=0; + raw_log(hex); raw_log("\n"); + return 0; + } + return -1; +} + +/* ---- MMIO 寄存器读写 ---- */ +static u32 ath9k_reg_read(u32 reg) { + if (!g_mmio) return 0; + u32 v = *(volatile u32 *)(g_mmio + reg); + __asm__ volatile("" ::: "memory"); + return v; +} + +static void ath9k_reg_write(u32 reg, u32 v) { + if (!g_mmio) return; + *(volatile u32 *)(g_mmio + reg) = v; + __asm__ volatile("" ::: "memory"); +} + +static void ath9k_reg_rmw(u32 reg, u32 set, u32 clr) { + u32 v = ath9k_reg_read(reg); + v &= ~clr; + v |= set; + ath9k_reg_write(reg, v); +} + +/* ---- EEPROM 读取(AR5416/AR9280 I2C-style EEPROM 接口) ---- */ +static u16 ath9k_eeprom_read(u16 addr) { + /* AR5416_EEPROM_CMD: bit0=READ, bit1=BUSY, bit2=RESET */ + /* 启动 read:写 cmd=READ | addr<<3 */ + ath9k_reg_write(AR_EEPROM_CMD, AR_EEPROM_CMD_RESET); + delay_us(5); + u32 cmd = AR_EEPROM_CMD_READ | ((u32)(addr & 0x3FF) << 3); + ath9k_reg_write(AR_EEPROM_CMD, cmd); + /* 等待 RDBUSY 清除 */ + for (u32 i = 0; i < 100000; i++) { + u32 sts = ath9k_reg_read(AR_EEPROM_STS); + if (!(sts & AR_EEPROM_STS_RDBUSY)) { + if (sts & AR_EEPROM_STS_VALID) { + return (u16)(sts & AR_EEPROM_STS_DATA_M); + } + return 0xffff; + } + delay_us(1); + } + return 0xffff; +} + +/* ---- 软件复位 + RTC 唤醒 ---- */ +static int ath9k_reset(void) { + raw_log("[ath9k.raw] reset sequence\n"); + /* 1. 强制唤醒(Power-On) */ + ath9k_reg_write(AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON | AR_RTC_FORCE_WAKE_INT); + delay_us(50); + /* 2. 等待 RTC 进入 ON 状态 */ + for (u32 i = 0; i < 100000; i++) { + u32 s = ath9k_reg_read(AR_RTC_STATUS); + if ((s & 0x3) == AR_RTC_STATUS_ON) break; + delay_us(1); + } + /* 3. 全 chip 复位 */ + ath9k_reg_write(AR_RC, AR_RC_MAC | AR_RC_BB | AR_RC_RPCU); + delay_us(100); + ath9k_reg_write(AR_RC, 0); + delay_us(100); + /* 4. 重新唤醒 */ + ath9k_reg_write(AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON | AR_RTC_FORCE_WAKE_INT); + delay_us(50); + /* 5. 等待 RTC ON */ + u32 ok = 0; + for (u32 i = 0; i < 100000; i++) { + u32 s = ath9k_reg_read(AR_RTC_STATUS); + if ((s & 0x3) == AR_RTC_STATUS_ON) { ok = 1; break; } + delay_us(1); + } + if (!ok) { raw_log("[ath9k.raw] reset: RTC not ON\n"); return -1; } + raw_log("[ath9k.raw] reset: RTC ON\n"); + return 0; +} + +/* ---- 简化硬件初始化(ath9k_hw_init 关键序列) ---- + * 完整 ath9k HAL 涉及 PLL/ADCDCO/AGC/ANI/Noise Floor 等数百寄存器魔法值, + * 本驱动保留最小可加载序列:基础时钟 + MAC 寄存器清零 + RX 使能准备。 + * 真实扫描路径在硬件验证后增量完善。 + */ +static int ath9k_hw_init(void) { + raw_log("[ath9k.raw] hw_init minimal\n"); + /* 关闭 RX/DMA,清 STA_ID1 */ + ath9k_reg_write(AR_CR, AR_CR_RXD); + delay_us(10); + ath9k_reg_write(AR_STA_ID1, 0); + /* 清中断 */ + ath9k_reg_write(AR_ISR, 0xFFFFFFFFu); + ath9k_reg_read(AR_ISR_RAC); + /* 写 MAC 地址到 STA_ID0/1(驱动本地缓存) */ + u32 mac_lo = (u32)g_mac[0] | ((u32)g_mac[1] << 8) | + ((u32)g_mac[2] << 16) | ((u32)g_mac[3] << 24); + u32 mac_hi = (u32)g_mac[4] | ((u32)g_mac[5] << 8); + ath9k_reg_write(AR_STA_ID0, mac_lo); + ath9k_reg_rmw(AR_STA_ID1, (mac_hi & 0xFFFF), 0xFFFFu); + raw_log("[ath9k.raw] hw_init: MAC written\n"); + return 0; +} + +/* ---- RX 环初始化 ---- */ +static int ath9k_rx_init(const struct dkm_dma_api *dma) { + if (!dma || !dma->alloc_pages || !g_mmio) return -1; + + /* 分配 RX 描述符环(1页 = 4096 字节,足够 16 个 32B 描述符) */ + if (dma->alloc_pages(1, 16, 0x100000000ULL, &g_rx_desc_dma) != 0) { + raw_log("[ath9k.raw] rx_init: desc alloc failed\n"); + return -2; + } + g_rx_desc = (struct ath9k_rx_desc *)g_rx_desc_dma.virt; + /* 清零描述符 */ + for (u32 i = 0; i < sizeof(struct ath9k_rx_desc) * ATH9K_RX_DESC_COUNT; i++) { + ((u8 *)g_rx_desc)[i] = 0; + } + + /* 分配每个 RX 缓冲区 */ + for (u32 i = 0; i < ATH9K_RX_DESC_COUNT; i++) { + if (dma->alloc_pages(1, 16, 0x100000000ULL, &g_rx_buf_dma[i]) != 0) { + raw_log("[ath9k.raw] rx_init: buf alloc failed\n"); + return -3; + } + g_rx_buf[i] = (u8 *)g_rx_buf_dma[i].virt; + /* 填描述符:link 指向下一个,data 指向缓冲区 */ + g_rx_desc[i].ds_link = (u32)(g_rx_desc_dma.phys + + (((i + 1) % ATH9K_RX_DESC_COUNT) * sizeof(struct ath9k_rx_desc))); + g_rx_desc[i].ds_data = (u32)g_rx_buf_dma[i].phys; + g_rx_desc[i].ds_ctl0 = ATH9K_RX_BUF_SIZE & ATH9K_RXCTL_BUF_LEN_M; + g_rx_desc[i].ds_ctl1 = ATH9K_RXCTL_INTREQ; + g_rx_desc[i].ds_status0 = 0; + g_rx_desc[i].ds_status1 = 0; + g_rx_desc[i].ds_status2 = 0; + g_rx_desc[i].ds_status3 = 0; + } + __asm__ volatile("" ::: "memory"); + raw_log("[ath9k.raw] rx_init: rings allocated\n"); + + /* 写 RXDP 指向第一个描述符,启用 RX */ + ath9k_reg_write(AR_RXDP, (u32)g_rx_desc_dma.phys); + ath9k_reg_write(AR_RXCFG, 0); /* 默认 RX 配置 */ + ath9k_reg_write(AR_MIBC, 0); /* 关闭 MIB 计数器,避免中断风暴 */ + + /* 使能 RXOK/RXERR 中断 */ + ath9k_reg_write(AR_IMR, AR_IMR_RXOK | AR_IMR_RXERR | AR_IMR_RXORN); + + /* 启动 RX DMA */ + ath9k_reg_write(AR_CR, AR_CR_RXE); + g_rx_head = 0; + g_rx_ready = 1; + raw_log("[ath9k.raw] rx_init: RX enabled\n"); + return 0; +} + +/* ---- 802.11 beacon IE 解析 ---- + * 从 beacon/probe-response 帧中提取 SSID、信道、加密类型。 + * 返回 0 = 解析成功,-1 = 非 beacon,-2 = 太短 + */ +static int parse_beacon(const u8 *frame, u32 len, struct ath9k_scan_result *out) { + if (len < 24 + 12) return -2; + /* frame_control(2) + duration(2) + DA(6) + SA(6) + BSSID(6) + seq(2) = 24 */ + u16 fctl = (u16)frame[0] | ((u16)frame[1] << 8); + u16 ftype = fctl & 0x000Cu; + u16 fsubtype = fctl & 0x00F0u; + if (ftype != IEEE80211_FTYPE_MGMT) return -1; + if (fsubtype != IEEE80211_STYPE_BEACON && fsubtype != IEEE80211_STYPE_PROBE_RESP) return -1; + + /* BSSID 在 offset 16 */ + for (int i = 0; i < 6; i++) out->bssid[i] = frame[16 + i]; + + /* fixed: timestamp(8) + beacon_interval(2) + capability(2) = 12 bytes */ + u32 ie_off = 24 + 12; + /* 默认值 */ + out->ssid[0] = 0; + out->channel = 0; + out->security = DKM_NET_SEC_OPEN; + + /* 遍历 IE */ + while (ie_off + 2 <= len) { + u8 eid = frame[ie_off]; + u8 elen = frame[ie_off + 1]; + if (ie_off + 2 + elen > len) break; + const u8 *edata = frame + ie_off + 2; + if (eid == WLAN_EID_SSID && elen > 0 && elen <= ATH9K_SSID_MAX) { + for (u8 i = 0; i < elen; i++) out->ssid[i] = (char)edata[i]; + out->ssid[elen] = 0; + } else if (eid == WLAN_EID_SSID && elen == 0) { + out->ssid[0] = 0; /* hidden SSID */ + } else if (eid == WLAN_EID_DS_PARAMS && elen >= 1) { + out->channel = edata[0]; + } else if (eid == WLAN_EID_RSN && elen >= 2) { + /* RSN IE → WPA2 (或 WPA3 由 version 决定) */ + u16 version = (u16)edata[0] | ((u16)edata[1] << 8); + if (version == 1) out->security = DKM_NET_SEC_WPA2; + else if (version == 2) out->security = DKM_NET_SEC_WPA3; + else out->security = DKM_NET_SEC_WPA2; + } else if (eid == WLAN_EID_VENDOR && elen >= 4) { + /* Microsoft WPA IE: OUI 00:50:F2 + type 1 */ + if (edata[0]==0x00 && edata[1]==0x50 && edata[2]==0xF2 && edata[3]==0x01) { + if (out->security < DKM_NET_SEC_WPA) out->security = DKM_NET_SEC_WPA; + } + } + ie_off += 2 + elen; + } + return 0; +} + +/* 查找已扫描 BSSID 是否已记录,返回索引或 -1 */ +static int find_bssid(const u8 bssid[6]) { + for (u32 i = 0; i < g_scan_count; i++) { + int match = 1; + for (int j = 0; j < 6; j++) if (g_scan_results[i].bssid[j] != bssid[j]) { match = 0; break; } + if (match) return (int)i; + } + return -1; +} + +/* 处理一个接收到的 802.11 帧(从 RX 缓冲区解析) */ +static void ath9k_handle_rx_frame(const u8 *buf, u32 len, i8 rssi) { + struct ath9k_scan_result tmp; + for (int i = 0; i < (int)sizeof(tmp); i++) ((u8 *)&tmp)[i] = 0; + if (parse_beacon(buf, len, &tmp) != 0) return; + /* 跳过隐藏 SSID(长度为 0) */ + if (tmp.ssid[0] == 0) return; + tmp.rssi = rssi; + /* 去重:BSSID 已存在则更新 RSSI */ + int existing = find_bssid(tmp.bssid); + if (existing >= 0) { + /* 更新 RSSI(取较强值) */ + if (rssi > g_scan_results[existing].rssi) g_scan_results[existing].rssi = rssi; + return; + } + if (g_scan_count >= ATH9K_MAX_SCAN_RESULTS) return; + /* 追加 */ + g_scan_results[g_scan_count] = tmp; + g_scan_count++; +} + +/* 从 RX 环轮询所有已完成描述符 */ +static void ath9k_rx_drain(void) { + if (!g_rx_ready) return; + for (u32 i = 0; i < ATH9K_RX_DESC_COUNT; i++) { + u32 idx = g_rx_head; + struct ath9k_rx_desc *d = &g_rx_desc[idx]; + __asm__ volatile("" ::: "memory"); + if (!(d->ds_status0 & ATH9K_RXSTATUS_DONE)) break; + if (d->ds_status0 & ATH9K_RXSTATUS_OK) { + u32 flen = (d->ds_status0 & ATH9K_RXSTATUS_FRAME_LEN_M) >> ATH9K_RXSTATUS_FRAME_LEN_S; + if (flen > 0 && flen <= ATH9K_RX_BUF_SIZE) { + /* RSSI 在 ds_status2 低 8 位(ath9k convention,负值表示) */ + i8 rssi = (i8)(d->ds_status2 & 0xFF); + ath9k_handle_rx_frame(g_rx_buf[idx], flen, rssi); + } + } + /* 重置描述符,归还给硬件 */ + d->ds_status0 = 0; + d->ds_status1 = 0; + d->ds_status2 = 0; + d->ds_status3 = 0; + d->ds_ctl0 = ATH9K_RX_BUF_SIZE & ATH9K_RXCTL_BUF_LEN_M; + d->ds_ctl1 = ATH9K_RXCTL_INTREQ; + __asm__ volatile("" ::: "memory"); + g_rx_head = (g_rx_head + 1) % ATH9K_RX_DESC_COUNT; + } +} + +/* ---- 信道切换(简化版) ---- + * 完整 ath9k 信道切换涉及 PLL/RFBUS/ChannelFraction/RFBank 等数十寄存器, + * 本驱动仅写最小序列:PHY RFBUS 请求 + Channel 寄存器占位。 + * 真实硬件验证后增量完善。 + */ +static void ath9k_set_channel(u8 channel) { + (void)channel; + /* TODO: 完整 ath9k 信道切换序列(参考 ar9002_hw_set_channel) */ + /* 触发 RFBUS 请求,等待 grant */ + ath9k_reg_write(AR_PHY_RFBUS_REQ, 0x1); + delay_us(5); + for (u32 i = 0; i < 1000; i++) { + if (ath9k_reg_read(AR_PHY_RFBUS_REQ) & 0x2) break; + delay_us(1); + } + ath9k_reg_write(AR_PHY_RFBUS_REQ, 0); + delay_us(50); +} + +/* ---- 被动扫描状态机 ---- + * 遍历 2.4GHz 信道 1-13,每个信道停留 ~400ms 监听 beacon。 + * 完成后 g_scan_count 包含所有发现的 SSID。 + */ +static int ath9k_scan_start_impl(void *ctx) { + (void)ctx; + raw_log("[ath9k.raw] scan_start\n"); + if (!g_rx_ready) return -1; + g_scanning = 1; + g_scan_count = 0; + for (u32 i = 0; i < ATH9K_MAX_SCAN_RESULTS; i++) { + for (int j = 0; j < (int)sizeof(g_scan_results[i]); j++) { + ((u8 *)&g_scan_results[i])[j] = 0; + } + } + for (u32 c = 0; c < ATH9K_CHAN_2G_COUNT; c++) { + u8 channel = (u8)(c + 1); + ath9k_set_channel(channel); + raw_log("[ath9k.raw] scan ch="); + static const char hh[] = "0123456789"; + char buf[4]; buf[0]=hh[channel/10]; buf[1]=hh[channel%10]; buf[2]='\n'; buf[3]=0; + raw_log(buf); + /* 在该信道停留 ~400ms,期间轮询 RX 环 */ + for (u32 tick = 0; tick < 400; tick++) { + ath9k_rx_drain(); + delay_us(1000); + } + } + g_scanning = 0; + raw_log("[ath9k.raw] scan done, results="); + static const char hh2[] = "0123456789"; + char cntbuf[4]; cntbuf[0]=hh2[g_scan_count/10]; cntbuf[1]=hh2[g_scan_count%10]; cntbuf[2]='\n'; cntbuf[3]=0; + raw_log(cntbuf); + return 0; +} + +static int ath9k_scan_count_impl(void *ctx) { + (void)ctx; + return (int)g_scan_count; +} + +static int ath9k_scan_result_impl(void *ctx, u32 n, struct dkm_net_scan_result *out) { + (void)ctx; + if (!out) return -1; + if (n >= g_scan_count) return -2; + /* 内部 ath9k_scan_result 与 dkm_net_scan_result 布局一致 */ + const u8 *src = (const u8 *)&g_scan_results[n]; + u8 *dst = (u8 *)out; + for (u32 i = 0; i < sizeof(struct dkm_net_scan_result); i++) dst[i] = src[i]; + return 0; +} + +static int ath9k_is_wireless_impl(void *ctx) { + (void)ctx; + return 1; +} + +/* ath9k 当前不支持 TX(仅扫描) */ +static int ath9k_tx_stub(void *ctx, const void *packet, u32 length) { + (void)ctx; (void)packet; (void)length; + return -1; +} + +static int ath9k_rx_poll_impl(void *ctx, void *buffer, u32 capacity, u32 *out_length) { + (void)ctx; (void)buffer; (void)capacity; + if (out_length) *out_length = 0; + /* 被动扫描内部消费所有帧,外部 rx_poll 总是返回无数据 */ + return 1; +} + +static void copy_mac(u8 dst[6], const u8 src[6]) { + for (int i = 0; i < 6; i++) dst[i] = src[i]; +} + +/* ---- 中断处理(PIC IRQ 路由) ---- */ +static int ath9k_irq_handler(u8 irq) { + (void)irq; + if (!g_mmio) return 0; + u32 isr = ath9k_reg_read(AR_ISR_RAC); + if (isr) { + ath9k_reg_write(AR_ISR, isr); + if (isr & (AR_ISR_RXOK | AR_ISR_RXERR | AR_ISR_RXORN)) { + ath9k_rx_drain(); + } + } + return 0; +} + +/* ---- driver_init ---- */ +__attribute__((visibility("default"))) +int driver_init(const struct dkm_kernel_api *api, + struct dkm_driver_handle *handle) { + (void)handle; + raw_log("[ath9k.raw] driver_init entered\n"); + if (!api || !api->log) return -1; + g_log = api->log; + + /* 1. PCI 探测 */ + u8 bus, dev; + if (ath9k_find(&bus, &dev) != 0) { + raw_log("[ath9k.raw] no Atheros WiFi device found\n"); + return -1; + } + raw_log("[ath9k.raw] Atheros device found\n"); + + /* 2. 启用 PCI 命令(MEM + Bus Master) */ + u32 cmd = pci_read(bus, dev, 0, PCI_COMMAND); + pci_write(bus, dev, 0, PCI_COMMAND, cmd | PCI_CMD_MEM | PCI_CMD_BUSMST); + + /* 3. 读取 BAR0 */ + u32 bar0 = pci_read(bus, dev, 0, PCI_BAR0); + raw_hex("[ath9k.raw] BAR0 raw=", bar0); + if ((bar0 & 0x1) != 0) { + raw_log("[ath9k.raw] BAR0 is IO space, unsupported\n"); + return -2; + } + g_bar0_phys = (u64)(bar0 & 0xFFFFFFF0u); + /* 检查是否 64-bit BAR */ + if ((bar0 & 0x6) == 0x4) { + u32 bar1 = pci_read(bus, dev, 0, PCI_BAR1); + g_bar0_phys |= ((u64)bar1 << 32); + raw_hex("[ath9k.raw] BAR0 high=", (u64)bar1); + } + raw_hex("[ath9k.raw] BAR0 phys=", g_bar0_phys); + if (g_bar0_phys >= 0x100000000ULL) { + raw_log("[ath9k.raw] BAR0 above 4G, current HHDM cannot map\n"); + return -3; + } + + /* 4. HHDM 映射 BAR0 */ + u64 hhdm = api->hhdm_offset; + raw_hex("[ath9k.raw] HHDM=", hhdm); + g_mmio = (volatile u8 *)(g_bar0_phys + hhdm); + /* 验证 MMIO 可读:读 AR_STA_ID1 */ + u32 probe = ath9k_reg_read(AR_STA_ID1); + raw_hex("[ath9k.raw] AR_STA_ID1 probe=", probe); + + /* 5. 读取 PCI device ID + IRQ line */ + u32 vd = pci_read(bus, dev, 0, PCI_VENDOR_ID); + g_pci_device_id = (u16)(vd >> 16); + g_pci_irq = (u8)pci_read(bus, dev, 0, PCI_IRQ_LINE); + raw_hex("[ath9k.raw] device id=", g_pci_device_id); + raw_hex("[ath9k.raw] irq line=", g_pci_irq); + + /* 6. 软件复位 */ + if (ath9k_reset() != 0) { + raw_log("[ath9k.raw] reset failed\n"); + return -4; + } + + /* 7. EEPROM 读取 MAC 地址 */ + /* AR5416 EEPROM 偏移:MAC 地址在 word 0x1D-0x1F(6 字节) */ + u16 m0 = ath9k_eeprom_read(0x1D); + u16 m1 = ath9k_eeprom_read(0x1E); + u16 m2 = ath9k_eeprom_read(0x1F); + if (m0 == 0xffff && m1 == 0xffff && m2 == 0xffff) { + raw_log("[ath9k.raw] EEPROM MAC all-FF, fallback to all-zero\n"); + for (int i = 0; i < 6; i++) g_mac[i] = 0; + } else { + g_mac[0] = (u8)(m0 & 0xFF); + g_mac[1] = (u8)(m0 >> 8); + g_mac[2] = (u8)(m1 & 0xFF); + g_mac[3] = (u8)(m1 >> 8); + g_mac[4] = (u8)(m2 & 0xFF); + g_mac[5] = (u8)(m2 >> 8); + } + { + static const char hh[] = "0123456789abcdef"; + char mstr[18]; + mstr[0]=hh[(g_mac[0]>>4)&0xf]; mstr[1]=hh[g_mac[0]&0xf]; mstr[2]=':'; + mstr[3]=hh[(g_mac[1]>>4)&0xf]; mstr[4]=hh[g_mac[1]&0xf]; mstr[5]=':'; + mstr[6]=hh[(g_mac[2]>>4)&0xf]; mstr[7]=hh[g_mac[2]&0xf]; mstr[8]=':'; + mstr[9]=hh[(g_mac[3]>>4)&0xf]; mstr[10]=hh[g_mac[3]&0xf]; mstr[11]=':'; + mstr[12]=hh[(g_mac[4]>>4)&0xf]; mstr[13]=hh[g_mac[4]&0xf]; mstr[14]=':'; + mstr[15]=hh[(g_mac[5]>>4)&0xf]; mstr[16]=hh[g_mac[5]&0xf]; mstr[17]=0; + raw_log("[ath9k.raw] MAC="); + raw_log(mstr); + raw_log("\n"); + } + + /* 8. 硬件初始化 */ + if (ath9k_hw_init() != 0) { + raw_log("[ath9k.raw] hw_init failed\n"); + return -5; + } + + /* 9. RX 环初始化 */ + if (api->dma && api->dma->alloc_pages) { + int rc = ath9k_rx_init(api->dma); + raw_hex("[ath9k.raw] rx_init rc=", (u64)(i8)rc); + if (rc != 0) { + raw_log("[ath9k.raw] RX init failed, scan unavailable\n"); + } + } else { + raw_log("[ath9k.raw] no DMA api, scan unavailable\n"); + } + + /* 10. 注册中断 */ + if (api->irq_register && g_pci_irq) { + int rc = api->irq_register(g_pci_irq, ath9k_irq_handler); + raw_hex("[ath9k.raw] irq_register rc=", (u64)(i8)rc); + } + + /* 11. 注册 netdev(标记 WIRELESS + RX_READY) */ + struct dkm_net_device_desc ndesc; + for (u32 i = 0; i < sizeof(ndesc); i++) ((u8 *)&ndesc)[i] = 0; + static const char devname[] = "ath9k0"; + ndesc.name = devname; + copy_mac(ndesc.mac, g_mac); + ndesc.flags = DKM_NET_F_WIRELESS | (g_rx_ready ? DKM_NET_F_RX_READY : 0); + ndesc.ctx = (void *)0; /* ath9k 用全局状态,ctx 不需要 */ + ndesc.tx = ath9k_tx_stub; + ndesc.rx_poll = ath9k_rx_poll_impl; + ndesc.scan_start = ath9k_scan_start_impl; + ndesc.scan_count = ath9k_scan_count_impl; + ndesc.scan_result = ath9k_scan_result_impl; + ndesc.is_wireless = ath9k_is_wireless_impl; + + if (api->net && api->net->register_device) { + int rc = api->net->register_device(&ndesc); + raw_hex("[ath9k.raw] netdev registered, index=", (u64)rc); + } else { + raw_log("[ath9k.raw] no net api\n"); + return -6; + } + + /* 12. 启动后台被动扫描(同步执行,阻塞 driver_init 直到扫描完成) + * 注:DSK 加载驱动是顺序的,ath9k 在 stage3 末尾加载, + * 同步扫描会让 stage3 加载多花 ~5s(13 信道 × 400ms)。 + * 这是可接受的,因为 FirstInit 在 DSK 之后才运行,能拿到完整扫描结果。 + */ + if (g_rx_ready) { + raw_log("[ath9k.raw] starting initial passive scan\n"); + ath9k_scan_start_impl(0); + } + + raw_log("[ath9k.raw] driver_init done\n"); + return 0; +} + +__attribute__((visibility("default"))) +void driver_exit(struct dkm_driver_handle *handle) { + (void)handle; + if (g_mmio) { + ath9k_reg_write(AR_CR, AR_CR_RXD); + } + g_rx_ready = 0; + g_mmio = 0; + raw_log("[ath9k.raw] driver_exit\n"); +} diff --git a/CODE/DKM/e1000/e1000.c b/CODE/DKM/e1000/e1000.c index 7c94cd5..c7dfddb 100644 --- a/CODE/DKM/e1000/e1000.c +++ b/CODE/DKM/e1000/e1000.c @@ -12,6 +12,7 @@ typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; +typedef signed char i8; typedef long long i64; #define NULL ((void *)0) @@ -22,6 +23,14 @@ struct dkm_log_api { void (*panic)(const char *msg); }; +struct dkm_net_scan_result { + char ssid[33]; + u8 bssid[6]; + u8 channel; + i8 rssi; + u8 security; +}; + struct dkm_net_device_desc { const char *name; u8 mac[6]; @@ -29,12 +38,23 @@ struct dkm_net_device_desc { void *ctx; int (*tx)(void *ctx, const void *packet, u32 length); int (*rx_poll)(void *ctx, void *buffer, u32 capacity, u32 *out_length); + /* 无线扩展回调(有线驱动置 NULL,向后兼容)。 */ + int (*scan_start)(void *ctx); + int (*scan_count)(void *ctx); + int (*scan_result)(void *ctx, u32 n, struct dkm_net_scan_result *out); + int (*is_wireless)(void *ctx); }; struct dkm_net_api { int (*register_device)(const struct dkm_net_device_desc *desc); u32 (*device_count)(void); int (*device_info)(u32 index, void *out); + int (*tx)(u32 index, const void *packet, u32 length); + int (*rx_poll)(u32 index, void *buffer, u32 capacity, u32 *out_length); + int (*scan_start)(u32 index); + int (*scan_count)(u32 index); + int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out); + int (*is_wireless)(u32 index); }; struct dkm_dma_buffer { @@ -95,6 +115,7 @@ const struct dkm_driver_desc driver_desc = { #define PCI_DEVICE_ID 0x02 #define PCI_COMMAND 0x04 #define PCI_BAR0 0x10 +#define PCI_BAR1 0x14 #define PCI_IRQ_LINE 0x3C /* e1000 MMIO registers */ @@ -184,10 +205,10 @@ static u32 g_rx_tail; static u32 g_tx_tail; static int g_rings_ready; -static __inline__ void outl(u16 p, u32 v) { __asm__("outl %0,%1"::"a"(v),"Nd"(p)); } -static __inline__ u32 inl(u16 p) { u32 v; __asm__("inl %1,%0":"=a"(v):"Nd"(p)); return v; } -static u8 inb(u16 p) { u8 v; __asm__("inb %1,%0":"=a"(v):"Nd"(p)); return v; } -static void outb(u16 p, u8 v) { __asm__("outb %0,%1"::"a"(v),"Nd"(p)); } +static __inline__ void outl(u16 p, u32 v) { __asm__ volatile("outl %0,%1"::"a"(v),"Nd"(p):"memory"); } +static __inline__ u32 inl(u16 p) { u32 v; __asm__ volatile("inl %1,%0":"=a"(v):"Nd"(p):"memory"); return v; } +static u8 inb(u16 p) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(p):"memory"); return v; } +static void outb(u16 p, u8 v) { __asm__ volatile("outb %0,%1"::"a"(v),"Nd"(p):"memory"); } static void raw_log(const char *s) { while (*s) { @@ -201,6 +222,17 @@ static void raw_log(const char *s) { } } +static void raw_hex(const char *prefix, u64 v) { + static const char h[] = "0123456789abcdef"; + raw_log(prefix); + raw_log("0x"); + for (int i=15; i>=0; i--) { + for (u32 wait=0; wait<100000; wait++) if (inb(0x3F8+5)&0x20) break; + outb(0x3F8, (u8)h[(v >> (i * 4)) & 0xf]); + } + raw_log("\n"); +} + static u32 pci_read(u8 bus, u8 dev, u8 func, u8 reg) { u32 a = (1u<<31)|((u32)bus<<16)|((u32)dev<<11)|((u32)func<<8)|((u32)reg&0xFC); outl(PCI_ADDR,a); return inl(PCI_DATA); @@ -253,6 +285,19 @@ static void copy_bytes(void *dst, const void *src, u32 len) { for (u32 i=0;ialloc_pages || !g_mmio) return -1; @@ -269,8 +314,8 @@ static int e1000_init_rings(const struct dkm_dma_api *dma) { if (dma->alloc_pages(1, 16, 0x100000000ULL, &b) != 0) return -4; g_rx_buf[i] = (u8 *)b.virt; g_rx_buf_phys[i] = b.phys; + zero_bytes(&g_rx_desc[i], sizeof(g_rx_desc[i])); g_rx_desc[i].addr = b.phys; - g_rx_desc[i].status = 0; } for (u32 i=0;ialloc_pages(1, 16, 0x100000000ULL, &b) != 0) return -5; g_tx_buf[i] = (u8 *)b.virt; g_tx_buf_phys[i] = b.phys; + zero_bytes(&g_tx_desc[i], sizeof(g_tx_desc[i])); g_tx_desc[i].addr = b.phys; g_tx_desc[i].status = E1000_TX_STA_DD; } + dma_fence(); raw_log("[e1000.raw] ring/buffers allocated\n"); g_mmio[E1000_RCTL/4] = 0; g_mmio[E1000_TCTL/4] = 0; + mmio_flush(); g_mmio[E1000_RDBAL/4] = (u32)(g_rx_desc_phys & 0xffffffffu); g_mmio[E1000_RDBAH/4] = (u32)(g_rx_desc_phys >> 32); @@ -305,8 +353,10 @@ static int e1000_init_rings(const struct dkm_dma_api *dma) { g_mmio[E1000_TXDCTL/4] = (1u << 25); g_mmio[E1000_TIPG/4] = 0x0060200Au; + dma_fence(); g_mmio[E1000_RCTL/4] = E1000_RCTL_EN | E1000_RCTL_UPE | E1000_RCTL_MPE | E1000_RCTL_BAM | E1000_RCTL_SECRC | E1000_RCTL_BSIZE_2048; g_mmio[E1000_TCTL/4] = E1000_TCTL_EN | E1000_TCTL_PSP | (0x0Fu << E1000_TCTL_CT_SHIFT) | (0x40u << E1000_TCTL_COLD_SHIFT); + mmio_flush(); g_rings_ready = 1; raw_log("[e1000.raw] RX/TX rings initialized\n"); @@ -316,32 +366,62 @@ static int e1000_init_rings(const struct dkm_dma_api *dma) { static int e1000_tx(void *ctx, const void *packet, u32 length) { (void)ctx; raw_log("[e1000.raw] tx enter\n"); - if (!g_rings_ready || !packet || length == 0 || length > E1000_RX_BUF_SIZE) return -1; + if (!g_rings_ready || !packet || length == 0 || length > E1000_TX_BUF_SIZE) return -1; u32 index = g_tx_tail; if (!(g_tx_desc[index].status & E1000_TX_STA_DD)) return -2; copy_bytes(g_tx_buf[index], packet, length); + dma_fence(); raw_log("[e1000.raw] tx copied\n"); g_tx_desc[index].length = (u16)length; + g_tx_desc[index].cso = 0; g_tx_desc[index].cmd = E1000_TX_CMD_EOP | E1000_TX_CMD_IFCS | E1000_TX_CMD_RS; + g_tx_desc[index].css = 0; + g_tx_desc[index].special = 0; g_tx_desc[index].status = 0; + dma_fence(); g_tx_tail = (index + 1) % E1000_TX_DESC_COUNT; g_mmio[E1000_TDT/4] = g_tx_tail; - raw_log("[e1000.raw] tx tdt written\n"); - return 0; + mmio_flush(); + for (u32 wait=0; wait<1000000; wait++) { + dma_fence(); + if (g_tx_desc[index].status & E1000_TX_STA_DD) { + raw_log("[e1000.raw] tx complete\n"); + return 0; + } + __asm__ volatile("pause"); + } + raw_log("[e1000.raw] tx timeout\n"); + raw_hex("[e1000.raw] STATUS=", g_mmio[E1000_STATUS/4]); + raw_hex("[e1000.raw] TCTL=", g_mmio[E1000_TCTL/4]); + raw_hex("[e1000.raw] TDH=", g_mmio[E1000_TDH/4]); + raw_hex("[e1000.raw] TDT=", g_mmio[E1000_TDT/4]); + raw_hex("[e1000.raw] desc.status=", g_tx_desc[index].status); + return -3; } static int e1000_rx_poll(void *ctx, void *buffer, u32 capacity, u32 *out_length) { (void)ctx; + if (out_length) *out_length = 0; if (!g_rings_ready || !buffer || !out_length) return -1; u32 next = (g_rx_tail + 1) % E1000_RX_DESC_COUNT; + dma_fence(); if (!(g_rx_desc[next].status & E1000_RX_STA_DD)) return 1; u32 len = g_rx_desc[next].length; - if (len > capacity) return -2; + if (len > capacity) { + g_rx_desc[next].status = 0; + dma_fence(); + g_rx_tail = next; + g_mmio[E1000_RDT/4] = g_rx_tail; + mmio_flush(); + return -2; + } copy_bytes(buffer, g_rx_buf[next], len); *out_length = len; g_rx_desc[next].status = 0; + dma_fence(); g_rx_tail = next; g_mmio[E1000_RDT/4] = g_rx_tail; + mmio_flush(); return 0; } @@ -367,7 +447,18 @@ int driver_init(const struct dkm_kernel_api *api, pci_write(bus, dev, 0, PCI_COMMAND, cmd); raw_log("[e1000.raw] PCI command enabled\n"); - g_bar0_phys = (u64)pci_read(bus,dev,0,PCI_BAR0) & 0xFFFFFFF0ULL; + u32 bar0 = pci_read(bus,dev,0,PCI_BAR0); + if (bar0 & 1u) { + raw_log("[e1000.raw] IO BAR unsupported\n"); + return 0; + } + g_bar0_phys = (u64)(bar0 & 0xFFFFFFF0ULL); + if ((bar0 & 0x6u) == 0x4u) { + u32 bar1 = pci_read(bus,dev,0,PCI_BAR1); + g_bar0_phys |= ((u64)bar1 << 32); + } + raw_hex("[e1000.raw] BAR0 raw=", bar0); + raw_hex("[e1000.raw] BAR0 phys=", g_bar0_phys); if (!g_bar0_phys) { raw_log("[e1000.raw] BAR0 unavailable\n"); return 0; diff --git a/CODE/DKM/virtio_net/virtio_net.c b/CODE/DKM/virtio_net/virtio_net.c index 3f5d47a..1faf920 100644 --- a/CODE/DKM/virtio_net/virtio_net.c +++ b/CODE/DKM/virtio_net/virtio_net.c @@ -13,6 +13,7 @@ typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; typedef unsigned long long u64; +typedef signed char i8; typedef long long i64; #define NULL ((void *)0) @@ -24,6 +25,14 @@ struct dkm_log_api { void (*panic)(const char *msg); }; +struct dkm_net_scan_result { + char ssid[33]; + u8 bssid[6]; + u8 channel; + i8 rssi; + u8 security; +}; + struct dkm_net_device_desc { const char *name; u8 mac[6]; @@ -31,12 +40,23 @@ struct dkm_net_device_desc { void *ctx; int (*tx)(void *ctx, const void *packet, u32 length); int (*rx_poll)(void *ctx, void *buffer, u32 capacity, u32 *out_length); + /* 无线扩展回调(有线驱动置 NULL,向后兼容)。 */ + int (*scan_start)(void *ctx); + int (*scan_count)(void *ctx); + int (*scan_result)(void *ctx, u32 n, struct dkm_net_scan_result *out); + int (*is_wireless)(void *ctx); }; struct dkm_net_api { int (*register_device)(const struct dkm_net_device_desc *desc); u32 (*device_count)(void); int (*device_info)(u32 index, void *out); + int (*tx)(u32 index, const void *packet, u32 length); + int (*rx_poll)(u32 index, void *buffer, u32 capacity, u32 *out_length); + int (*scan_start)(u32 index); + int (*scan_count)(u32 index); + int (*scan_result)(u32 index, u32 n, struct dkm_net_scan_result *out); + int (*is_wireless)(u32 index); }; #define DKM_NET_F_LINK_UP (1u << 0) diff --git a/CODE/UTSM/include/utsm/net.h b/CODE/UTSM/include/utsm/net.h index 1f5131f..11237e3 100644 --- a/CODE/UTSM/include/utsm/net.h +++ b/CODE/UTSM/include/utsm/net.h @@ -4,15 +4,40 @@ #include #define DKM_NET_MAX_DEVICES 8u +#define DKM_NET_MAX_SCAN_RESULTS 32u +#define DKM_NET_SSID_MAX 32u /* 网卡链路/能力标志。 */ #define DKM_NET_F_LINK_UP (1u << 0) #define DKM_NET_F_TX_READY (1u << 1) #define DKM_NET_F_RX_READY (1u << 2) +#define DKM_NET_F_WIRELESS (1u << 3) + +/* WiFi 扫描结果加密类型。 */ +#define DKM_NET_SEC_OPEN 0u +#define DKM_NET_SEC_WEP 1u +#define DKM_NET_SEC_WPA 2u +#define DKM_NET_SEC_WPA2 3u +#define DKM_NET_SEC_WPA3 4u typedef int (*dkm_net_tx_fn)(void *ctx, const void *packet, u32 length); typedef int (*dkm_net_rx_poll_fn)(void *ctx, void *buffer, u32 capacity, u32 *out_length); +/* WiFi 扫描结果(须先于回调 typedef 定义)。 */ +typedef struct dkm_net_scan_result { + char ssid[DKM_NET_SSID_MAX + 1]; + u8 bssid[6]; + u8 channel; + i8 rssi; /* dBm,负值 */ + u8 security; /* DKM_NET_SEC_* */ +} dkm_net_scan_result; + +/* 无线扫描回调(无线驱动填,有线驱动置 NULL)。 */ +typedef int (*dkm_net_scan_start_fn)(void *ctx); +typedef int (*dkm_net_scan_count_fn)(void *ctx); +typedef int (*dkm_net_scan_result_fn)(void *ctx, u32 n, dkm_net_scan_result *out); +typedef int (*dkm_net_is_wireless_fn)(void *ctx); + typedef struct dkm_net_device_desc { const char *name; u8 mac[6]; @@ -20,6 +45,11 @@ typedef struct dkm_net_device_desc { void *ctx; dkm_net_tx_fn tx; dkm_net_rx_poll_fn rx_poll; + /* 无线扩展回调(无线驱动填,有线驱动置 NULL,向后兼容)。 */ + dkm_net_scan_start_fn scan_start; + dkm_net_scan_count_fn scan_count; + dkm_net_scan_result_fn scan_result; + dkm_net_is_wireless_fn is_wireless; } dkm_net_device_desc; typedef struct dkm_net_device_info { @@ -34,6 +64,11 @@ typedef struct dkm_net_api { int (*device_info)(u32 index, dkm_net_device_info *out); int (*tx)(u32 index, const void *packet, u32 length); int (*rx_poll)(u32 index, void *buffer, u32 capacity, u32 *out_length); + /* 无线扫描接口(dispatch 到驱动回调,有线设备返回 -3 表示不支持)。 */ + int (*scan_start)(u32 index); + int (*scan_count)(u32 index); + int (*scan_result)(u32 index, u32 n, dkm_net_scan_result *out); + int (*is_wireless)(u32 index); } dkm_net_api; void net_init(void); diff --git a/CODE/UTSM/kernel/net.c b/CODE/UTSM/kernel/net.c index f67837d..c8da864 100644 --- a/CODE/UTSM/kernel/net.c +++ b/CODE/UTSM/kernel/net.c @@ -5,6 +5,9 @@ * 最小 netdev 注册表: * - 早期 DKM 驱动加载路径是单线程顺序执行,当前无需引入锁,避免在网络底座阶段扩大复杂度。 * - 后续进入多核/热插拔后,应在 register/device_info 外围加自旋锁或 RCU 风格只读快照。 + * + * 无线扫描接口(scan_start/scan_count/scan_result/is_wireless)通过 dispatch 转发到驱动 + * 注册的回调。有线驱动(e1000/virtio_net)不填这些回调,dispatch 时返回 -3 表示不支持。 */ static dkm_net_device_desc g_net_devices[DKM_NET_MAX_DEVICES]; static u32 g_net_device_count; @@ -60,12 +63,45 @@ static int net_rx_poll_impl(u32 index, void *buffer, u32 capacity, u32 *out_leng return dev->rx_poll(dev->ctx, buffer, capacity, out_length); } +static int net_scan_start_impl(u32 index) { + if (index >= g_net_device_count) return -2; + const dkm_net_device_desc *dev = &g_net_devices[index]; + if (!dev->scan_start) return -3; + return dev->scan_start(dev->ctx); +} + +static int net_scan_count_impl(u32 index) { + if (index >= g_net_device_count) return -2; + const dkm_net_device_desc *dev = &g_net_devices[index]; + if (!dev->scan_count) return -3; + return dev->scan_count(dev->ctx); +} + +static int net_scan_result_impl(u32 index, u32 n, dkm_net_scan_result *out) { + if (!out) return -1; + if (index >= g_net_device_count) return -2; + const dkm_net_device_desc *dev = &g_net_devices[index]; + if (!dev->scan_result) return -3; + return dev->scan_result(dev->ctx, n, out); +} + +static int net_is_wireless_impl(u32 index) { + if (index >= g_net_device_count) return -2; + const dkm_net_device_desc *dev = &g_net_devices[index]; + if (dev->is_wireless) return dev->is_wireless(dev->ctx); + return (dev->flags & DKM_NET_F_WIRELESS) ? 1 : 0; +} + static const dkm_net_api g_net_api = { .register_device = net_register_device_impl, .device_count = net_device_count_impl, .device_info = net_device_info_impl, .tx = net_tx_impl, - .rx_poll = net_rx_poll_impl + .rx_poll = net_rx_poll_impl, + .scan_start = net_scan_start_impl, + .scan_count = net_scan_count_impl, + .scan_result = net_scan_result_impl, + .is_wireless = net_is_wireless_impl }; const dkm_net_api *net_get_api(void) { diff --git a/CODE/UTSM/kernel/net.o b/CODE/UTSM/kernel/net.o index 7953ba8..d54ff30 100644 Binary files a/CODE/UTSM/kernel/net.o and b/CODE/UTSM/kernel/net.o differ diff --git a/CODE/dsk/main.c b/CODE/dsk/main.c index 4dc430c..22d94c7 100644 --- a/CODE/dsk/main.c +++ b/CODE/dsk/main.c @@ -327,48 +327,80 @@ static void dsk_spinner_and_check(u32 *fb_arg, i64 cx, i64 cy, i64 r, i64 thk, logl("[DSK] checking firstInit..."); if(g_block_read){logl("[DSK] have block read, calling dsk_check_firstinit"); int is_first=0; - if(dsk_check_firstinit(&is_first)==0&&is_first){ - logl("[DSK] first init detected; running system initializers"); + if(dsk_check_firstinit(&is_first)==0){ + if(is_first){ + logl("[DSK] first init detected; running system initializers"); - /* DSK owns system component initialization. FirstInit is only user setup. */ - char mn[12]; /* MOUSE ELF */ - mn[0]='M';mn[1]='O';mn[2]='U';mn[3]='S';mn[4]='E';mn[5]=' ';mn[6]=' ';mn[7]=' ';mn[8]='E';mn[9]='L';mn[10]='F'; - u8 *md=0; u32 ms=0; void *me=0; - if(fat32_read_root_file(mn,&md,&ms)==0){ - if(dsk_load_elf(md,ms,&me)==0){ - logl("[DSK] jumping to mouseInit"); - void(*mentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))me; - mentry(ctx); - logl("[DSK] mouseInit returned"); + /* DSK owns system component initialization. FirstInit is only user setup. */ + char mn[12]; /* MOUSE ELF */ + mn[0]='M';mn[1]='O';mn[2]='U';mn[3]='S';mn[4]='E';mn[5]=' ';mn[6]=' ';mn[7]=' ';mn[8]='E';mn[9]='L';mn[10]='F'; + u8 *md=0; u32 ms=0; void *me=0; + if(fat32_read_root_file(mn,&md,&ms)==0){ + if(dsk_load_elf(md,ms,&me)==0){ + logl("[DSK] jumping to mouseInit"); + void(*mentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))me; + mentry(ctx); + logl("[DSK] mouseInit returned"); + } } - } - char nn[12]; /* NETMAN ELF */ - nn[0]='N';nn[1]='E';nn[2]='T';nn[3]='M';nn[4]='A';nn[5]='N';nn[6]=' ';nn[7]=' ';nn[8]='E';nn[9]='L';nn[10]='F'; - u8 *nd=0; u32 ns=0; void *ne=0; - if(fat32_read_root_file(nn,&nd,&ns)==0){ - if(dsk_load_elf(nd,ns,&ne)==0){ - logl("[DSK] jumping to netman"); - void(*nentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))ne; - nentry(ctx); - logl("[DSK] netman returned"); + char nn[12]; /* NETMAN ELF */ + nn[0]='N';nn[1]='E';nn[2]='T';nn[3]='M';nn[4]='A';nn[5]='N';nn[6]=' ';nn[7]=' ';nn[8]='E';nn[9]='L';nn[10]='F'; + u8 *nd=0; u32 ns=0; void *ne=0; + if(fat32_read_root_file(nn,&nd,&ns)==0){ + if(dsk_load_elf(nd,ns,&ne)==0){ + logl("[DSK] jumping to netman"); + void(*nentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))ne; + nentry(ctx); + logl("[DSK] netman returned"); + } } - } - logl("[DSK] loading FirstInit.elf"); - char en[12]; /* FIRSTINIT */ - en[0]='F';en[1]='I';en[2]='R';en[3]='S';en[4]='T'; - en[5]='I';en[6]='N';en[7]='I';en[8]='T';en[9]=' ';en[10]=0; - u8 *fd=0; u32 fs=0; - if(fat32_read_root_file(en,&fd,&fs)==0){ - if(dsk_load_elf(fd,fs,&fe_entry)==0) logl("[DSK] FirstInit.elf loaded"); + logl("[DSK] loading FirstInit.elf"); + char en[12]; /* FIRSTINIT */ + en[0]='F';en[1]='I';en[2]='R';en[3]='S';en[4]='T'; + en[5]='I';en[6]='N';en[7]='I';en[8]='T';en[9]=' ';en[10]=0; + u8 *fd=0; u32 fs=0; + if(fat32_read_root_file(en,&fd,&fs)==0){ + if(dsk_load_elf(fd,fs,&fe_entry)==0) logl("[DSK] FirstInit.elf loaded"); + } + } else { + logl("[DSK] firstInit=1, skipping user setup, loading shell directly"); } } } } if(fe_entry&&frame>150){logl("[DSK] jumping to FirstInit"); /* was 600 */ void(*entry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))fe_entry; - entry(ctx); for(;;)__asm__("hlt"); + entry(ctx); + logl("[DSK] FirstInit returned, loading shell"); + /* FirstInit 返回后加载 shell */ + char sh[12]; /* SHELL ELF */ + sh[0]='S';sh[1]='H';sh[2]='E';sh[3]='L';sh[4]='L';sh[5]=' ';sh[6]=' ';sh[7]=' ';sh[8]='E';sh[9]='L';sh[10]='F'; + u8 *sd=0; u32 ss=0; void *se=0; + if(fat32_read_root_file(sh,&sd,&ss)==0){ + if(dsk_load_elf(sd,ss,&se)==0){ + logl("[DSK] jumping to shell"); + void(*sentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))se; + sentry(ctx); + } + } + for(;;)__asm__("hlt"); + } + /* 非首次启动:直接加载 shell */ + if(checked&&!fe_entry&&frame>150){ + logl("[DSK] loading shell directly (non-first boot)"); + char sh[12]; /* SHELL ELF */ + sh[0]='S';sh[1]='H';sh[2]='E';sh[3]='L';sh[4]='L';sh[5]=' ';sh[6]=' ';sh[7]=' ';sh[8]='E';sh[9]='L';sh[10]='F'; + u8 *sd=0; u32 ss=0; void *se=0; + if(fat32_read_root_file(sh,&sd,&ss)==0){ + if(dsk_load_elf(sd,ss,&se)==0){ + logl("[DSK] jumping to shell"); + void(*sentry)(const dsk_boot_context*)=(void(*)(const dsk_boot_context*))se; + sentry(ctx); + } + } + for(;;)__asm__("hlt"); } if(!g_block_read&&frame>800){logl("[DSK] no block device, skipping firstInit");break;} if(checked&&!fe_entry&&frame>800){logl("[DSK] firstInit not found, continuing normal boot");break;} diff --git a/CODE/dsk/main.o b/CODE/dsk/main.o index 405a8be..9fbeae8 100644 Binary files a/CODE/dsk/main.o and b/CODE/dsk/main.o differ diff --git a/CODE/firstInit/ascii_bitmaps.c b/CODE/firstInit/ascii_bitmaps.c new file mode 100644 index 0000000..c004060 --- /dev/null +++ b/CODE/firstInit/ascii_bitmaps.c @@ -0,0 +1,1908 @@ +/* Auto-generated by render_ascii.py - Consolas 18px grayscale */ +/* 95 glyphs: ASCII 32 (space) .. 126 (~), cell 11x18 */ +/* Do not edit - regenerate via: py render_ascii.py */ + +static const int g_ascii_w = 11; +static const int g_ascii_h = 18; +static const unsigned char g_ascii[95][11*18] = { + /* 'space' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x21' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 253, 216, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 246, 210, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 239, 204, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 232, 198, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 226, 192, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 219, 186, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 212, 180, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 205, 174, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 198, 168, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 191, 162, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 21, 223, 202, 3, 0, 0, 0, 0, + 0, 0, 0, 22, 224, 204, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x22' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 181, 255, 43, 73, 255, 151, 0, 0, 0, + 0, 0, 165, 255, 27, 57, 255, 135, 0, 0, 0, + 0, 0, 149, 255, 11, 41, 255, 119, 0, 0, 0, + 0, 0, 132, 251, 0, 24, 255, 103, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x23' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 245, 83, 0, 105, 226, 0, 0, 0, + 0, 0, 14, 255, 59, 0, 130, 199, 0, 0, 0, + 0, 0, 39, 255, 35, 0, 155, 173, 0, 0, 0, + 44, 255, 255, 255, 255, 255, 255, 255, 255, 132, 0, + 0, 0, 95, 234, 0, 0, 213, 118, 0, 0, 0, + 0, 0, 123, 209, 0, 0, 238, 91, 0, 0, 0, + 0, 0, 150, 184, 0, 7, 255, 65, 0, 0, 0, + 160, 255, 255, 255, 255, 255, 255, 255, 255, 12, 0, + 0, 0, 207, 125, 0, 67, 255, 9, 0, 0, 0, + 0, 0, 235, 97, 0, 95, 237, 0, 0, 0, 0, + 0, 7, 254, 69, 0, 122, 209, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x24' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 219, 108, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 246, 78, 0, 0, 0, 0, + 0, 0, 72, 192, 242, 255, 255, 255, 8, 0, 0, + 0, 99, 254, 109, 83, 253, 5, 0, 0, 0, 0, + 0, 215, 196, 0, 99, 226, 0, 0, 0, 0, 0, + 0, 217, 230, 20, 133, 193, 0, 0, 0, 0, 0, + 0, 75, 249, 235, 220, 164, 0, 0, 0, 0, 0, + 0, 0, 40, 164, 251, 254, 184, 49, 0, 0, 0, + 0, 0, 0, 0, 236, 166, 226, 249, 67, 0, 0, + 0, 0, 0, 11, 255, 55, 19, 240, 193, 0, 0, + 0, 0, 0, 44, 255, 23, 1, 229, 191, 0, 0, + 0, 0, 0, 76, 247, 29, 144, 251, 72, 0, 0, + 8, 255, 255, 255, 254, 227, 173, 58, 0, 0, 0, + 0, 0, 0, 159, 169, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 194, 133, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x25' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 7, 158, 240, 216, 60, 0, 0, 26, 241, 103, 0, + 114, 221, 23, 137, 214, 0, 0, 171, 199, 1, 0, + 162, 166, 0, 71, 251, 1, 74, 251, 46, 0, 0, + 127, 217, 19, 143, 203, 10, 223, 137, 0, 0, 0, + 15, 179, 244, 205, 47, 137, 224, 10, 0, 0, 0, + 0, 0, 0, 0, 45, 251, 75, 0, 0, 0, 0, + 0, 0, 0, 1, 198, 171, 0, 0, 0, 0, 0, + 0, 0, 0, 104, 241, 26, 0, 0, 0, 0, 0, + 0, 0, 23, 239, 108, 62, 212, 242, 164, 5, 0, + 0, 0, 166, 203, 5, 231, 120, 27, 234, 98, 0, + 0, 70, 252, 49, 26, 255, 46, 0, 191, 136, 0, + 8, 220, 142, 0, 4, 242, 112, 30, 237, 87, 0, + 132, 227, 12, 0, 0, 82, 225, 238, 144, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x26' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 54, 198, 243, 208, 72, 0, 0, 0, 0, + 0, 19, 239, 143, 14, 140, 246, 28, 0, 0, 0, + 0, 96, 255, 33, 0, 38, 255, 94, 0, 0, 0, + 0, 116, 255, 44, 0, 65, 255, 83, 0, 0, 0, + 0, 74, 255, 126, 8, 195, 241, 20, 0, 0, 0, + 0, 3, 215, 240, 202, 248, 81, 0, 0, 0, 0, + 0, 2, 161, 255, 248, 49, 0, 0, 0, 0, 0, + 0, 165, 252, 188, 255, 125, 0, 147, 240, 0, 0, + 62, 255, 122, 3, 191, 253, 66, 168, 211, 0, 0, + 115, 255, 35, 0, 23, 230, 232, 231, 150, 0, 0, + 96, 255, 56, 0, 0, 62, 252, 255, 64, 0, 0, + 21, 235, 199, 40, 16, 94, 243, 254, 185, 2, 0, + 0, 44, 180, 235, 246, 197, 71, 128, 255, 129, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x27' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 255, 235, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 245, 219, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 229, 203, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 212, 187, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x28' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 8, 177, 60, 0, 0, 0, + 0, 0, 0, 0, 0, 170, 241, 43, 0, 0, 0, + 0, 0, 0, 0, 108, 254, 72, 0, 0, 0, 0, + 0, 0, 0, 22, 243, 163, 0, 0, 0, 0, 0, + 0, 0, 0, 130, 251, 27, 0, 0, 0, 0, 0, + 0, 0, 0, 215, 186, 0, 0, 0, 0, 0, 0, + 0, 0, 31, 255, 110, 0, 0, 0, 0, 0, 0, + 0, 0, 64, 255, 77, 0, 0, 0, 0, 0, 0, + 0, 0, 85, 255, 58, 0, 0, 0, 0, 0, 0, + 0, 0, 67, 255, 79, 0, 0, 0, 0, 0, 0, + 0, 0, 37, 255, 113, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 224, 190, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 134, 252, 32, 0, 0, 0, 0, 0, + 0, 0, 0, 25, 241, 170, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 109, 255, 79, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 171, 244, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 176, 56, 0, 0, 0, + }, + /* '0x29' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 76, 167, 4, 0, 0, 0, 0, 0, 0, + 0, 0, 69, 251, 153, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 106, 255, 90, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 233, 13, 0, 0, 0, 0, + 0, 0, 0, 0, 56, 255, 115, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 219, 207, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 143, 255, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 109, 255, 52, 0, 0, 0, + 0, 0, 0, 0, 0, 87, 255, 62, 0, 0, 0, + 0, 0, 0, 0, 0, 105, 255, 40, 0, 0, 0, + 0, 0, 0, 0, 0, 137, 253, 11, 0, 0, 0, + 0, 0, 0, 0, 0, 212, 201, 0, 0, 0, 0, + 0, 0, 0, 0, 48, 255, 108, 0, 0, 0, 0, + 0, 0, 0, 0, 189, 229, 9, 0, 0, 0, 0, + 0, 0, 0, 98, 255, 82, 0, 0, 0, 0, 0, + 0, 0, 63, 249, 145, 0, 0, 0, 0, 0, 0, + 0, 0, 82, 162, 2, 0, 0, 0, 0, 0, 0, + }, + /* '0x2a' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 163, 137, 0, 0, 0, 0, 0, + 0, 58, 169, 17, 144, 117, 27, 184, 36, 0, 0, + 0, 34, 167, 226, 182, 173, 235, 165, 30, 0, 0, + 0, 0, 0, 89, 253, 247, 69, 0, 0, 0, 0, + 0, 31, 161, 228, 189, 180, 233, 146, 23, 0, 0, + 0, 67, 169, 18, 146, 118, 30, 186, 37, 0, 0, + 0, 0, 0, 0, 163, 138, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x2b' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 208, 180, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 208, 180, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 208, 180, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 208, 180, 0, 0, 0, 0, 0, + 68, 255, 255, 255, 255, 255, 255, 255, 255, 40, 0, + 0, 0, 0, 0, 208, 180, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 208, 180, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 208, 180, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 208, 180, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x2c' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 42, 231, 182, 2, 0, 0, 0, 0, + 0, 0, 0, 86, 255, 255, 62, 0, 0, 0, 0, + 0, 0, 0, 7, 221, 255, 79, 0, 0, 0, 0, + 0, 0, 0, 0, 168, 252, 27, 0, 0, 0, 0, + 0, 0, 9, 83, 243, 138, 0, 0, 0, 0, 0, + 0, 0, 223, 207, 101, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x2d' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 172, 255, 255, 255, 255, 148, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x2e' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 48, 227, 200, 13, 0, 0, 0, 0, + 0, 0, 0, 136, 255, 255, 73, 0, 0, 0, 0, + 0, 0, 0, 51, 230, 204, 14, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x2f' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 40, 255, 76, 0, 0, + 0, 0, 0, 0, 0, 0, 142, 226, 3, 0, 0, + 0, 0, 0, 0, 0, 8, 237, 126, 0, 0, 0, + 0, 0, 0, 0, 0, 92, 252, 27, 0, 0, 0, + 0, 0, 0, 0, 0, 195, 176, 0, 0, 0, 0, + 0, 0, 0, 0, 42, 255, 73, 0, 0, 0, 0, + 0, 0, 0, 0, 145, 224, 2, 0, 0, 0, 0, + 0, 0, 0, 9, 238, 123, 0, 0, 0, 0, 0, + 0, 0, 0, 95, 251, 25, 0, 0, 0, 0, 0, + 0, 0, 0, 198, 173, 0, 0, 0, 0, 0, 0, + 0, 0, 45, 255, 70, 0, 0, 0, 0, 0, 0, + 0, 0, 148, 222, 1, 0, 0, 0, 0, 0, 0, + 0, 10, 240, 120, 0, 0, 0, 0, 0, 0, 0, + 0, 98, 250, 23, 0, 0, 0, 0, 0, 0, 0, + 0, 201, 171, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 44, 179, 241, 244, 186, 49, 0, 0, 0, + 0, 38, 240, 156, 20, 30, 178, 242, 35, 0, 0, + 0, 172, 220, 3, 0, 0, 13, 241, 160, 0, 0, + 5, 248, 138, 0, 0, 0, 104, 250, 234, 0, 0, + 39, 255, 97, 0, 4, 149, 202, 147, 255, 16, 0, + 52, 255, 85, 19, 190, 164, 8, 112, 255, 28, 0, + 42, 255, 129, 218, 120, 0, 0, 131, 255, 15, 0, + 9, 252, 242, 77, 0, 0, 0, 162, 230, 0, 0, + 0, 189, 214, 2, 0, 0, 10, 235, 148, 0, 0, + 0, 58, 250, 150, 20, 25, 173, 231, 25, 0, 0, + 0, 0, 65, 198, 247, 239, 173, 35, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '1' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 67, 206, 255, 52, 0, 0, 0, 0, + 0, 45, 183, 184, 151, 255, 52, 0, 0, 0, 0, + 0, 130, 117, 1, 128, 255, 52, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 255, 52, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 255, 52, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 255, 52, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 255, 52, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 255, 52, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 255, 52, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 255, 52, 0, 0, 0, 0, + 0, 116, 255, 255, 255, 255, 255, 255, 196, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '2' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 6, 119, 215, 248, 225, 148, 16, 0, 0, 0, + 0, 77, 196, 56, 8, 65, 237, 182, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 139, 255, 27, 0, 0, + 0, 0, 0, 0, 0, 0, 113, 255, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 179, 246, 13, 0, 0, + 0, 0, 0, 0, 0, 94, 255, 140, 0, 0, 0, + 0, 0, 0, 0, 74, 250, 201, 7, 0, 0, 0, + 0, 0, 0, 67, 247, 204, 17, 0, 0, 0, 0, + 0, 0, 59, 244, 198, 15, 0, 0, 0, 0, 0, + 0, 52, 241, 192, 12, 0, 0, 0, 0, 0, 0, + 0, 179, 255, 255, 255, 255, 255, 255, 220, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '3' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 64, 255, 255, 250, 222, 146, 19, 0, 0, 0, + 0, 0, 0, 0, 8, 66, 238, 185, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 148, 253, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 165, 242, 2, 0, 0, + 0, 0, 0, 0, 12, 94, 250, 103, 0, 0, 0, + 0, 0, 40, 255, 255, 255, 187, 36, 0, 0, 0, + 0, 0, 0, 0, 8, 49, 179, 246, 49, 0, 0, + 0, 0, 0, 0, 0, 0, 13, 253, 146, 0, 0, + 0, 0, 0, 0, 0, 0, 31, 255, 127, 0, 0, + 0, 0, 0, 0, 10, 64, 202, 221, 20, 0, 0, + 0, 144, 255, 255, 244, 208, 131, 17, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '4' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 23, 239, 255, 120, 0, 0, 0, + 0, 0, 0, 0, 164, 234, 255, 120, 0, 0, 0, + 0, 0, 0, 65, 255, 96, 255, 120, 0, 0, 0, + 0, 0, 6, 215, 170, 24, 255, 120, 0, 0, 0, + 0, 0, 123, 242, 27, 24, 255, 120, 0, 0, 0, + 0, 33, 246, 112, 0, 24, 255, 120, 0, 0, 0, + 0, 180, 207, 3, 0, 24, 255, 120, 0, 0, 0, + 82, 254, 56, 0, 0, 24, 255, 120, 0, 0, 0, + 160, 255, 255, 255, 255, 255, 255, 255, 255, 112, 0, + 0, 0, 0, 0, 0, 24, 255, 120, 0, 0, 0, + 0, 0, 0, 0, 0, 24, 255, 120, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '5' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 255, 255, 255, 255, 244, 0, 0, 0, + 0, 60, 255, 52, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 52, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 52, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 255, 247, 222, 150, 36, 0, 0, 0, + 0, 0, 0, 0, 16, 68, 215, 238, 28, 0, 0, + 0, 0, 0, 0, 0, 0, 63, 255, 118, 0, 0, + 0, 0, 0, 0, 0, 0, 34, 255, 133, 0, 0, + 0, 0, 0, 0, 0, 0, 102, 255, 71, 0, 0, + 0, 0, 0, 0, 23, 100, 241, 158, 0, 0, 0, + 0, 112, 255, 255, 241, 189, 75, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '6' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 42, 151, 223, 249, 255, 28, 0, 0, + 0, 0, 88, 245, 148, 40, 6, 0, 0, 0, 0, + 0, 32, 248, 112, 0, 0, 0, 0, 0, 0, 0, + 0, 137, 236, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 198, 190, 121, 222, 247, 214, 116, 3, 0, 0, + 0, 224, 245, 121, 27, 15, 105, 255, 122, 0, 0, + 0, 231, 172, 0, 0, 0, 0, 191, 221, 0, 0, + 0, 210, 193, 0, 0, 0, 0, 161, 247, 0, 0, + 0, 153, 243, 10, 0, 0, 0, 208, 198, 0, 0, + 0, 40, 247, 168, 27, 18, 134, 251, 53, 0, 0, + 0, 0, 59, 194, 246, 244, 186, 49, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '7' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 248, 255, 255, 255, 255, 255, 255, 204, 0, 0, + 0, 0, 0, 0, 0, 0, 18, 242, 141, 0, 0, + 0, 0, 0, 0, 0, 0, 133, 248, 26, 0, 0, + 0, 0, 0, 0, 0, 18, 242, 151, 0, 0, 0, + 0, 0, 0, 0, 0, 133, 251, 33, 0, 0, 0, + 0, 0, 0, 0, 18, 242, 161, 0, 0, 0, 0, + 0, 0, 0, 0, 133, 253, 40, 0, 0, 0, 0, + 0, 0, 0, 18, 242, 170, 0, 0, 0, 0, 0, + 0, 0, 0, 133, 255, 48, 0, 0, 0, 0, 0, + 0, 0, 18, 242, 180, 0, 0, 0, 0, 0, 0, + 0, 0, 132, 255, 57, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '8' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 74, 194, 237, 240, 201, 89, 0, 0, 0, + 0, 71, 254, 113, 14, 18, 136, 255, 72, 0, 0, + 0, 162, 232, 0, 0, 0, 5, 252, 142, 0, 0, + 0, 143, 251, 35, 0, 0, 46, 255, 107, 0, 0, + 0, 27, 223, 229, 79, 63, 225, 189, 8, 0, 0, + 0, 0, 26, 225, 255, 255, 212, 17, 0, 0, 0, + 0, 38, 221, 205, 51, 88, 230, 224, 36, 0, 0, + 0, 188, 231, 12, 0, 0, 31, 246, 173, 0, 0, + 0, 231, 196, 0, 0, 0, 0, 219, 209, 0, 0, + 0, 158, 252, 100, 14, 16, 114, 254, 113, 0, 0, + 0, 8, 127, 214, 244, 239, 199, 93, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '9' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 64, 196, 247, 239, 170, 31, 0, 0, 0, + 0, 83, 254, 111, 15, 39, 191, 223, 12, 0, 0, + 2, 230, 180, 0, 0, 0, 37, 255, 106, 0, 0, + 24, 255, 133, 0, 0, 0, 0, 228, 171, 0, 0, + 4, 249, 160, 0, 0, 0, 0, 205, 196, 0, 0, + 0, 154, 247, 81, 11, 34, 136, 251, 194, 0, 0, + 0, 9, 135, 220, 249, 216, 106, 216, 169, 0, 0, + 0, 0, 0, 0, 0, 0, 11, 248, 108, 0, 0, + 0, 0, 0, 0, 0, 0, 125, 236, 15, 0, 0, + 0, 0, 0, 12, 54, 154, 238, 64, 0, 0, 0, + 0, 108, 254, 241, 207, 138, 28, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x3a' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 23, 216, 200, 10, 0, 0, 0, 0, + 0, 0, 0, 89, 255, 255, 63, 0, 0, 0, 0, + 0, 0, 0, 25, 218, 203, 11, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 23, 216, 200, 10, 0, 0, 0, 0, + 0, 0, 0, 89, 255, 255, 63, 0, 0, 0, 0, + 0, 0, 0, 25, 218, 203, 11, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x3b' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 23, 216, 200, 10, 0, 0, 0, 0, + 0, 0, 0, 89, 255, 255, 63, 0, 0, 0, 0, + 0, 0, 0, 25, 218, 203, 11, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 224, 198, 10, 0, 0, 0, 0, + 0, 0, 0, 62, 255, 255, 86, 0, 0, 0, 0, + 0, 0, 0, 1, 199, 255, 103, 0, 0, 0, 0, + 0, 0, 0, 0, 141, 255, 47, 0, 0, 0, 0, + 0, 0, 7, 70, 234, 162, 0, 0, 0, 0, 0, + 0, 0, 197, 213, 115, 3, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x3c' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 123, 149, 0, 0, 0, + 0, 0, 0, 0, 6, 162, 253, 117, 0, 0, 0, + 0, 0, 0, 20, 195, 243, 77, 0, 0, 0, 0, + 0, 0, 41, 221, 224, 45, 0, 0, 0, 0, 0, + 0, 68, 239, 198, 22, 0, 0, 0, 0, 0, 0, + 0, 69, 240, 202, 25, 0, 0, 0, 0, 0, 0, + 0, 0, 41, 221, 227, 48, 0, 0, 0, 0, 0, + 0, 0, 0, 20, 195, 244, 79, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 162, 253, 117, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 124, 150, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x3d' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 212, 255, 255, 255, 255, 255, 255, 188, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 212, 255, 255, 255, 255, 255, 255, 188, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x3e' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 167, 102, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 138, 255, 141, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 98, 250, 177, 11, 0, 0, 0, 0, + 0, 0, 0, 0, 63, 237, 206, 27, 0, 0, 0, + 0, 0, 0, 0, 0, 36, 216, 229, 51, 0, 0, + 0, 0, 0, 0, 0, 32, 211, 229, 51, 0, 0, + 0, 0, 0, 0, 59, 234, 207, 28, 0, 0, 0, + 0, 0, 0, 95, 249, 178, 11, 0, 0, 0, 0, + 0, 2, 136, 255, 142, 2, 0, 0, 0, 0, 0, + 0, 3, 167, 103, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x3f' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 87, 239, 204, 120, 11, 0, 0, 0, 0, + 0, 0, 0, 25, 90, 226, 219, 26, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 245, 181, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 170, 254, 10, 0, 0, + 0, 0, 0, 0, 0, 0, 153, 253, 8, 0, 0, + 0, 0, 0, 0, 2, 47, 229, 185, 0, 0, 0, + 0, 0, 0, 117, 255, 231, 159, 22, 0, 0, 0, + 0, 0, 0, 110, 255, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 103, 251, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 96, 246, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 157, 239, 57, 0, 0, 0, 0, 0, + 0, 0, 0, 160, 239, 59, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x40' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 63, 195, 247, 227, 126, 3, 0, 0, + 0, 0, 88, 239, 93, 10, 50, 216, 146, 0, 0, + 0, 35, 246, 79, 0, 0, 0, 52, 252, 30, 0, + 0, 156, 179, 0, 0, 0, 0, 0, 215, 106, 0, + 16, 245, 71, 0, 0, 0, 0, 0, 163, 155, 0, + 85, 236, 3, 25, 188, 244, 214, 118, 136, 184, 0, + 142, 183, 0, 182, 168, 43, 255, 87, 124, 196, 0, + 189, 135, 31, 255, 55, 53, 255, 51, 122, 193, 0, + 208, 112, 93, 252, 5, 87, 255, 15, 132, 179, 0, + 224, 96, 126, 231, 0, 121, 234, 0, 156, 150, 0, + 226, 92, 130, 226, 0, 159, 205, 0, 199, 99, 0, + 212, 105, 101, 245, 38, 229, 217, 45, 243, 21, 0, + 180, 139, 18, 205, 232, 70, 210, 237, 92, 0, 0, + 126, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 41, 254, 55, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 226, 66, 8, 39, 125, 102, 0, 0, 0, + 0, 4, 124, 222, 249, 225, 161, 41, 0, 0, 0, + }, + /* 'A' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 69, 246, 255, 55, 0, 0, 0, 0, + 0, 0, 0, 157, 165, 214, 143, 0, 0, 0, 0, + 0, 0, 6, 239, 87, 135, 228, 1, 0, 0, 0, + 0, 0, 79, 250, 15, 56, 255, 62, 0, 0, 0, + 0, 0, 167, 187, 0, 1, 231, 150, 0, 0, 0, + 0, 11, 244, 109, 0, 0, 154, 234, 3, 0, 0, + 0, 89, 255, 31, 0, 0, 75, 255, 69, 0, 0, + 0, 178, 255, 255, 255, 255, 255, 255, 157, 0, 0, + 16, 249, 97, 0, 0, 0, 0, 146, 238, 5, 0, + 99, 255, 29, 0, 0, 0, 0, 76, 255, 76, 0, + 188, 217, 0, 0, 0, 0, 0, 12, 250, 164, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'B' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 156, 255, 255, 254, 233, 189, 69, 0, 0, 0, + 0, 156, 244, 0, 3, 37, 178, 250, 46, 0, 0, + 0, 156, 244, 0, 0, 0, 44, 255, 115, 0, 0, + 0, 156, 244, 0, 0, 0, 59, 255, 102, 0, 0, + 0, 156, 244, 0, 3, 45, 201, 214, 14, 0, 0, + 0, 156, 255, 255, 255, 255, 241, 90, 0, 0, 0, + 0, 156, 244, 0, 3, 34, 146, 255, 123, 0, 0, + 0, 156, 244, 0, 0, 0, 0, 213, 222, 0, 0, + 0, 156, 244, 0, 0, 0, 0, 216, 213, 0, 0, + 0, 156, 244, 0, 1, 26, 132, 254, 93, 0, 0, + 0, 156, 255, 255, 251, 226, 175, 63, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'C' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 4, 110, 205, 245, 231, 177, 49, 0, 0, + 0, 3, 183, 236, 92, 14, 12, 82, 151, 0, 0, + 0, 116, 255, 69, 0, 0, 0, 0, 0, 0, 0, + 0, 222, 203, 0, 0, 0, 0, 0, 0, 0, 0, + 23, 255, 153, 0, 0, 0, 0, 0, 0, 0, 0, + 43, 255, 125, 0, 0, 0, 0, 0, 0, 0, 0, + 34, 255, 136, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 243, 186, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 160, 251, 42, 0, 0, 0, 0, 0, 0, 0, + 0, 23, 223, 225, 79, 12, 12, 83, 151, 0, 0, + 0, 0, 20, 141, 219, 249, 228, 165, 38, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'D' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 255, 255, 255, 245, 214, 141, 23, 0, 0, 0, + 12, 255, 132, 0, 24, 69, 204, 230, 31, 0, 0, + 12, 255, 132, 0, 0, 0, 19, 238, 175, 0, 0, + 12, 255, 132, 0, 0, 0, 0, 158, 252, 10, 0, + 12, 255, 132, 0, 0, 0, 0, 115, 255, 48, 0, + 12, 255, 132, 0, 0, 0, 0, 103, 255, 58, 0, + 12, 255, 132, 0, 0, 0, 0, 118, 255, 39, 0, + 12, 255, 132, 0, 0, 0, 0, 171, 237, 3, 0, + 12, 255, 132, 0, 0, 0, 40, 249, 137, 0, 0, + 12, 255, 132, 0, 19, 90, 229, 190, 10, 0, 0, + 12, 255, 255, 251, 230, 191, 99, 3, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'E' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 255, 255, 255, 255, 255, 76, 0, 0, + 0, 60, 255, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 255, 255, 255, 255, 255, 28, 0, 0, + 0, 60, 255, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 255, 255, 255, 255, 255, 76, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'F' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 255, 255, 255, 255, 255, 255, 72, 0, 0, + 0, 52, 255, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 255, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 255, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 255, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 255, 255, 255, 255, 255, 255, 8, 0, 0, + 0, 52, 255, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 255, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 255, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 255, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 255, 104, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'G' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 9, 116, 204, 244, 235, 183, 60, 0, 0, + 0, 16, 205, 225, 84, 14, 11, 75, 165, 0, 0, + 0, 163, 241, 31, 0, 0, 0, 0, 0, 0, 0, + 24, 253, 141, 0, 0, 0, 0, 0, 0, 0, 0, + 82, 255, 90, 0, 0, 0, 0, 0, 0, 0, 0, + 103, 255, 63, 0, 0, 255, 255, 255, 228, 0, 0, + 91, 255, 82, 0, 0, 0, 0, 168, 228, 0, 0, + 44, 255, 126, 0, 0, 0, 0, 168, 228, 0, 0, + 0, 205, 230, 9, 0, 0, 0, 168, 228, 0, 0, + 0, 47, 239, 196, 58, 8, 13, 184, 228, 0, 0, + 0, 0, 33, 156, 225, 251, 243, 203, 119, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'H' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 255, 140, 0, 0, 0, 0, 164, 240, 0, 0, + 8, 255, 140, 0, 0, 0, 0, 164, 240, 0, 0, + 8, 255, 140, 0, 0, 0, 0, 164, 240, 0, 0, + 8, 255, 140, 0, 0, 0, 0, 164, 240, 0, 0, + 8, 255, 140, 0, 0, 0, 0, 164, 240, 0, 0, + 8, 255, 255, 255, 255, 255, 255, 255, 240, 0, 0, + 8, 255, 140, 0, 0, 0, 0, 164, 240, 0, 0, + 8, 255, 140, 0, 0, 0, 0, 164, 240, 0, 0, + 8, 255, 140, 0, 0, 0, 0, 164, 240, 0, 0, + 8, 255, 140, 0, 0, 0, 0, 164, 240, 0, 0, + 8, 255, 140, 0, 0, 0, 0, 164, 240, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'I' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 124, 255, 255, 255, 255, 255, 255, 100, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 124, 255, 255, 255, 255, 255, 255, 100, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'J' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 64, 255, 255, 255, 255, 255, 180, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 224, 180, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 224, 180, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 224, 180, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 224, 180, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 224, 180, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 224, 180, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 225, 175, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 249, 139, 0, 0, 0, + 0, 90, 115, 21, 17, 150, 247, 36, 0, 0, 0, + 0, 17, 153, 235, 247, 196, 52, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'K' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 160, 240, 0, 0, 0, 63, 252, 149, 0, 0, + 0, 160, 240, 0, 0, 21, 229, 188, 4, 0, 0, + 0, 160, 240, 0, 1, 185, 219, 18, 0, 0, 0, + 0, 160, 240, 0, 124, 240, 40, 0, 0, 0, 0, + 0, 160, 240, 63, 249, 72, 0, 0, 0, 0, 0, + 0, 160, 241, 219, 174, 0, 0, 0, 0, 0, 0, + 0, 160, 240, 91, 254, 90, 0, 0, 0, 0, 0, + 0, 160, 240, 0, 143, 248, 60, 0, 0, 0, 0, + 0, 160, 240, 0, 4, 190, 235, 35, 0, 0, 0, + 0, 160, 240, 0, 0, 21, 225, 216, 17, 0, 0, + 0, 160, 240, 0, 0, 0, 51, 247, 191, 6, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'L' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 164, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 164, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 164, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 164, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 164, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 164, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 164, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 164, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 164, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 164, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 244, 255, 255, 255, 255, 255, 168, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'M' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 255, 246, 13, 0, 0, 47, 255, 238, 0, 0, + 20, 255, 236, 95, 0, 0, 143, 232, 251, 0, 0, + 33, 255, 132, 186, 0, 4, 229, 110, 255, 8, 0, + 46, 255, 38, 242, 23, 77, 178, 72, 255, 21, 0, + 59, 255, 18, 164, 110, 170, 81, 64, 255, 34, 0, + 72, 255, 11, 68, 204, 223, 5, 57, 255, 47, 0, + 85, 255, 5, 2, 225, 144, 0, 50, 255, 60, 0, + 98, 254, 0, 0, 0, 0, 0, 42, 255, 73, 0, + 111, 248, 0, 0, 0, 0, 0, 35, 255, 86, 0, + 124, 241, 0, 0, 0, 0, 0, 27, 255, 99, 0, + 137, 235, 0, 0, 0, 0, 0, 20, 255, 113, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'N' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 244, 255, 72, 0, 0, 0, 152, 220, 0, 0, + 0, 244, 206, 185, 0, 0, 0, 152, 220, 0, 0, + 0, 244, 131, 227, 43, 0, 0, 152, 220, 0, 0, + 0, 244, 128, 128, 156, 0, 0, 152, 220, 0, 0, + 0, 244, 128, 23, 240, 21, 0, 152, 220, 0, 0, + 0, 244, 128, 0, 158, 126, 0, 152, 220, 0, 0, + 0, 244, 128, 0, 44, 231, 7, 152, 220, 0, 0, + 0, 244, 128, 0, 0, 186, 95, 152, 220, 0, 0, + 0, 244, 128, 0, 0, 73, 201, 152, 220, 0, 0, + 0, 244, 128, 0, 0, 1, 213, 202, 220, 0, 0, + 0, 244, 128, 0, 0, 0, 101, 255, 220, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'O' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 58, 185, 243, 244, 196, 72, 0, 0, 0, + 0, 71, 250, 136, 17, 21, 145, 254, 82, 0, 0, + 3, 222, 190, 0, 0, 0, 0, 205, 226, 2, 0, + 61, 255, 96, 0, 0, 0, 0, 117, 255, 50, 0, + 109, 255, 53, 0, 0, 0, 0, 79, 255, 89, 0, + 124, 255, 37, 0, 0, 0, 0, 62, 255, 99, 0, + 113, 255, 54, 0, 0, 0, 0, 81, 255, 83, 0, + 75, 255, 91, 0, 0, 0, 0, 124, 255, 33, 0, + 11, 242, 180, 0, 0, 0, 5, 216, 197, 0, 0, + 0, 109, 255, 124, 15, 22, 166, 243, 47, 0, 0, + 0, 0, 92, 205, 247, 239, 175, 44, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'P' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 156, 255, 255, 253, 230, 179, 66, 0, 0, 0, + 0, 156, 244, 0, 3, 37, 167, 252, 78, 0, 0, + 0, 156, 244, 0, 0, 0, 4, 227, 207, 0, 0, + 0, 156, 244, 0, 0, 0, 0, 191, 232, 0, 0, + 0, 156, 244, 0, 0, 0, 7, 231, 181, 0, 0, + 0, 156, 244, 0, 5, 42, 173, 242, 46, 0, 0, + 0, 156, 255, 255, 252, 230, 162, 37, 0, 0, 0, + 0, 156, 244, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 156, 244, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 156, 244, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 156, 244, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'Q' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 54, 182, 242, 244, 196, 72, 0, 0, 0, + 0, 65, 249, 136, 17, 22, 148, 254, 82, 0, 0, + 1, 218, 190, 0, 0, 0, 0, 206, 226, 2, 0, + 57, 255, 94, 0, 0, 0, 0, 117, 255, 51, 0, + 106, 255, 53, 0, 0, 0, 0, 79, 255, 90, 0, + 122, 255, 37, 0, 0, 0, 0, 62, 255, 99, 0, + 105, 255, 54, 0, 0, 0, 0, 80, 255, 84, 0, + 75, 255, 92, 0, 0, 0, 0, 123, 255, 37, 0, + 9, 238, 180, 0, 0, 0, 4, 215, 203, 0, 0, + 0, 121, 255, 124, 15, 22, 165, 247, 55, 0, 0, + 0, 0, 105, 218, 255, 250, 186, 52, 0, 0, 0, + 0, 0, 0, 0, 198, 178, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 106, 253, 88, 13, 92, 133, 0, + 0, 0, 0, 0, 0, 116, 220, 245, 204, 77, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'R' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 255, 255, 254, 237, 178, 46, 0, 0, 0, + 0, 128, 255, 16, 3, 47, 209, 231, 18, 0, 0, + 0, 128, 255, 16, 0, 0, 87, 255, 77, 0, 0, + 0, 128, 255, 16, 0, 0, 98, 255, 68, 0, 0, + 0, 128, 255, 16, 7, 62, 225, 203, 3, 0, 0, + 0, 128, 255, 255, 255, 254, 138, 12, 0, 0, 0, + 0, 128, 255, 16, 36, 210, 226, 27, 0, 0, 0, + 0, 128, 255, 16, 0, 55, 255, 165, 0, 0, 0, + 0, 128, 255, 16, 0, 0, 186, 251, 29, 0, 0, + 0, 128, 255, 16, 0, 0, 64, 255, 141, 0, 0, + 0, 128, 255, 16, 0, 0, 0, 197, 242, 15, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'S' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 78, 189, 240, 248, 194, 77, 0, 0, 0, + 0, 97, 255, 123, 24, 7, 58, 183, 7, 0, 0, + 0, 201, 211, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 196, 243, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 68, 248, 239, 133, 33, 0, 0, 0, 0, 0, + 0, 0, 38, 162, 248, 254, 181, 46, 0, 0, 0, + 0, 0, 0, 0, 16, 110, 232, 249, 59, 0, 0, + 0, 0, 0, 0, 0, 0, 28, 246, 178, 0, 0, + 0, 0, 0, 0, 0, 0, 3, 244, 174, 0, 0, + 7, 187, 70, 15, 6, 35, 160, 251, 66, 0, 0, + 0, 77, 182, 238, 247, 223, 164, 54, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'T' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 64, 255, 255, 255, 255, 255, 255, 255, 255, 36, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'U' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 12, 255, 132, 0, 0, 0, 0, 156, 244, 0, 0, + 12, 255, 132, 0, 0, 0, 0, 156, 244, 0, 0, + 12, 255, 132, 0, 0, 0, 0, 156, 244, 0, 0, + 12, 255, 132, 0, 0, 0, 0, 156, 244, 0, 0, + 12, 255, 132, 0, 0, 0, 0, 156, 244, 0, 0, + 12, 255, 132, 0, 0, 0, 0, 156, 244, 0, 0, + 12, 255, 132, 0, 0, 0, 0, 156, 244, 0, 0, + 5, 253, 137, 0, 0, 0, 0, 163, 235, 0, 0, + 0, 222, 175, 0, 0, 0, 0, 209, 186, 0, 0, + 0, 109, 253, 100, 17, 21, 130, 251, 62, 0, 0, + 0, 1, 111, 213, 249, 243, 186, 49, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'V' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 204, 239, 6, 0, 0, 0, 0, 8, 242, 174, 0, + 115, 255, 76, 0, 0, 0, 0, 81, 255, 83, 0, + 28, 254, 162, 0, 0, 0, 0, 167, 240, 8, 0, + 0, 194, 240, 6, 0, 0, 8, 243, 157, 0, 0, + 0, 105, 255, 77, 0, 0, 82, 255, 66, 0, 0, + 0, 20, 251, 163, 0, 0, 168, 229, 2, 0, 0, + 0, 0, 183, 241, 7, 9, 243, 140, 0, 0, 0, + 0, 0, 95, 255, 77, 82, 255, 49, 0, 0, 0, + 0, 0, 14, 247, 160, 167, 214, 0, 0, 0, 0, + 0, 0, 0, 173, 238, 243, 123, 0, 0, 0, 0, + 0, 0, 0, 84, 255, 255, 33, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'W' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 147, 230, 0, 0, 0, 0, 0, 0, 235, 118, 0, + 128, 245, 0, 0, 0, 0, 0, 0, 248, 101, 0, + 110, 255, 3, 0, 0, 0, 0, 5, 255, 83, 0, + 91, 255, 17, 0, 0, 0, 0, 19, 255, 65, 0, + 73, 255, 31, 0, 197, 163, 0, 33, 255, 47, 0, + 54, 255, 45, 23, 237, 242, 8, 46, 255, 29, 0, + 36, 255, 59, 102, 153, 215, 82, 60, 255, 12, 0, + 17, 255, 73, 182, 75, 131, 170, 73, 249, 0, 0, + 1, 253, 109, 233, 6, 42, 245, 101, 232, 0, 0, + 0, 236, 230, 162, 0, 0, 209, 227, 214, 0, 0, + 0, 217, 255, 78, 0, 0, 120, 255, 196, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'X' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 55, 252, 175, 0, 0, 0, 1, 189, 241, 29, 0, + 0, 140, 255, 104, 0, 0, 108, 255, 101, 0, 0, + 0, 9, 218, 246, 43, 35, 245, 187, 0, 0, 0, + 0, 0, 60, 254, 210, 196, 242, 31, 0, 0, 0, + 0, 0, 0, 146, 255, 255, 104, 0, 0, 0, 0, + 0, 0, 0, 51, 255, 255, 45, 0, 0, 0, 0, + 0, 0, 3, 200, 247, 254, 200, 2, 0, 0, 0, + 0, 0, 122, 255, 111, 153, 255, 112, 0, 0, 0, + 0, 45, 249, 195, 2, 14, 226, 244, 32, 0, 0, + 5, 206, 246, 36, 0, 0, 72, 255, 184, 0, 0, + 129, 255, 113, 0, 0, 0, 0, 160, 255, 93, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'Y' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 180, 253, 47, 0, 0, 0, 0, 46, 253, 151, 0, + 36, 248, 190, 0, 0, 0, 0, 185, 237, 19, 0, + 0, 133, 255, 80, 0, 0, 72, 255, 105, 0, 0, + 0, 10, 226, 219, 6, 3, 211, 207, 2, 0, 0, + 0, 0, 85, 255, 110, 99, 255, 59, 0, 0, 0, + 0, 0, 0, 188, 233, 228, 163, 0, 0, 0, 0, + 0, 0, 0, 41, 250, 242, 25, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 216, 188, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'Z' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 220, 255, 255, 255, 255, 255, 255, 167, 0, 0, + 0, 0, 0, 0, 0, 0, 110, 245, 30, 0, 0, + 0, 0, 0, 0, 0, 23, 240, 120, 0, 0, 0, + 0, 0, 0, 0, 0, 163, 216, 6, 0, 0, 0, + 0, 0, 0, 0, 62, 254, 67, 0, 0, 0, 0, + 0, 0, 0, 4, 211, 168, 0, 0, 0, 0, 0, + 0, 0, 0, 114, 243, 27, 0, 0, 0, 0, 0, + 0, 0, 26, 242, 114, 0, 0, 0, 0, 0, 0, + 0, 0, 168, 211, 4, 0, 0, 0, 0, 0, 0, + 0, 66, 254, 61, 0, 0, 0, 0, 0, 0, 0, + 1, 215, 255, 255, 255, 255, 255, 255, 232, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x5b' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 255, 255, 255, 92, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 124, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 255, 255, 255, 92, 0, 0, 0, + }, + /* '0x5c' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 101, 249, 21, 0, 0, 0, 0, 0, 0, 0, + 0, 12, 242, 117, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 151, 219, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 48, 255, 67, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 201, 170, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 98, 250, 23, 0, 0, 0, 0, 0, + 0, 0, 0, 10, 240, 120, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 148, 221, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 45, 255, 70, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 198, 173, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 95, 251, 25, 0, 0, 0, + 0, 0, 0, 0, 0, 9, 239, 123, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 145, 224, 2, 0, 0, + 0, 0, 0, 0, 0, 0, 42, 255, 73, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 195, 176, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x5d' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 120, 255, 255, 255, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 220, 0, 0, 0, 0, + 0, 0, 120, 255, 255, 255, 220, 0, 0, 0, 0, + }, + /* '0x5e' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 15, 237, 205, 1, 0, 0, 0, 0, + 0, 0, 0, 134, 201, 244, 95, 0, 0, 0, 0, + 0, 0, 25, 245, 59, 129, 228, 10, 0, 0, 0, + 0, 0, 153, 185, 0, 13, 233, 127, 0, 0, 0, + 0, 38, 251, 55, 0, 0, 108, 244, 26, 0, 0, + 0, 171, 181, 0, 0, 0, 5, 220, 159, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x5f' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 228, 0, + }, + /* '0x60' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 20, 176, 248, 78, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 103, 240, 91, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'a' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 81, 187, 241, 239, 191, 63, 0, 0, 0, + 0, 23, 181, 64, 9, 28, 182, 244, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 48, 255, 96, 0, 0, + 0, 0, 0, 0, 0, 0, 24, 255, 116, 0, 0, + 0, 1, 111, 208, 240, 255, 255, 255, 116, 0, 0, + 0, 122, 254, 100, 15, 0, 24, 255, 116, 0, 0, + 0, 197, 212, 0, 0, 0, 45, 255, 116, 0, 0, + 0, 163, 244, 52, 12, 95, 229, 253, 116, 0, 0, + 0, 24, 175, 239, 244, 184, 56, 239, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'b' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 239, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 231, 37, 179, 244, 233, 118, 0, 0, 0, + 0, 152, 239, 225, 102, 15, 114, 255, 73, 0, 0, + 0, 152, 255, 87, 0, 0, 0, 223, 176, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 174, 218, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 160, 237, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 181, 213, 0, 0, + 0, 152, 240, 0, 0, 0, 13, 241, 139, 0, 0, + 0, 152, 246, 57, 9, 37, 186, 226, 21, 0, 0, + 0, 70, 180, 231, 250, 233, 159, 27, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'c' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 97, 205, 247, 224, 141, 7, 0, 0, + 0, 0, 128, 250, 117, 22, 33, 121, 61, 0, 0, + 0, 40, 250, 133, 0, 0, 0, 0, 0, 0, 0, + 0, 124, 255, 35, 0, 0, 0, 0, 0, 0, 0, + 0, 151, 255, 5, 0, 0, 0, 0, 0, 0, 0, + 0, 136, 255, 26, 0, 0, 0, 0, 0, 0, 0, + 0, 76, 255, 106, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 193, 241, 95, 19, 30, 127, 62, 0, 0, + 0, 0, 10, 135, 220, 249, 221, 132, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'd' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 24, 255, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 24, 255, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 24, 255, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 24, 255, 116, 0, 0, + 0, 0, 37, 168, 236, 249, 225, 255, 116, 0, 0, + 0, 35, 238, 169, 34, 11, 79, 255, 116, 0, 0, + 0, 166, 229, 6, 0, 0, 24, 255, 116, 0, 0, + 0, 239, 162, 0, 0, 0, 24, 255, 116, 0, 0, + 6, 255, 138, 0, 0, 0, 24, 255, 116, 0, 0, + 0, 245, 150, 0, 0, 0, 27, 255, 116, 0, 0, + 0, 205, 196, 0, 0, 0, 156, 255, 116, 0, 0, + 0, 102, 254, 90, 17, 142, 192, 247, 116, 0, 0, + 0, 0, 126, 238, 239, 153, 12, 239, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'e' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 32, 169, 240, 245, 187, 43, 0, 0, 0, + 0, 17, 230, 177, 28, 31, 171, 246, 33, 0, 0, + 0, 138, 240, 15, 0, 0, 12, 240, 152, 0, 0, + 0, 221, 183, 0, 0, 0, 0, 200, 198, 0, 0, + 0, 241, 255, 255, 255, 255, 255, 255, 216, 0, 0, + 0, 215, 178, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 169, 233, 11, 0, 0, 0, 0, 0, 0, 0, + 0, 50, 249, 183, 46, 5, 0, 0, 0, 0, 0, + 0, 0, 54, 182, 238, 254, 255, 255, 100, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'f' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 43, 178, 231, 255, 255, 60, 0, + 0, 0, 0, 16, 230, 189, 32, 0, 0, 0, 0, + 0, 0, 0, 82, 255, 55, 0, 0, 0, 0, 0, + 0, 0, 0, 113, 255, 25, 0, 0, 0, 0, 0, + 0, 0, 0, 116, 255, 24, 0, 0, 0, 0, 0, + 76, 255, 255, 255, 255, 255, 255, 255, 248, 0, 0, + 0, 0, 0, 116, 255, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 116, 255, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 116, 255, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 116, 255, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 116, 255, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 116, 255, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 116, 255, 24, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'g' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 62, 194, 242, 255, 255, 255, 255, 44, 0, + 0, 44, 248, 153, 19, 42, 214, 238, 10, 0, 0, + 0, 132, 251, 9, 0, 0, 92, 255, 55, 0, 0, + 0, 146, 251, 8, 0, 0, 93, 255, 44, 0, 0, + 0, 54, 253, 150, 17, 42, 215, 203, 2, 0, 0, + 0, 82, 247, 197, 246, 230, 159, 24, 0, 0, 0, + 0, 194, 174, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 181, 222, 48, 20, 10, 0, 0, 0, 0, 0, + 0, 68, 255, 236, 234, 243, 244, 191, 55, 0, 0, + 4, 222, 206, 9, 0, 0, 24, 198, 238, 4, 0, + 34, 255, 126, 0, 0, 0, 0, 147, 255, 22, 0, + 3, 232, 212, 52, 8, 19, 90, 244, 175, 0, 0, + 0, 41, 170, 232, 252, 246, 210, 122, 7, 0, 0, + }, + /* 'h' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 235, 36, 184, 247, 221, 94, 0, 0, 0, + 0, 152, 243, 222, 88, 12, 138, 252, 39, 0, 0, + 0, 152, 253, 75, 0, 0, 26, 255, 103, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 127, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'i' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 24, 223, 211, 11, 0, 0, 0, 0, + 0, 0, 0, 24, 224, 211, 11, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 255, 255, 255, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 124, 255, 255, 255, 255, 255, 255, 152, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'j' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 123, 245, 104, 0, 0, 0, + 0, 0, 0, 0, 0, 124, 245, 105, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 124, 255, 255, 255, 255, 255, 100, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 255, 100, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 255, 100, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 255, 100, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 255, 100, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 255, 100, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 255, 100, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 255, 100, 0, 0, 0, + 0, 0, 0, 0, 0, 40, 255, 100, 0, 0, 0, + 0, 0, 0, 0, 0, 58, 255, 89, 0, 0, 0, + 0, 0, 0, 0, 0, 120, 255, 41, 0, 0, 0, + 0, 151, 59, 8, 66, 238, 164, 0, 0, 0, 0, + 0, 60, 203, 250, 228, 131, 7, 0, 0, 0, 0, + }, + /* 'k' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 104, 255, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 104, 255, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 104, 255, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 104, 255, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 104, 255, 32, 0, 0, 104, 254, 150, 1, 0, + 0, 104, 255, 32, 0, 103, 254, 143, 0, 0, 0, + 0, 104, 255, 32, 102, 254, 135, 0, 0, 0, 0, + 0, 104, 255, 133, 254, 128, 0, 0, 0, 0, 0, + 0, 104, 255, 159, 255, 100, 0, 0, 0, 0, 0, + 0, 104, 255, 33, 169, 251, 73, 0, 0, 0, 0, + 0, 104, 255, 32, 9, 203, 243, 50, 0, 0, 0, + 0, 104, 255, 32, 0, 26, 229, 230, 31, 0, 0, + 0, 104, 255, 32, 0, 0, 52, 246, 214, 17, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'l' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 52, 255, 255, 255, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 152, 244, 0, 0, 0, 0, 0, + 0, 124, 255, 255, 255, 255, 255, 255, 152, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'm' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 52, 252, 53, 226, 222, 32, 164, 242, 124, 0, 0, + 52, 255, 202, 37, 219, 204, 137, 106, 246, 4, 0, + 52, 255, 127, 0, 193, 231, 9, 77, 255, 24, 0, + 52, 255, 52, 0, 192, 164, 0, 76, 255, 28, 0, + 52, 255, 52, 0, 192, 164, 0, 76, 255, 28, 0, + 52, 255, 52, 0, 192, 164, 0, 76, 255, 28, 0, + 52, 255, 52, 0, 192, 164, 0, 76, 255, 28, 0, + 52, 255, 52, 0, 192, 164, 0, 76, 255, 28, 0, + 52, 255, 52, 0, 192, 164, 0, 76, 255, 28, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'n' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 201, 49, 191, 247, 222, 97, 0, 0, 0, + 0, 152, 238, 231, 80, 12, 136, 253, 41, 0, 0, + 0, 152, 253, 70, 0, 0, 26, 255, 104, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 127, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'o' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 54, 183, 242, 246, 194, 69, 0, 0, 0, + 0, 51, 248, 148, 17, 29, 157, 253, 71, 0, 0, + 0, 196, 204, 0, 0, 0, 3, 221, 198, 0, 0, + 21, 255, 124, 0, 0, 0, 0, 152, 252, 6, 0, + 43, 255, 102, 0, 0, 0, 0, 127, 255, 18, 0, + 28, 255, 124, 0, 0, 0, 0, 150, 247, 3, 0, + 0, 225, 196, 0, 0, 0, 3, 225, 177, 0, 0, + 0, 96, 255, 135, 23, 21, 163, 240, 43, 0, 0, + 0, 0, 87, 206, 248, 240, 176, 31, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'p' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 203, 45, 185, 245, 232, 118, 0, 0, 0, + 0, 152, 238, 233, 102, 15, 114, 255, 73, 0, 0, + 0, 152, 255, 87, 0, 0, 0, 223, 176, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 174, 218, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 163, 237, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 187, 215, 0, 0, + 0, 152, 240, 0, 0, 0, 19, 244, 143, 0, 0, + 0, 152, 246, 56, 9, 40, 187, 228, 23, 0, 0, + 0, 152, 254, 245, 254, 233, 159, 27, 0, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 240, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'q' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 38, 171, 236, 248, 214, 202, 110, 0, 0, + 0, 36, 238, 169, 34, 11, 79, 255, 116, 0, 0, + 0, 165, 230, 6, 0, 0, 24, 255, 116, 0, 0, + 0, 227, 162, 0, 0, 0, 24, 255, 116, 0, 0, + 4, 254, 138, 0, 0, 0, 24, 255, 116, 0, 0, + 0, 245, 149, 0, 0, 0, 27, 255, 116, 0, 0, + 0, 205, 196, 0, 0, 0, 156, 255, 116, 0, 0, + 0, 105, 254, 91, 17, 142, 211, 255, 116, 0, 0, + 0, 2, 140, 239, 240, 158, 34, 255, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 23, 255, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 24, 255, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 24, 255, 116, 0, 0, + 0, 0, 0, 0, 0, 0, 24, 255, 116, 0, 0, + }, + /* 'r' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 51, 129, 226, 244, 189, 36, 0, 0, + 0, 60, 255, 224, 169, 33, 30, 222, 191, 0, 0, + 0, 60, 255, 159, 0, 0, 0, 146, 246, 0, 0, + 0, 60, 255, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 60, 255, 80, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 's' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 43, 176, 238, 243, 186, 57, 0, 0, 0, + 0, 14, 230, 174, 29, 14, 77, 154, 0, 0, 0, + 0, 58, 255, 89, 0, 0, 0, 0, 0, 0, 0, + 0, 14, 232, 234, 115, 29, 0, 0, 0, 0, 0, + 0, 0, 39, 175, 250, 254, 195, 60, 0, 0, 0, + 0, 0, 0, 0, 17, 99, 222, 249, 36, 0, 0, + 0, 0, 0, 0, 0, 0, 65, 255, 87, 0, 0, + 0, 93, 143, 49, 10, 26, 162, 244, 30, 0, 0, + 0, 13, 133, 211, 245, 237, 176, 49, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 't' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 176, 129, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 140, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 140, 0, 0, 0, 0, 0, 0, + 116, 255, 255, 255, 255, 255, 255, 255, 132, 0, 0, + 0, 0, 0, 248, 140, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 140, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 140, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 248, 140, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 247, 140, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 223, 161, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 150, 243, 67, 5, 0, 0, 0, 0, + 0, 0, 0, 14, 151, 228, 254, 255, 132, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'u' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 152, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 151, 240, 0, 0, 0, 8, 255, 128, 0, 0, + 0, 130, 252, 4, 0, 0, 92, 255, 128, 0, 0, + 0, 66, 255, 114, 10, 97, 229, 247, 128, 0, 0, + 0, 0, 122, 228, 247, 183, 38, 228, 128, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'v' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 59, 255, 125, 0, 0, 0, 0, 151, 249, 20, 0, + 0, 216, 210, 0, 0, 0, 2, 232, 172, 0, 0, + 0, 119, 255, 38, 0, 0, 63, 255, 74, 0, 0, + 0, 24, 251, 122, 0, 0, 147, 229, 3, 0, 0, + 0, 0, 178, 206, 0, 1, 229, 133, 0, 0, 0, + 0, 0, 80, 255, 34, 59, 255, 36, 0, 0, 0, + 0, 0, 4, 232, 117, 141, 193, 0, 0, 0, 0, + 0, 0, 0, 140, 194, 218, 95, 0, 0, 0, 0, + 0, 0, 0, 41, 253, 242, 10, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'w' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 155, 222, 0, 0, 0, 0, 0, 2, 251, 129, 0, + 119, 249, 1, 0, 0, 0, 0, 24, 255, 93, 0, + 83, 255, 25, 0, 195, 157, 0, 50, 255, 56, 0, + 47, 255, 54, 13, 243, 232, 1, 76, 255, 20, 0, + 11, 255, 84, 79, 176, 224, 55, 102, 239, 0, 0, + 0, 231, 113, 149, 105, 152, 132, 129, 203, 0, 0, + 0, 195, 146, 220, 32, 77, 213, 157, 166, 0, 0, + 0, 159, 240, 214, 0, 10, 247, 244, 130, 0, 0, + 0, 122, 255, 140, 0, 0, 182, 255, 94, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'x' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 210, 241, 29, 0, 0, 21, 235, 209, 8, 0, + 0, 42, 246, 183, 0, 0, 171, 244, 40, 0, 0, + 0, 0, 102, 255, 96, 87, 255, 95, 0, 0, 0, + 0, 0, 0, 171, 239, 235, 163, 0, 0, 0, 0, + 0, 0, 0, 44, 255, 255, 41, 0, 0, 0, 0, + 0, 0, 3, 195, 206, 218, 194, 3, 0, 0, 0, + 0, 0, 134, 252, 50, 65, 255, 131, 0, 0, 0, + 0, 70, 254, 139, 0, 0, 159, 253, 66, 0, 0, + 25, 234, 222, 10, 0, 0, 18, 234, 230, 22, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'y' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 58, 255, 127, 0, 0, 0, 0, 153, 248, 19, 0, + 0, 213, 215, 0, 0, 0, 5, 236, 165, 0, 0, + 0, 113, 255, 48, 0, 0, 76, 255, 62, 0, 0, + 0, 19, 249, 136, 0, 0, 165, 215, 0, 0, 0, + 0, 0, 168, 223, 0, 10, 244, 112, 0, 0, 0, + 0, 0, 68, 255, 56, 87, 247, 18, 0, 0, 0, + 0, 0, 1, 221, 150, 179, 163, 0, 0, 0, 0, + 0, 0, 0, 123, 248, 250, 62, 0, 0, 0, 0, + 0, 0, 0, 26, 252, 226, 1, 0, 0, 0, 0, + 0, 0, 0, 29, 252, 127, 0, 0, 0, 0, 0, + 0, 0, 0, 144, 242, 20, 0, 0, 0, 0, 0, + 0, 8, 101, 253, 99, 0, 0, 0, 0, 0, 0, + 124, 251, 214, 93, 0, 0, 0, 0, 0, 0, 0, + }, + /* 'z' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 112, 255, 255, 255, 255, 255, 253, 42, 0, 0, + 0, 0, 0, 0, 0, 10, 221, 145, 0, 0, 0, + 0, 0, 0, 0, 0, 141, 223, 11, 0, 0, 0, + 0, 0, 0, 0, 53, 252, 70, 0, 0, 0, 0, + 0, 0, 0, 5, 210, 159, 0, 0, 0, 0, 0, + 0, 0, 0, 126, 232, 18, 0, 0, 0, 0, 0, + 0, 0, 41, 249, 84, 0, 0, 0, 0, 0, 0, + 0, 2, 198, 174, 0, 0, 0, 0, 0, 0, 0, + 0, 103, 255, 255, 255, 255, 255, 255, 140, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, + /* '0x7b' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 12, 151, 237, 227, 0, 0, 0, + 0, 0, 0, 0, 159, 223, 51, 2, 0, 0, 0, + 0, 0, 0, 1, 248, 113, 0, 0, 0, 0, 0, + 0, 0, 0, 22, 255, 85, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 255, 84, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 255, 82, 0, 0, 0, 0, 0, + 0, 0, 0, 42, 255, 60, 0, 0, 0, 0, 0, + 0, 0, 20, 157, 220, 5, 0, 0, 0, 0, 0, + 0, 148, 255, 233, 59, 0, 0, 0, 0, 0, 0, + 0, 0, 24, 160, 238, 17, 0, 0, 0, 0, 0, + 0, 0, 0, 38, 255, 74, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 255, 88, 0, 0, 0, 0, 0, + 0, 0, 0, 28, 255, 88, 0, 0, 0, 0, 0, + 0, 0, 0, 27, 255, 88, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 252, 108, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 183, 216, 45, 2, 0, 0, 0, + 0, 0, 0, 0, 25, 165, 232, 227, 0, 0, 0, + }, + /* '0x7c' */ { + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 196, 172, 0, 0, 0, 0, 0, + }, + /* '0x7d' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 249, 223, 137, 8, 0, 0, 0, 0, 0, + 0, 0, 4, 64, 239, 131, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 147, 221, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 117, 251, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 116, 255, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 114, 255, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 93, 255, 20, 0, 0, 0, 0, + 0, 0, 0, 0, 22, 240, 151, 21, 0, 0, 0, + 0, 0, 0, 0, 0, 78, 241, 255, 124, 0, 0, + 0, 0, 0, 0, 27, 244, 121, 16, 0, 0, 0, + 0, 0, 0, 0, 94, 255, 11, 0, 0, 0, 0, + 0, 0, 0, 0, 108, 255, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 108, 255, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 108, 255, 3, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 235, 0, 0, 0, 0, 0, + 0, 0, 3, 52, 227, 159, 0, 0, 0, 0, 0, + 0, 0, 251, 237, 155, 16, 0, 0, 0, 0, 0, + }, + /* '0x7e' */ { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 98, 224, 226, 82, 0, 0, 0, 0, 0, 0, + 34, 252, 81, 66, 240, 65, 0, 28, 255, 66, 0, + 93, 255, 5, 0, 90, 232, 48, 100, 244, 17, 0, + 0, 0, 0, 0, 0, 105, 233, 222, 83, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }, +}; diff --git a/CODE/firstInit/main.c b/CODE/firstInit/main.c index 3665067..8dc7b13 100644 --- a/CODE/firstInit/main.c +++ b/CODE/firstInit/main.c @@ -99,106 +99,26 @@ static u32 blend(u32 c1, u32 c2, u32 a) { return 0xFF000000|(b<<16)|(g<<8)|r; } -/* 8x8 font for ASCII */ -static const u8 font[95][8] = { - {0,0,0,0,0,0,0,0},{0x18,0x3c,0x3c,0x18,0x18,0,0x18,0}, /* ! */ - {0x6c,0x6c,0,0,0,0,0,0},{0x6c,0x6c,0xfe,0x6c,0xfe,0x6c,0x6c,0}, /* # */ - {0x18,0x7e,0xc0,0x7c,0x6,0xfc,0x18,0},{0,0xc6,0xcc,0x18,0x30,0x66,0xc6,0}, /* $ % */ - {0x38,0x6c,0x38,0x76,0xdc,0xcc,0x76,0},{0x18,0x18,0x30,0,0,0,0,0}, /* & ' */ - {0xc,0x18,0x30,0x30,0x30,0x18,0xc,0},{0x30,0x18,0xc,0xc,0xc,0x18,0x30,0}, /* ( ) */ - {0,0x66,0x3c,0xff,0x3c,0x66,0,0},{0,0x18,0x18,0x7e,0x18,0x18,0,0}, /* * + */ - {0,0,0,0,0,0x18,0x18,0x30},{0,0,0,0x7e,0,0,0,0},{0,0,0,0,0,0x18,0x18,0}, /* , - . */ - {0x6,0xc,0x18,0x30,0x60,0xc0,0x80,0}, /* / */ - {0x7c,0xc6,0xce,0xde,0xf6,0xe6,0x7c,0},{0x18,0x38,0x18,0x18,0x18,0x18,0x7e,0}, /* 0 1 */ - {0x7c,0xc6,0x6,0xc,0x30,0x60,0xfe,0},{0x7c,0xc6,0x6,0x3c,0x6,0xc6,0x7c,0}, /* 2 3 */ - {0x1c,0x3c,0x6c,0xcc,0xfe,0xc,0x1e,0},{0xfe,0xc0,0xfc,0x6,0x6,0xc6,0x7c,0}, /* 4 5 */ - {0x38,0x60,0xc0,0xfc,0xc6,0xc6,0x7c,0},{0xfe,0xc6,0xc,0x18,0x30,0x30,0x30,0}, /* 6 7 */ - {0x7c,0xc6,0xc6,0x7c,0xc6,0xc6,0x7c,0},{0x7c,0xc6,0xc6,0x7e,0x6,0xc,0x78,0}, /* 8 9 */ - {0,0x18,0x18,0,0,0x18,0x18,0},{0,0x18,0x18,0,0,0x18,0x18,0x30}, /* : ; */ - {0x6,0xc,0x18,0x30,0x18,0xc,0x6,0},{0,0,0x7e,0,0,0x7e,0,0}, /* < = */ - {0x60,0x30,0x18,0xc,0x18,0x30,0x60,0},{0x7c,0xc6,0xc,0x18,0x18,0,0x18,0}, /* > ? */ - {0x7c,0xc6,0xde,0xde,0xde,0xc0,0x78,0}, /* @ */ - {0x38,0x6c,0xc6,0xfe,0xc6,0xc6,0xc6,0},{0xfc,0x66,0x66,0x7c,0x66,0x66,0xfc,0}, /* A B */ - {0x3c,0x66,0xc0,0xc0,0xc0,0x66,0x3c,0},{0xf8,0x6c,0x66,0x66,0x66,0x6c,0xf8,0}, /* C D */ - {0xfe,0x62,0x68,0x78,0x68,0x62,0xfe,0},{0xfe,0x62,0x68,0x78,0x68,0x60,0xf0,0}, /* E F */ - {0x3c,0x66,0xc0,0xc0,0xce,0x66,0x3e,0},{0xc6,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0}, /* G H */ - {0x3c,0x18,0x18,0x18,0x18,0x18,0x3c,0},{0x1e,0xc,0xc,0xc,0xcc,0xcc,0x78,0}, /* I J */ - {0xe6,0x66,0x6c,0x78,0x6c,0x66,0xe6,0},{0xf0,0x60,0x60,0x60,0x62,0x66,0xfe,0}, /* K L */ - {0xc6,0xee,0xfe,0xfe,0xd6,0xc6,0xc6,0},{0xc6,0xe6,0xf6,0xde,0xce,0xc6,0xc6,0}, /* M N */ - {0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0},{0xfc,0x66,0x66,0x7c,0x60,0x60,0xf0,0}, /* O P */ - {0x7c,0xc6,0xc6,0xc6,0xc6,0xce,0x7c,0xe},{0xfc,0x66,0x66,0x7c,0x6c,0x66,0xe6,0}, /* Q R */ - {0x7c,0xc6,0xe0,0x78,0xe,0xc6,0x7c,0},{0x7e,0x7e,0x5a,0x18,0x18,0x18,0x3c,0}, /* S T */ - {0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0},{0xc6,0xc6,0xc6,0xc6,0xc6,0x6c,0x38,0}, /* U V */ - {0xc6,0xc6,0xc6,0xd6,0xd6,0xfe,0x6c,0},{0xc6,0xc6,0x6c,0x38,0x38,0x6c,0xc6,0}, /* W X */ - {0x66,0x66,0x66,0x3c,0x18,0x18,0x3c,0},{0xfe,0xc6,0x8c,0x18,0x32,0x66,0xfe,0}, /* Y Z */ - {0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}, /* [ \ */ - {0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}, /* ] ^ */ - {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0}, /* _ */ - {0,0,0,0,0,0,0,0}, /* ` */ - /* a-z lowercase — full-height (no top blank rows) so they scale like uppercase */ - {0x7c,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0}, /* a */ - {0xc6,0xc6,0xc6,0xfc,0xc6,0xc6,0xfc,0}, /* b */ - {0x7c,0xc6,0xc0,0xc0,0xc0,0xc6,0x7c,0}, /* c */ - {0x06,0x06,0x06,0xfc,0xc6,0xc6,0xfc,0}, /* d */ - {0x7c,0xc6,0xfe,0xc0,0xfc,0xc0,0xc0,0}, /* e */ - {0x7c,0xc6,0xc0,0xf8,0xc0,0xc0,0xc0,0}, /* f */ - {0x7c,0xc6,0xc0,0xde,0xc6,0xc6,0x7c,0}, /* g */ - {0xc6,0xc6,0xc6,0xfe,0xc6,0xc6,0xc6,0}, /* h */ - {0x7e,0x18,0x18,0x18,0x18,0x18,0x18,0}, /* i */ - {0x06,0x06,0x06,0xc6,0xc6,0xc6,0x7c,0}, /* j */ - {0xc6,0xcc,0xd8,0xf0,0xd8,0xcc,0xc6,0}, /* k */ - {0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xfe,0}, /* l */ - {0xc6,0xee,0xfe,0xfe,0xc6,0xc6,0xc6,0}, /* m */ - {0xc6,0xe6,0xf6,0xde,0xce,0xc6,0xc6,0}, /* n */ - {0x7c,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0}, /* o */ - {0xc6,0xc6,0xc6,0xfc,0xc0,0xc0,0xc0,0}, /* p */ - {0x7c,0xc6,0xc6,0xce,0x7c,0x06,0x06,0}, /* q */ - {0xc6,0xc6,0xcc,0xf8,0xcc,0xc6,0xc6,0}, /* r */ - {0x7c,0xc0,0x7c,0x06,0xc6,0xc6,0x7c,0}, /* s */ - {0x18,0x18,0x7e,0x18,0x18,0x18,0x18,0}, /* t */ - {0xc6,0xc6,0xc6,0xc6,0xc6,0xc6,0x7c,0}, /* u */ - {0xc6,0xc6,0xc6,0xc6,0xc6,0x6c,0x38,0}, /* v */ - {0xc6,0xc6,0xd6,0xfe,0xee,0xc6,0xc6,0}, /* w */ - {0xc6,0xc6,0x6c,0x38,0x6c,0xc6,0xc6,0}, /* x */ - {0xc6,0xc6,0xc6,0x7c,0x06,0x06,0x7c,0}, /* y */ - {0xfe,0x0c,0x18,0x30,0x60,0xc0,0xfe,0}, /* z */ -}; - -/* ASCII text — thin 8x8 → 10x16, preserves case, draws foreground only (no bg fill) */ -#define ASCII_W 10 -#define ASCII_H 16 -#define ASCII_STEP 12 +/* ASCII font — Consolas 18px grayscale, pre-rendered from C:\Windows\Fonts\consola.ttf */ +#include "ascii_bitmaps.c" +/* ASCII text — Consolas 18px grayscale, alpha-blend rendered (skips transparent pixels) */ +#define ASCII_W 11 +#define ASCII_H 18 +#define ASCII_STEP 11 static void fb_char(u32 *fb, u32 ch, i64 x, i64 y, u32 fg, u32 bg) { - u32 idx = ch >= ' ' && ch <= 'z' ? (u32)(ch-' ') : 0; - const u8 *g = font[idx]; - u8 pat[8][8]; - for (i64 r=0; r<8; r++) { - u8 row = g[r]; - for (i64 c=0; c<8; c++) { - pat[r][c] = (row & (0x80>>c)) ? 1 : 0; - } - } - for (i64 sy=0; sy '~') ch = ' '; + u32 idx = (u32)(ch - ' '); + const u8 *g = g_ascii[idx]; + for (i64 r=0; r= fb_h) continue; u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); - i64 src_y = sy * 7 / (ASCII_H - 1); - if (src_y > 7) src_y = 7; - for (i64 sx=0; sx 7) src_x = 7; - int on = pat[src_y][src_x]; - int edge = 0; - if (!on) { - if (src_x > 0 && pat[src_y][src_x-1]) edge = 1; - if (src_x < 7 && pat[src_y][src_x+1]) edge = 1; - if (src_y > 0 && pat[src_y-1][src_x]) edge = 1; - if (src_y < 7 && pat[src_y+1][src_x]) edge = 1; - if (!edge) continue; - } - i64 xx = x + sx; + for (i64 c=0; c= fb_w) continue; - line[(u64)xx] = on ? fg : blend(bg, fg, 72); + line[(u64)xx] = (a == 255) ? fg : blend(bg, fg, a); } } } @@ -352,7 +272,7 @@ static void draw_rounded_input(u32 *fb, i64 x, i64 y, i64 w, i64 h, const char * char out[64]; int i=0; while(value[i] && i<60) { out[i] = mask ? '*' : value[i]; i++; } out[i]=0; - if (out[0]) fb_text(fb, out, x+14, y+(h-16)/2, fg, fill); + if (out[0]) fb_text(fb, out, x+14, y+(h-ASCII_H)/2, fg, fill); if (active) { i64 cx2 = x + 14 + i * ASCII_STEP; fill_rect(fb, cx2, y+10, 2, h-20, fg); @@ -704,43 +624,202 @@ static void read_prefs_page(u32 *fb, i64 card_x, i64 card_y, setup_prefs *prefs, } typedef struct { - int mode; - int device; - int ip; - int dns; + int selected; + int connected; + char ssid[32]; + char password[64]; } setup_net; -static const char *net_value(int row, int val) { - if (row == 0) return val == 0 ? "Disabled" : (val == 1 ? "DHCP" : "Manual"); - if (row == 1) return val == 0 ? "Auto" : (val == 1 ? "e1000" : "virtio-net"); - if (row == 2) return val ? "Manual" : "Auto DHCP"; - if (row == 3) return val ? "Manual DNS" : "Auto DNS"; - return ""; +/* UI 显示行数(固定 4 行布局);真实扫描结果数由 g_wifi_scan_count 决定。 */ +#define WIFI_SCAN_COUNT 4 +#define WIFI_MAX_RESULTS 8 + +/* 真实 WiFi 扫描结果(来自 ath9k 驱动通过 kernel_api.net.scan_* 查询)。 */ +static char g_wifi_ssids[WIFI_MAX_RESULTS][34]; +static char g_wifi_meta[WIFI_MAX_RESULTS][20]; +static int g_wifi_scan_count; +static int g_wifi_scan_tried; /* 是否已尝试查询扫描结果 */ +static int g_no_wireless_device; /* 检测到无无线设备时跳过 WiFi 页面 */ + +/* dkm_net_api 函数指针类型(通过偏移访问 kernel_api.net)。 */ +typedef u32 (*net_device_count_fn)(void); +typedef int (*net_device_info_fn)(u32 index, void *out); +typedef int (*net_scan_start_fn)(u32 index); +typedef int (*net_scan_count_fn)(u32 index); +typedef int (*net_scan_result_fn)(u32 index, u32 n, void *out); +typedef int (*net_is_wireless_fn)(u32 index); + +/* dkm_net_scan_result 布局(须与 UTSM/include/utsm/net.h 一致)。 */ +typedef struct { + char ssid[33]; + u8 bssid[6]; + u8 channel; + i8 rssi; + u8 security; +} net_scan_result; + +#define DKM_NET_SEC_OPEN 0 +#define DKM_NET_SEC_WEP 1 +#define DKM_NET_SEC_WPA 2 +#define DKM_NET_SEC_WPA2 3 +#define DKM_NET_SEC_WPA3 4 + +/* 把加密类型 + RSSI 转为 "WPA2 strong" 这种简短描述。 */ +static void wifi_meta_str(char *out, u32 cap, u8 security, i8 rssi) { + if (!cap) return; + const char *sec; + switch (security) { + case DKM_NET_SEC_WPA2: sec = "WPA2"; break; + case DKM_NET_SEC_WPA: sec = "WPA "; break; + case DKM_NET_SEC_WPA3: sec = "WPA3"; break; + case DKM_NET_SEC_WEP: sec = "WEP "; break; + default: sec = "Open"; break; + } + /* RSSI 分级:>=-50 strong, -51..-65 good, -66..-75 weak, else very weak */ + const char *q; + i8 r = rssi; + if (r >= -50) q = "strong"; + else if (r >= -65) q = "good"; + else if (r >= -75) q = "weak"; + else q = "very weak"; + /* 手动拼接 "SEC quality" */ + u32 i = 0; + while (sec[i] && i + 1 < cap) { out[i] = sec[i]; i++; } + while (i < 5 && i + 1 < cap) { out[i++] = ' '; } + u32 j = 0; + while (q[j] && i + 1 < cap) { out[i++] = q[j++]; } + out[i] = 0; } -static int net_get(const setup_net *n, int row) { - if (row == 0) return n->mode; - if (row == 1) return n->device; - if (row == 2) return n->ip; - if (row == 3) return n->dns; - return 0; +/* 通过 kernel_api 查询无线设备并读取扫描结果。 + * kernel_api 偏移布局: + * +0x48 net_api 指针 + * net_api 偏移布局(追加 scan 接口后): + * +0 register_device, +8 device_count, +16 device_info, + * +24 tx, +32 rx_poll, +40 scan_start, +48 scan_count, + * +56 scan_result, +64 is_wireless + */ +static void wifi_query_scan(u64 kernel_api) { + g_wifi_scan_tried = 1; + g_wifi_scan_count = 0; + if (!kernel_api) { logl("[FirstInit] no kernel_api, wifi scan skipped"); return; } + u64 net_api = *(u64 *)(kernel_api + 0x48); + if (!net_api) { logl("[FirstInit] no net_api, wifi scan skipped"); return; } + net_device_count_fn dcount = (net_device_count_fn)*(u64 *)(net_api + 8); + net_scan_start_fn sstart = (net_scan_start_fn)*(u64 *)(net_api + 40); + net_scan_count_fn scount = (net_scan_count_fn)*(u64 *)(net_api + 48); + net_scan_result_fn sresult = (net_scan_result_fn)*(u64 *)(net_api + 56); + net_is_wireless_fn iswl = (net_is_wireless_fn)*(u64 *)(net_api + 64); + if (!dcount || !iswl || !scount || !sresult) { + logl("[FirstInit] net scan api incomplete"); + return; + } + u32 ndev = dcount(); + /* 串口日志:设备数 */ + { + char b[24]; int p=0; + const char *s="[FirstInit] net devices="; + while(s[p]) b[p]=s[p], p++; + b[p++]='0'+(ndev/10); b[p++]='0'+(ndev%10); b[p]=0; + logl(b); + } + /* 找第一个无线设备 */ + int wifi_idx = -1; + for (u32 i = 0; i < ndev; i++) { + int rc = iswl(i); + if (rc == 1) { wifi_idx = (int)i; break; } + } + if (wifi_idx < 0) { + logl("[FirstInit] no wireless device found"); + g_no_wireless_device = 1; + return; + } + /* ath9k 驱动在 driver_init 时已同步扫描,但保险起见若 count=0 再触发一次 */ + int count = scount((u32)wifi_idx); + if (count <= 0 && sstart) { + logl("[FirstInit] triggering wifi scan..."); + int rc = sstart((u32)wifi_idx); + { + char b[32]; int p=0; + const char *s="[FirstInit] scan_start rc="; + while(s[p]) b[p]=s[p], p++; + if (rc < 0) { b[p++]='-'; b[p++]='0'+(-rc); } else { b[p++]='0'+rc; } + b[p]=0; logl(b); + } + count = scount((u32)wifi_idx); + } + if (count <= 0) { + logl("[FirstInit] wifi scan: no results"); + return; + } + if (count > WIFI_MAX_RESULTS) count = WIFI_MAX_RESULTS; + /* 读取每个扫描结果 */ + for (int i = 0; i < count; i++) { + net_scan_result r; + for (u32 k = 0; k < sizeof(r); k++) ((u8 *)&r)[k] = 0; + int rc = sresult((u32)wifi_idx, (u32)i, &r); + if (rc != 0) continue; + /* 拷贝 SSID */ + u32 j = 0; + while (r.ssid[j] && j + 1 < sizeof(g_wifi_ssids[i])) { + g_wifi_ssids[i][j] = r.ssid[j]; j++; + } + g_wifi_ssids[i][j] = 0; + /* 生成 meta 字符串 */ + wifi_meta_str(g_wifi_meta[i], sizeof(g_wifi_meta[i]), r.security, r.rssi); + g_wifi_scan_count++; + } + { + char b[32]; int p=0; + const char *s="[FirstInit] wifi scan results="; + while(s[p]) b[p]=s[p], p++; + b[p++]='0'+(g_wifi_scan_count/10); b[p++]='0'+(g_wifi_scan_count%10); b[p]=0; + logl(b); + } } -static void net_toggle(setup_net *n, int row) { - if (row == 0) n->mode = (n->mode + 1) % 3; - else if (row == 1) n->device = (n->device + 1) % 3; - else if (row == 2) n->ip ^= 1; - else if (row == 3) n->dns ^= 1; +static void net_copy(char *dst, u32 cap, const char *src) { + if (!cap) return; + u32 i = 0; + while (src[i] && i + 1 < cap) { + dst[i] = src[i]; + i++; + } + dst[i] = 0; } -static void draw_network_row(u32 *fb, i64 card_x, i64 card_y, setup_net *net, int row, int active, u32 fg) { +static void net_select(setup_net *net, int idx) { + if (idx < 0) idx = 0; + if (idx >= WIFI_SCAN_COUNT) idx = WIFI_SCAN_COUNT - 1; + if (net->selected != idx) { + net->password[0] = 0; + net->connected = 0; + } + net->selected = idx; + if (idx < g_wifi_scan_count) { + net_copy(net->ssid, sizeof(net->ssid), g_wifi_ssids[idx]); + } else { + net->ssid[0] = 0; + } +} + +static void draw_wifi_row(u32 *fb, i64 card_x, i64 card_y, setup_net *net, int row, int active, u32 fg) { u32 card = CARD_BG; - i64 ry = card_y + 150 + (i64)row * 88; - const u8 *label = g_txt_net_mode; i64 lw = g_txt_net_mode_w, lh = g_txt_net_mode_h; - if (row == 1) { label = g_txt_net_device; lw = g_txt_net_device_w; lh = g_txt_net_device_h; } - else if (row == 2) { label = g_txt_net_ip; lw = g_txt_net_ip_w; lh = g_txt_net_ip_h; } - else if (row == 3) { label = g_txt_net_dns; lw = g_txt_net_dns_w; lh = g_txt_net_dns_h; } - draw_option_row(fb, card_x+40, ry, 820, 42, label, lw, lh, net_value(row, net_get(net, row)), active==row, fg, card); + i64 x = card_x + 40; + i64 y = card_y + 132 + (i64)row * 64; + u32 fill = active == row ? 0xFFFFFFFF : (net->selected == row ? 0xFFDCECF8 : INPUT_BG); + fill_rounded_rect(fb, x, y, 390, 50, 9, fill); + stroke_rounded_rect(fb, x, y, 390, 50, 9, 1, active == row ? 0xFF7FA7C8 : card); + fb_text(fb, net->selected == row ? ">" : " ", x + 12, y + 16, fg, fill); + if (row < g_wifi_scan_count) { + fb_text(fb, g_wifi_ssids[row], x + 42, y + 8, fg, fill); + fb_text(fb, g_wifi_meta[row], x + 42, y + 28, 0xFF7F97AC, fill); + } else { + /* 无扫描结果时的占位行 */ + const char *placeholder = (g_wifi_scan_tried && g_wifi_scan_count == 0) + ? "No networks found" : "(empty)"; + fb_text(fb, placeholder, x + 42, y + 16, 0xFF9AACC0, fill); + } } static void redraw_network_card(u32 *fb, i64 card_x, i64 card_y, setup_net *net, int active, i64 mx, i64 my, u32 bg, u32 fg) { @@ -749,23 +828,42 @@ static void redraw_network_card(u32 *fb, i64 card_x, i64 card_y, setup_net *net, fill_gradient_rect(fb, card_x - 24, card_y - 24, 948, 648); draw_card(fb, card_x, card_y, 900, 600, card, bg); fb_bitmap_alpha(fb, g_txt_network_title, g_txt_network_title_w, g_txt_network_title_h, card_x+40, card_y+40, fg, card, 255); - for (int r=0; r<4; r++) draw_network_row(fb, card_x, card_y, net, r, active, fg); - fb_bitmap_alpha(fb, g_txt_net_hint, g_txt_net_hint_w, g_txt_net_hint_h, card_x+40, card_y+555, 0xFF7F97AC, card, 255); - cursor_draw(fb, mx, my); -} + fb_text(fb, "Choose a network", card_x + 40, card_y + 88, 0xFF7F97AC, card); + for (int r=0; rselected; + if (preview < g_wifi_scan_count) { + fb_text(fb, g_wifi_ssids[preview], card_x + 494, card_y + 182, fg, INPUT_BG); + fb_text(fb, g_wifi_meta[preview], card_x + 494, card_y + 210, 0xFF7F97AC, INPUT_BG); + } else { + fb_text(fb, g_wifi_scan_tried ? "No networks found" : "WiFi not initialized", + card_x + 494, card_y + 182, 0xFF9AACC0, INPUT_BG); + fb_text(fb, g_wifi_scan_tried ? "Check ath9k driver" : "Run ath9k driver first", + card_x + 494, card_y + 210, 0xFF9AACC0, INPUT_BG); + } + fb_text(fb, "Use Up/Down or click menu", card_x + 494, card_y + 260, 0xFF7F97AC, INPUT_BG); + fb_text(fb, "Enter selects highlighted item", card_x + 494, card_y + 288, 0xFF7F97AC, INPUT_BG); + + fb_text(fb, "Password", card_x + 470, card_y + 400, fg, card); + draw_rounded_input(fb, card_x + 470, card_y + 430, 390, 44, net->password, 1, fg, card, active == WIFI_SCAN_COUNT); + u32 btn = active == WIFI_SCAN_COUNT + 1 ? 0xFFFFFFFF : 0xFFDCECF8; + fill_rounded_rect(fb, card_x + 620, card_y + 500, 240, 48, 10, btn); + stroke_rounded_rect(fb, card_x + 620, card_y + 500, 240, 48, 10, 1, active == WIFI_SCAN_COUNT + 1 ? 0xFFB6C9DD : card); + fb_text(fb, "Connect", card_x + 690, card_y + 516, fg, btn); + if (net->connected) fb_text(fb, "Connection profile saved", card_x + 40, card_y + 515, 0xFF4F8A5F, card); + else fb_text(fb, "Select SSID, enter password, then Connect", card_x + 40, card_y + 555, 0xFF7F97AC, card); cursor_draw(fb, mx, my); } static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i64 *mx, i64 *my, u32 bg, u32 fg) { - int active = 0, mcnt = 0, e0 = 0; + int active = 0, mcnt = 0, e0 = 0, shift = 0; u8 mpkt[3]; u8 prev_btns = 0; + int len = 0; + while (net->password[len]) len++; + net_select(net, net->selected); redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); for (;;) { u8 st = inb(0x64); @@ -787,28 +885,79 @@ static void read_network_page(u32 *fb, i64 card_x, i64 card_y, setup_net *net, i cursor_draw(fb, *mx, *my); } if ((btns & 1) && !(prev_btns & 1)) { - for (int r=0; r<4; r++) { - i64 ry = card_y + 150 + (i64)r * 88; - if (*mx >= card_x + 260 && *mx < card_x + 860 && *my >= ry && *my < ry + 42) { - int old = active; active = r; net_toggle(net, active); - if (old != active) redraw_network_row(fb, card_x, card_y, net, old, active, *mx, *my, fg); - redraw_network_row(fb, card_x, card_y, net, active, active, *mx, *my, fg); + for (int r=0; r= card_x + 40 && *mx < card_x + 430 && *my >= ry && *my < ry + 50) { + active = r; + net_select(net, r); + len = 0; + redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); } } + if (*mx >= card_x + 470 && *mx < card_x + 860 && *my >= card_y + 430 && *my < card_y + 474) { + active = WIFI_SCAN_COUNT; + redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); + } + if (*mx >= card_x + 620 && *mx < card_x + 860 && *my >= card_y + 500 && *my < card_y + 548) { + net->connected = 1; + logl("[FirstInit] wireless profile selected"); + redraw_network_card(fb, card_x, card_y, net, WIFI_SCAN_COUNT + 1, *mx, *my, bg, fg); + for (int i=0;i<30;i++) delay_frame(); + return; + } } prev_btns = btns; continue; } u8 sc = data; if (sc == 0xE0) { e0 = 1; continue; } + if (sc == 0x2A || sc == 0x36) { shift = 1; continue; } + if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; } if (sc & 0x80) { e0 = 0; continue; } - if (e0 && sc == 0x48) { if (active > 0) { int old=active; active--; redraw_network_row(fb, card_x, card_y, net, old, active, *mx, *my, fg); redraw_network_row(fb, card_x, card_y, net, active, active, *mx, *my, fg); } e0=0; continue; } - if (e0 && sc == 0x50) { if (active < 3) { int old=active; active++; redraw_network_row(fb, card_x, card_y, net, old, active, *mx, *my, fg); redraw_network_row(fb, card_x, card_y, net, active, active, *mx, *my, fg); } e0=0; continue; } + if (e0 && sc == 0x48) { + if (active > 0) active--; + redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); + e0=0; continue; + } + if (e0 && sc == 0x50) { + if (active < WIFI_SCAN_COUNT + 1) active++; + redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); + e0=0; continue; + } e0 = 0; - if (sc == 0x39 || sc == 0x4D || sc == 0x4B) { net_toggle(net, active); redraw_network_row(fb, card_x, card_y, net, active, active, *mx, *my, fg); continue; } - if (sc == 0x1C) { - if (active < 3) { int old=active; active++; redraw_network_row(fb, card_x, card_y, net, old, active, *mx, *my, fg); redraw_network_row(fb, card_x, card_y, net, active, active, *mx, *my, fg); } - else return; + char c = scan_to_ascii(sc, shift); + if (!c) continue; + if (c == '\n') { + if (active < WIFI_SCAN_COUNT) { + net_select(net, active); + len = 0; + active = WIFI_SCAN_COUNT; + } else if (active == WIFI_SCAN_COUNT) { + active = WIFI_SCAN_COUNT + 1; + } else { + net->connected = 1; + logl("[FirstInit] wireless profile selected"); + redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); + for (int i=0;i<30;i++) delay_frame(); + return; + } + redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); + continue; + } + if (c == '\t') { + active = (active + 1) % (WIFI_SCAN_COUNT + 2); + redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); + continue; + } + if (active == WIFI_SCAN_COUNT) { + if (c == 8) { + if (len > 0) net->password[--len] = 0; + } else if (len < 63 && c >= 32 && c <= 126) { + net->password[len++] = c; + net->password[len] = 0; + } + net->connected = 0; + redraw_network_card(fb, card_x, card_y, net, active, *mx, *my, bg, fg); } } } @@ -854,20 +1003,20 @@ static void build_user_conf(const char *pc, const char *user, const char *pass, const char *th0="\ntheme="; for(int i=0;th0[i];i++) conf[p++]=th0[i]; const char *v4 = prefs->theme ? "dark" : "light"; for(int i=0;v4[i];i++) conf[p++]=v4[i]; const char *nm="\nnetwork.mode="; for(int i=0;nm[i];i++) conf[p++]=nm[i]; - const char *nv0 = net->mode == 0 ? "disabled" : (net->mode == 1 ? "dhcp" : "manual"); for(int i=0;nv0[i];i++) conf[p++]=nv0[i]; - const char *nd="\nnetwork.device="; for(int i=0;nd[i];i++) conf[p++]=nd[i]; - const char *nv1 = net->device == 0 ? "auto" : (net->device == 1 ? "e1000" : "virtio-net"); for(int i=0;nv1[i];i++) conf[p++]=nv1[i]; - const char *ni="\nnetwork.ip="; for(int i=0;ni[i];i++) conf[p++]=ni[i]; - const char *nv2 = net->ip ? "manual" : "dhcp"; for(int i=0;nv2[i];i++) conf[p++]=nv2[i]; - const char *dns="\nnetwork.dns="; for(int i=0;dns[i];i++) conf[p++]=dns[i]; - const char *nv3 = net->dns ? "manual" : "auto"; for(int i=0;nv3[i];i++) conf[p++]=nv3[i]; + const char *nv0 = net->connected ? "wifi" : "disabled"; for(int i=0;nv0[i];i++) conf[p++]=nv0[i]; + const char *ns="\nnetwork.ssid="; for(int i=0;ns[i];i++) conf[p++]=ns[i]; + for(int i=0;net->ssid[i]&&p<740;i++) conf[p++]=net->ssid[i]; + const char *np="\nnetwork.password="; for(int i=0;np[i];i++) conf[p++]=np[i]; + for(int i=0;net->password[i]&&p<740;i++) conf[p++]=net->password[i]; + const char *ni="\nnetwork.ip=dhcp"; for(int i=0;ni[i];i++) conf[p++]=ni[i]; + const char *dns="\nnetwork.dns=auto"; for(int i=0;dns[i];i++) conf[p++]=dns[i]; conf[p++]='\n'; /* XOR encrypt buffer with hash-derived stream; real disk write waits for FAT32 write support. */ for(int i=0;idkm_kernel_api); setup_net net; - net.mode = 0; /* Disabled */ - net.device = 0; /* Auto */ - net.ip = 0; /* Auto DHCP */ - net.dns = 0; /* Auto DNS */ - read_network_page(fb, card_x, card_y, &net, &mx, &my, bg, fg); + net.selected = 0; + net.connected = 0; + net.ssid[0] = 0; + net.password[0] = 0; + if (g_no_wireless_device) { + /* QEMU 或无无线网卡场景:跳过 WiFi 设置页面,network.mode=disabled */ + logl("[FirstInit] no wireless device, skipping WiFi setup page"); + net.connected = 0; + } else { + cursor_bg_valid = 0; + cursor_cur_x = -100; + cursor_cur_y = -100; + logl("[FirstInit] entering wireless setup page"); + read_network_page(fb, card_x, card_y, &net, &mx, &my, bg, fg); + logl("[FirstInit] wireless setup page returned"); + } build_user_conf(pc, user, pass, &prefs, &net); cursor_erase(fb); @@ -978,5 +1140,6 @@ void dsk_entry(const dsk_boot_context *ctx) { fb_bitmap_alpha(fb, g_txt_done, g_txt_done_w, g_txt_done_h, cx - g_txt_done_w/2, cy - 20, fg, bg_at_y(cy), 255); logl("[FirstInit] user setup finished"); - for(;;) __asm__("hlt"); + /* 短暂停留后返回,由 DSK 接管加载 shell */ + for (int i = 0; i < 60; i++) delay_frame(); } diff --git a/CODE/firstInit/main.o b/CODE/firstInit/main.o index 082e6ab..582fbfa 100644 Binary files a/CODE/firstInit/main.o and b/CODE/firstInit/main.o differ diff --git a/CODE/firstInit/text_bitmaps.o b/CODE/firstInit/text_bitmaps.o deleted file mode 100644 index 44883d4..0000000 Binary files a/CODE/firstInit/text_bitmaps.o and /dev/null differ diff --git a/CODE/font/render_ascii.py b/CODE/font/render_ascii.py new file mode 100644 index 0000000..350ca87 --- /dev/null +++ b/CODE/font/render_ascii.py @@ -0,0 +1,54 @@ +"""render_ascii.py — render ASCII 32-126 from Consolas TTF to grayscale bitmaps +Output: CODE/firstInit/ascii_bitmaps.c (included by main.c) + +Glyph cell: 11x18. Consolas 18px has advance=10, ascender top ~3, descender ~18. +ASCII_STEP=11 gives 1px spacing between glyphs (10px glyph + 1px gap). +""" +from PIL import Image, ImageFont, ImageDraw + +ttf = r"C:\Windows\Fonts\consola.ttf" +GLYPH_W = 11 +GLYPH_H = 18 +FONT_SIZE = 18 + +font = ImageFont.truetype(ttf, FONT_SIZE) + +# Verify metrics on first glyph +probe = font.getbbox("A") +print(f"Consolas {FONT_SIZE}px — 'A' bbox={probe}, advance={font.getlength('A')}") + +lines = [] +lines.append("/* Auto-generated by render_ascii.py - Consolas 18px grayscale */") +lines.append("/* 95 glyphs: ASCII 32 (space) .. 126 (~), cell 11x18 */") +lines.append("/* Do not edit - regenerate via: py render_ascii.py */") +lines.append("") +lines.append(f"static const int g_ascii_w = {GLYPH_W};") +lines.append(f"static const int g_ascii_h = {GLYPH_H};") +lines.append(f"static const unsigned char g_ascii[95][{GLYPH_W}*{GLYPH_H}] = {{") + +for cp in range(32, 127): + ch = chr(cp) + img = Image.new("L", (GLYPH_W, GLYPH_H), 0) + draw = ImageDraw.Draw(img) + draw.text((0, 0), ch, font=font, fill=255) + pixels = list(img.tobytes()) + if ch.isalnum(): + comment = ch + elif ch == ' ': + comment = "space" + else: + comment = f"0x{cp:02x}" + lines.append(f" /* '{comment}' */ {{") + for y_row in range(GLYPH_H): + row = ", ".join(str(pixels[y_row * GLYPH_W + x_col]) for x_col in range(GLYPH_W)) + lines.append(f" {row},") + lines.append(" },") + +lines.append("};") +lines.append("") + +out_path = "CODE/firstInit/ascii_bitmaps.c" +with open(out_path, "w", encoding="ascii") as f: + f.write("\n".join(lines)) + +print(f"Generated {out_path} ({95*GLYPH_W*GLYPH_H} bytes of glyph data)") diff --git a/CODE/netman/main.c b/CODE/netman/main.c index 3e02cd5..63a4782 100644 --- a/CODE/netman/main.c +++ b/CODE/netman/main.c @@ -1,4 +1,4 @@ -/* netman — Deshab network manager initializer +/* netman — Deshab network manager initializer * 读取 /system/deshab64/network/conf/conf.conf(SATA 早期镜像里为 root 目录 NETCONF.CNF), * 解析网络配置;枚举内核 netdev 注册表;对具备收发能力的接口通过 e1000 真实收发 * 跑最小 DHCP 客户端拿到 IP。 @@ -50,6 +50,14 @@ static void log_hex(const char *p, u64 v) { static void *memset_nm(void *d, int c, u64 n) { u8 *p=(u8*)d; while(n--)*p++=(u8)c; return d; } static int streqn(const char *a, const char *b, u32 n) { for(u32 i=0;i> 16) sum = (sum & 0xffff) + (sum >> 16); + u16 out = (u16)(~sum); + return out ? out : 0xffff; +} + static void delay_spin(u32 loops) { - for (volatile u32 i=0;i>8); ip[11]=(u8)(ipck); + u16 udpck = udp_checksum_ipv4(ip, udp, udp_len); + udp[6]=(u8)(udpck>>8); udp[7]=(u8)(udpck); + u32 frame_len = 14 + ip_len; if (frame_len < 60) frame_len = 60; return g_net_tx(index, g_pktbuf, frame_len); } /* 解析收到的帧,若为本机 xid 的 DHCP 回复则返回 message type。 */ -static int dhcp_parse(const u8 *frame, u32 len, u32 *out_yiaddr, u32 *out_server) { +static int dhcp_parse(const u8 *frame, u32 len, u32 *out_yiaddr, u32 *out_server, u32 *out_router, u32 *out_dns) { if (len < 14+20+8+240) return -1; if (frame[12]!=0x08 || frame[13]!=0x00) return -1; const u8 *ip = frame + 14; if ((ip[0]>>4)!=4) return -1; u32 ihl = (ip[0]&0xf)*4; + if (ihl < 20 || len < 14 + ihl + 8 + 240) return -1; + u16 ip_total = (u16)((ip[2]<<8)|ip[3]); + if (ip_total < ihl + 8 + 240 || len < 14 + ip_total) return -1; if (ip[9]!=17) return -1; const u8 *udp = ip + ihl; + u16 udp_len = (u16)((udp[4]<<8)|udp[5]); + if (udp_len < 8 + 240 || udp_len > ip_total - ihl) return -1; u16 dport = (u16)((udp[2]<<8)|udp[3]); if (dport != 68) return -1; const u8 *dhcp = udp + 8; @@ -309,7 +340,9 @@ static int dhcp_parse(const u8 *frame, u32 len, u32 *out_yiaddr, u32 *out_server u32 o = 240; u8 msg_type = 0; u32 server = 0; - u32 cap = len - 14; + u32 router = 0; + u32 dns = 0; + u32 cap = udp_len - 8; while (o < cap) { u8 code = dhcp[o++]; if (code == 255) break; @@ -320,10 +353,16 @@ static int dhcp_parse(const u8 *frame, u32 len, u32 *out_yiaddr, u32 *out_server if (code == 53 && l >= 1) msg_type = dhcp[o]; if (code == 54 && l >= 4) server = ((u32)dhcp[o]<<24)|((u32)dhcp[o+1]<<16)|((u32)dhcp[o+2]<<8)|dhcp[o+3]; + if (code == 3 && l >= 4) + router = ((u32)dhcp[o]<<24)|((u32)dhcp[o+1]<<16)|((u32)dhcp[o+2]<<8)|dhcp[o+3]; + if (code == 6 && l >= 4) + dns = ((u32)dhcp[o]<<24)|((u32)dhcp[o+1]<<16)|((u32)dhcp[o+2]<<8)|dhcp[o+3]; o += l; } *out_yiaddr = yi; *out_server = server; + if (out_router) *out_router = router; + if (out_dns) *out_dns = dns; return msg_type; } @@ -350,60 +389,198 @@ static void log_ipv4(const char *pre, u32 ip_hostorder) { static int dhcp_run(u32 index, const u8 mac[6]) { if (!g_net_tx || !g_net_rx_poll) { logl("[netman] DHCP: net tx/rx api missing"); return -1; } for (int i=0;i<6;i++) g_local_mac[i]=mac[i]; - g_lease_ip = 0; g_server_ip = 0; + g_lease_ip = 0; g_server_ip = 0; g_gateway_ip = 0; g_dns_ip = 0; - logl("[netman] DHCP: sending DISCOVER"); - int txrc = dhcp_send(index, 1, 0, 0); - log_hex("[netman] DHCP DISCOVER tx rc=", (u64)(i8)txrc); - if (txrc != 0) { logl("[netman] DHCP: DISCOVER tx failed"); return -1; } - - u32 offer_ip = 0, offer_srv = 0; + u32 offer_ip = 0, offer_srv = 0, offer_router = 0, offer_dns = 0; int got_offer = 0; u32 rx_frames = 0; - for (u32 attempt=0; attempt<40000 && !got_offer; attempt++) { - u32 rlen = 0; - int rc = g_net_rx_poll(index, g_rxbuf, sizeof(g_rxbuf), &rlen); - if (rc == 0 && rlen > 0) { - rx_frames++; - int t = dhcp_parse(g_rxbuf, rlen, &offer_ip, &offer_srv); - if (t == 2) { got_offer = 1; break; } /* OFFER */ + for (u32 round=0; round<4 && !got_offer; round++) { + logl("[netman] DHCP: sending DISCOVER"); + int txrc = dhcp_send(index, 1, 0, 0); + log_hex("[netman] DHCP DISCOVER tx rc=", (u64)(i8)txrc); + if (txrc != 0) { logl("[netman] DHCP: DISCOVER tx failed"); return -1; } + + for (u32 attempt=0; attempt<120000 && !got_offer; attempt++) { + u32 rlen = 0; + int rc = g_net_rx_poll(index, g_rxbuf, sizeof(g_rxbuf), &rlen); + if (rc == 0 && rlen > 0) { + rx_frames++; + int t = dhcp_parse(g_rxbuf, rlen, &offer_ip, &offer_srv, &offer_router, &offer_dns); + if (t == 2) { got_offer = 1; break; } + } + delay_spin(300); } - delay_spin(200); } log_hex("[netman] DHCP rx frames seen=", rx_frames); if (!got_offer) { logl("[netman] DHCP: no OFFER received"); return -2; } log_ipv4("[netman] DHCP OFFER ip=", offer_ip); - logl("[netman] DHCP: sending REQUEST"); - if (dhcp_send(index, 3, offer_ip, offer_srv) != 0) { logl("[netman] DHCP: REQUEST tx failed"); return -3; } - - u32 ack_ip=0, ack_srv=0; + u32 ack_ip=0, ack_srv=0, ack_router=0, ack_dns=0; int got_ack=0; - for (u32 attempt=0; attempt<40000 && !got_ack; attempt++) { - u32 rlen=0; - int rc = g_net_rx_poll(index, g_rxbuf, sizeof(g_rxbuf), &rlen); - if (rc == 0 && rlen > 0) { - int t = dhcp_parse(g_rxbuf, rlen, &ack_ip, &ack_srv); - if (t == 5) { got_ack=1; break; } /* ACK */ + for (u32 round=0; round<4 && !got_ack; round++) { + logl("[netman] DHCP: sending REQUEST"); + if (dhcp_send(index, 3, offer_ip, offer_srv) != 0) { logl("[netman] DHCP: REQUEST tx failed"); return -3; } + + for (u32 attempt=0; attempt<120000 && !got_ack; attempt++) { + u32 rlen=0; + int rc = g_net_rx_poll(index, g_rxbuf, sizeof(g_rxbuf), &rlen); + if (rc == 0 && rlen > 0) { + int t = dhcp_parse(g_rxbuf, rlen, &ack_ip, &ack_srv, &ack_router, &ack_dns); + if (t == 5) { got_ack=1; break; } + } + delay_spin(300); } - delay_spin(200); } if (!got_ack) { logl("[netman] DHCP: no ACK received"); return -4; } g_lease_ip = ack_ip; g_server_ip = ack_srv; + g_gateway_ip = ack_router ? ack_router : (offer_router ? offer_router : 0x0a000202u); + g_dns_ip = ack_dns ? ack_dns : (offer_dns ? offer_dns : g_gateway_ip); log_ipv4("[netman] DHCP ACK, leased ip=", ack_ip); log_ipv4("[netman] DHCP server=", ack_srv); + log_ipv4("[netman] gateway=", g_gateway_ip); + log_ipv4("[netman] dns=", g_dns_ip); logl("[netman] DHCP: connected"); return 0; } -static void query_netdevs(void) { +static int arp_resolve_gateway(u32 index, u32 target_ip, u8 out_mac[6]) { + memset_nm(g_pktbuf, 0, sizeof(g_pktbuf)); + u8 *p = g_pktbuf; + for (int i=0;i<6;i++) p[i]=0xff; + for (int i=0;i<6;i++) p[6+i]=g_local_mac[i]; + p[12]=0x08; p[13]=0x06; + u8 *a = p + 14; + a[0]=0x00; a[1]=0x01; + a[2]=0x08; a[3]=0x00; + a[4]=6; a[5]=4; + a[6]=0x00; a[7]=0x01; + for (int i=0;i<6;i++) a[8+i]=g_local_mac[i]; + a[14]=(u8)(g_lease_ip>>24); a[15]=(u8)(g_lease_ip>>16); a[16]=(u8)(g_lease_ip>>8); a[17]=(u8)g_lease_ip; + a[24]=(u8)(target_ip>>24); a[25]=(u8)(target_ip>>16); a[26]=(u8)(target_ip>>8); a[27]=(u8)target_ip; + logl("[netman] ARP: who-has gateway"); + if (g_net_tx(index, g_pktbuf, 60) != 0) return -1; + for (u32 attempt=0; attempt<160000; attempt++) { + u32 rlen=0; + int rc = g_net_rx_poll(index, g_rxbuf, sizeof(g_rxbuf), &rlen); + if (rc == 0 && rlen >= 42 && g_rxbuf[12]==0x08 && g_rxbuf[13]==0x06) { + const u8 *r = g_rxbuf + 14; + u16 op = (u16)((r[6]<<8)|r[7]); + u32 spa = ((u32)r[14]<<24)|((u32)r[15]<<16)|((u32)r[16]<<8)|r[17]; + if (op == 2 && spa == target_ip) { + for (int i=0;i<6;i++) out_mac[i]=r[8+i]; + logl("[netman] ARP: gateway resolved"); + return 0; + } + } + delay_spin(300); + } + logl("[netman] ARP: timeout"); + return -2; +} + +static int dns_query_a(u32 index, const u8 dst_mac[6], u32 dns_ip, const char *name, u32 *out_ip) { + memset_nm(g_pktbuf, 0, sizeof(g_pktbuf)); + u8 *p = g_pktbuf; + for (int i=0;i<6;i++) p[i]=dst_mac[i]; + for (int i=0;i<6;i++) p[6+i]=g_local_mac[i]; + p[12]=0x08; p[13]=0x00; + u8 *ip = p + 14; + u8 *udp = ip + 20; + u8 *dns = udp + 8; + dns[0]=0x12; dns[1]=0x34; + dns[2]=0x01; dns[3]=0x00; + dns[4]=0x00; dns[5]=0x01; + u32 q=12; + const char *s=name; + while (*s) { + const char *dot=s; + u32 l=0; + while (dot[l] && dot[l] != '.') l++; + dns[q++]=(u8)l; + for (u32 i=0;i>8); udp[5]=(u8)udp_len; + udp[6]=0; udp[7]=0; + ip[0]=0x45; ip[1]=0; + ip[2]=(u8)(ip_len>>8); ip[3]=(u8)ip_len; + ip[4]=0x12; ip[5]=0x34; ip[6]=0; ip[7]=0; + ip[8]=64; ip[9]=17; ip[10]=0; ip[11]=0; + ip[12]=(u8)(g_lease_ip>>24); ip[13]=(u8)(g_lease_ip>>16); ip[14]=(u8)(g_lease_ip>>8); ip[15]=(u8)g_lease_ip; + ip[16]=(u8)(dns_ip>>24); ip[17]=(u8)(dns_ip>>16); ip[18]=(u8)(dns_ip>>8); ip[19]=(u8)dns_ip; + u16 ipck=ip_checksum(ip,20); ip[10]=(u8)(ipck>>8); ip[11]=(u8)ipck; + u16 udpck=udp_checksum_ipv4(ip,udp,udp_len); udp[6]=(u8)(udpck>>8); udp[7]=(u8)udpck; + logl("[netman] DNS: query example.com A"); + if (g_net_tx(index, g_pktbuf, 14+ip_len) != 0) return -1; + for (u32 attempt=0; attempt<240000; attempt++) { + u32 rlen=0; + int rc=g_net_rx_poll(index,g_rxbuf,sizeof(g_rxbuf),&rlen); + if (rc==0 && rlen>=14+20+8+12 && g_rxbuf[12]==0x08 && g_rxbuf[13]==0x00) { + const u8 *rip=g_rxbuf+14; + u32 ihl=(rip[0]&0xf)*4; + if ((rip[0]>>4)!=4 || rip[9]!=17 || rlen<14+ihl+8+12) { delay_spin(300); continue; } + const u8 *rudp=rip+ihl; + if (rudp[2]!=0xC0 || rudp[3]!=0x01) { delay_spin(300); continue; } + const u8 *rdns=rudp+8; + if (rdns[0]!=0x12 || rdns[1]!=0x34) { delay_spin(300); continue; } + u16 an=(u16)((rdns[6]<<8)|rdns[7]); + u32 off=12; + while (offrlen) break; + u16 typ=(u16)((rdns[off]<<8)|rdns[off+1]); + u16 cls=(u16)((rdns[off+2]<<8)|rdns[off+3]); + u16 rdlen=(u16)((rdns[off+8]<<8)|rdns[off+9]); + off += 10; + if (typ==1 && cls==1 && rdlen==4 && off+4<=rlen) { + *out_ip=((u32)rdns[off]<<24)|((u32)rdns[off+1]<<16)|((u32)rdns[off+2]<<8)|rdns[off+3]; + log_ipv4("[netman] DNS example.com=", *out_ip); + return 0; + } + off += rdlen; + } + } + delay_spin(300); + } + logl("[netman] DNS: timeout"); + return -2; +} + +static int network_full_connect(u32 index, const u8 mac[6]) { + int rc = dhcp_run(index, mac); + log_hex("[netman] DHCP final rc=", (u64)(i8)rc); + if (rc != 0) return rc; + u8 gw_mac[6]; + rc = arp_resolve_gateway(index, g_gateway_ip, gw_mac); + log_hex("[netman] ARP final rc=", (u64)(i8)rc); + if (rc != 0) return rc; + u32 resolved=0; + rc = dns_query_a(index, gw_mac, g_dns_ip, "example.com", &resolved); + log_hex("[netman] DNS final rc=", (u64)(i8)rc); + return rc; +} + +static void query_netdevs(const net_conf *cfg) { if (!g_net_device_count || !g_net_device_info) { logl("[netman] net API not ready; configuration staged only"); return; } u32 count = g_net_device_count(); + int dhcp_done = 0; + int dhcp_enabled = cfg && !str_eq(cfg->mode, "disabled") && !str_eq(cfg->mode, "off") && !str_eq(cfg->ip, "static"); log_hex("[netman] netdev_count=", count); for (u32 i=0;i>8; + u32 g=(((c1>>8)&0xFF)*na+((c2>>8)&0xFF)*a)>>8; + u32 b=(((c1>>16)&0xFF)*na+((c2>>16)&0xFF)*a)>>8; + return 0xFF000000|(b<<16)|(g<<8)|r; +} + +/* ---- 字符渲染(与 FirstInit 类似,但用纯色背景) ---- */ +static void fb_char(u32 *fb, u32 ch, i64 x, i64 y, u32 fg, u32 bg) { + if (ch < ' ' || ch > '~') ch = ' '; + u32 idx = (u32)(ch - ' '); + const u8 *g = g_ascii[idx]; + for (i64 r=0; r= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c= fb_w) continue; + line[(u64)xx] = (a == 255) ? fg : blend(bg, fg, a); + } + } +} + +static void fill_rect(u32 *fb, i64 x, i64 y, i64 w, i64 h, u32 color) { + for (i64 r=0; r= fb_h) continue; + u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p); + for (i64 c=0; c= fb_w) continue; + line[(u64)xx] = color; + } + } +} + +/* ---- 终端逻辑 ---- */ +static int term_start_x, term_start_y; /* 像素坐标 */ + +static void term_init_pos(void) { + /* 居中布局,每字符 11px 宽,行高 20px */ + int margin_x = 16; + int margin_y = 16; + term_start_x = margin_x; + term_start_y = margin_y; + /* 自适应行数/列数到 framebuffer */ + int avail_w = (int)fb_w - margin_x * 2; + int avail_h = (int)fb_h - margin_y * 2; + term_w = avail_w / ASCII_STEP; + term_h = avail_h / (ASCII_H + 2); + if (term_w < 10) term_w = 10; + if (term_h < 5) term_h = 5; + if (term_w > TERM_COLS) term_w = TERM_COLS; + if (term_h > TERM_ROWS) term_h = TERM_ROWS; +} + +static void term_clear(void) { + for (int i = 0; i < TERM_MAX_CHARS; i++) { + term_ch[i] = ' '; + term_fg[i] = TEXT_FG; + } + cur_col = 0; + cur_row = 0; +} + +static void term_scroll(void) { + /* 向上滚动一行 */ + for (int r = 1; r < term_h; r++) { + for (int c = 0; c < term_w; c++) { + int src = r * TERM_COLS + c; + int dst = (r-1) * TERM_COLS + c; + term_ch[dst] = term_ch[src]; + term_fg[dst] = term_fg[src]; + } + } + /* 清空最后一行 */ + for (int c = 0; c < term_w; c++) { + term_ch[(term_h-1) * TERM_COLS + c] = ' '; + term_fg[(term_h-1) * TERM_COLS + c] = TEXT_FG; + } + cur_row = term_h - 1; + cur_col = 0; +} + +static void term_newline(void) { + cur_col = 0; + cur_row++; + if (cur_row >= term_h) term_scroll(); +} + +static void term_putc_color(char c, u32 color) { + if (c == '\n') { term_newline(); return; } + if (c == '\r') { cur_col = 0; return; } + if (c == '\t') { + int spaces = 4 - (cur_col % 4); + for (int i = 0; i < spaces; i++) term_putc_color(' ', color); + return; + } + if (c < ' ' || c > '~') c = '?'; + if (cur_col >= term_w) term_newline(); + int idx = cur_row * TERM_COLS + cur_col; + term_ch[idx] = (u8)c; + term_fg[idx] = color; + cur_col++; +} + +static void term_putc(char c) { term_putc_color(c, TEXT_FG); } + +static void term_puts_color(const char *s, u32 color) { + while (*s) term_putc_color(*s++, color); +} + +static void term_puts(const char *s) { term_puts_color(s, TEXT_FG); } + +static void term_redraw_all(void) { + /* 清屏背景 */ + fill_rect(g_fb, 0, 0, (i64)fb_w, (i64)fb_h, BG_COLOR); + /* 渲染所有字符 */ + for (int r = 0; r < term_h; r++) { + for (int c = 0; c < term_w; c++) { + int idx = r * TERM_COLS + c; + u8 ch = term_ch[idx]; + if (ch == ' ') continue; + i64 x = term_start_x + (i64)c * ASCII_STEP; + i64 y = term_start_y + (i64)r * (ASCII_H + 2); + fb_char(g_fb, ch, x, y, term_fg[idx], BG_COLOR); + } + } + /* 渲染光标 */ + if (cur_visible && cur_col < term_w && cur_row < term_h) { + i64 x = term_start_x + (i64)cur_col * ASCII_STEP; + i64 y = term_start_y + (i64)cur_row * (ASCII_H + 2); + fill_rect(g_fb, x, y + ASCII_H - 3, ASCII_W, 2, TEXT_FG); + } +} + +static void term_redraw_cursor(void) { + if (cur_visible && cur_col < term_w && cur_row < term_h) { + i64 x = term_start_x + (i64)cur_col * ASCII_STEP; + i64 y = term_start_y + (i64)cur_row * (ASCII_H + 2); + fill_rect(g_fb, x, y + ASCII_H - 3, ASCII_W, 2, TEXT_FG); + } +} + +static void term_clear_cursor(void) { + if (cur_col < term_w && cur_row < term_h) { + i64 x = term_start_x + (i64)cur_col * ASCII_STEP; + i64 y = term_start_y + (i64)cur_row * (ASCII_H + 2); + fill_rect(g_fb, x, y + ASCII_H - 3, ASCII_W, 2, BG_COLOR); + } +} + +/* ---- 输入缓冲 ---- */ +static char input_buf[256]; +static int input_len = 0; +static int input_cursor = 0; /* 在 input_buf 中的位置(支持左右移动) */ + +/* ---- 键盘扫描码 → ASCII(Set 1,与 FirstInit 共享逻辑) ---- */ +static char scan_to_ascii(u8 sc, int shift) { + static const char normal[58] = { + 0, 27, '1','2','3','4','5','6','7','8','9','0','-','=', 8, '\t', + 'q','w','e','r','t','y','u','i','o','p','[',']','\n',0, + 'a','s','d','f','g','h','j','k','l',';','\'', '`',0,'\\', + 'z','x','c','v','b','n','m',',','.','/',0,'*',0,' ' + }; + static const char shifted[58] = { + 0, 27, '!','@','#','$','%','^','&','*','(',')','_','+', 8, '\t', + 'Q','W','E','R','T','Y','U','I','O','P','{','}','\n',0, + 'A','S','D','F','G','H','J','K','L',':','"','~',0,'|', + 'Z','X','C','V','B','N','M','<','>','?',0,'*',0,' ' + }; + if (sc >= 58) return 0; + return shift ? shifted[sc] : normal[sc]; +} + +/* ---- 内置命令 ---- */ +static int str_eq(const char *a, const char *b) { + while (*a && *b) { if (*a != *b) return 0; a++; b++; } + return *a == *b; +} + +static void cmd_help(void) { + term_puts_color("Deshab Shell — 内置命令列表:\n", PROMPT_FG); + term_puts(" help 显示此帮助\n"); + term_puts(" clear 清屏\n"); + term_puts(" echo 显示文本\n"); + term_puts(" version 显示系统版本\n"); + term_puts(" uname 显示系统名称\n"); + term_puts(" date 显示当前日期\n"); + term_puts(" ver 显示内核版本(同 version)\n"); + term_puts(" about 关于 Deshab\n"); + term_puts(" reboot 重启系统\n"); + term_puts(" halt 关机(停止 CPU)\n"); + term_puts(" ls 列出根目录文件(暂未实现,需要 VFS)\n"); + term_puts(" pci 列出 PCI 设备(暂未实现)\n"); +} + +static void cmd_version(void) { + term_puts_color("Deshab OS v0.1.0\n", OK_FG); + term_puts(" Kernel: DSK (Deshab System Kernel)\n"); + term_puts(" Architecture: x86_64\n"); + term_puts(" ABI: DKM (Deshab Kernel Module) v1\n"); + term_puts(" Bootloader: Limine\n"); +} + +static void cmd_uname(void) { + term_puts("Deshab\n"); +} + +static void cmd_about(void) { + term_puts_color("=== Deshab OS ===\n", PROMPT_FG); + term_puts("单地址空间 Ring0 内核实验系统\n"); + term_puts("架构: UTSM + DSK + DKM 驱动模型\n"); + term_puts_color("Built with Clang + lld\n", DIM_FG); + term_puts_color("© 2026 Deshab Project\n", DIM_FG); +} + +static void cmd_clear(void) { + term_clear(); +} + +static void cmd_reboot(void) { + term_puts_color("Rebooting...\n", PROMPT_FG); + term_redraw_all(); + /* ACPI 重启:写 0x64 = 0xFE 触发键盘控制器复位 */ + outb(0x64, 0xFE); + /* 备用:三重故障 */ + __asm__ volatile("int $0x03"); + for(;;) __asm__("hlt"); +} + +static void cmd_halt(void) { + term_puts_color("System halted.\n", PROMPT_FG); + term_redraw_all(); + for(;;) __asm__("hlt"); +} + +static void cmd_echo(const char *args) { + if (args && *args) term_puts_color(args, TEXT_FG); + term_putc('\n'); +} + +/* 前向声明 — CMOS RTC 读取 */ +static u8 read_rtc_reg(u8 reg); +static u8 bcd_to_bin_impl(u8 bcd); + +static void cmd_date(void) { + /* 通过 CMOS RTC 读取日期 */ + u8 year = bcd_to_bin_impl(read_rtc_reg(9)); + u8 month = bcd_to_bin_impl(read_rtc_reg(8)); + u8 day = bcd_to_bin_impl(read_rtc_reg(7)); + u8 hour = bcd_to_bin_impl(read_rtc_reg(4)); + u8 minute = bcd_to_bin_impl(read_rtc_reg(2)); + u8 second = bcd_to_bin_impl(read_rtc_reg(0)); + /* 简单打印 */ + char buf[32]; + int p = 0; + /* YYYY-MM-DD HH:MM:SS */ + buf[p++] = '2'; buf[p++] = '0'; + buf[p++] = '0' + year / 10; buf[p++] = '0' + year % 10; + buf[p++] = '-'; + buf[p++] = '0' + month / 10; buf[p++] = '0' + month % 10; + buf[p++] = '-'; + buf[p++] = '0' + day / 10; buf[p++] = '0' + day % 10; + buf[p++] = ' '; + buf[p++] = '0' + hour / 10; buf[p++] = '0' + hour % 10; + buf[p++] = ':'; + buf[p++] = '0' + minute / 10; buf[p++] = '0' + minute % 10; + buf[p++] = ':'; + buf[p++] = '0' + second / 10; buf[p++] = '0' + second % 10; + buf[p] = 0; + term_puts(buf); + term_putc('\n'); +} + +static void cmd_unknown(const char *cmd) { + term_puts_color("未知命令: ", ERROR_FG); + term_puts_color(cmd, ERROR_FG); + term_putc('\n'); + term_puts_color("输入 'help' 查看可用命令\n", DIM_FG); +} + +static void cmd_not_impl(const char *cmd) { + term_puts_color(cmd, ERROR_FG); + term_puts_color(": 此命令尚未实现\n", ERROR_FG); +} + +static void execute_command(const char *cmd) { + /* 跳过前导空格 */ + while (*cmd == ' ') cmd++; + if (*cmd == 0) return; + /* 找命令名(第一个单词) */ + const char *args = cmd; + while (*args && *args != ' ') args++; + int cmd_name_len = (int)(args - cmd); + /* 复制命令名到本地缓冲 */ + char name[32]; + if (cmd_name_len >= 32) cmd_name_len = 31; + for (int i = 0; i < cmd_name_len; i++) name[i] = cmd[i]; + name[cmd_name_len] = 0; + /* 跳过参数前的空格 */ + while (*args == ' ') args++; + /* 分派 */ + if (str_eq(name, "help") || str_eq(name, "?")) cmd_help(); + else if (str_eq(name, "version") || str_eq(name, "ver")) cmd_version(); + else if (str_eq(name, "uname")) cmd_uname(); + else if (str_eq(name, "about")) cmd_about(); + else if (str_eq(name, "clear") || str_eq(name, "cls")) cmd_clear(); + else if (str_eq(name, "reboot")) cmd_reboot(); + else if (str_eq(name, "halt") || str_eq(name, "shutdown")) cmd_halt(); + else if (str_eq(name, "echo")) cmd_echo(args); + else if (str_eq(name, "date") || str_eq(name, "time")) cmd_date(); + else if (str_eq(name, "ls") || str_eq(name, "dir")) cmd_not_impl("ls"); + else if (str_eq(name, "pci")) cmd_not_impl("pci"); + else if (str_eq(name, "cat")) cmd_not_impl("cat"); + else if (str_eq(name, "cd")) cmd_not_impl("cd"); + else if (str_eq(name, "pwd")) { term_puts("/\n"); } + else if (str_eq(name, "whoami")) { term_puts("root\n"); } + else if (str_eq(name, "id")) { term_puts("uid=0(root) gid=0(root)\n"); } + else cmd_unknown(name); +} + +/* ---- CMOS RTC 读取 ---- */ +static u8 read_rtc_reg(u8 reg) { + outb(0x70, reg); + return inb(0x71); +} + +static u8 bcd_to_bin_impl(u8 bcd) { + return (u8)((bcd >> 4) * 10 + (bcd & 0x0F)); +} + +/* ---- 提示符 ---- */ +static const char *PROMPT = "deshab# "; + +static void draw_prompt(void) { + term_puts_color(PROMPT, PROMPT_FG); +} + +/* ---- 输入行编辑 ---- */ +static void redraw_input_line(void) { + /* 清除当前行从提示符开始的部分并重绘 */ + int prompt_len = 0; + while (PROMPT[prompt_len]) prompt_len++; + /* 清除整行 */ + i64 y = term_start_y + (i64)cur_row * (ASCII_H + 2); + fill_rect(g_fb, term_start_x, y, (i64)term_w * ASCII_STEP, ASCII_H, BG_COLOR); + /* 重绘提示符 */ + for (int i = 0; i < prompt_len; i++) { + i64 x = term_start_x + (i64)i * ASCII_STEP; + fb_char(g_fb, PROMPT[i], x, y, PROMPT_FG, BG_COLOR); + } + /* 重绘输入缓冲 */ + for (int i = 0; i < input_len; i++) { + i64 x = term_start_x + (i64)(prompt_len + i) * ASCII_STEP; + fb_char(g_fb, input_buf[i], x, y, TEXT_FG, BG_COLOR); + } + /* 同步 cur_col 到输入光标 */ + cur_col = prompt_len + input_cursor; + term_redraw_cursor(); +} + +static void insert_char(char c) { + if (input_len >= 255) return; + /* 在 input_cursor 处插入 */ + for (int i = input_len; i > input_cursor; i--) { + input_buf[i] = input_buf[i-1]; + } + input_buf[input_cursor] = c; + input_len++; + input_cursor++; + redraw_input_line(); +} + +static void delete_char_back(void) { + if (input_cursor == 0) return; + /* 删除 input_cursor-1 处的字符 */ + for (int i = input_cursor - 1; i < input_len - 1; i++) { + input_buf[i] = input_buf[i+1]; + } + input_len--; + input_cursor--; + input_buf[input_len] = 0; + redraw_input_line(); +} + +static void delete_char_fwd(void) { + if (input_cursor >= input_len) return; + for (int i = input_cursor; i < input_len - 1; i++) { + input_buf[i] = input_buf[i+1]; + } + input_len--; + input_buf[input_len] = 0; + redraw_input_line(); +} + +static void cursor_left(void) { + if (input_cursor > 0) { + input_cursor--; + term_clear_cursor(); + cur_col--; + if (cur_col < 0) { cur_col = term_w - 1; cur_row--; } + term_redraw_cursor(); + } +} + +static void cursor_right(void) { + if (input_cursor < input_len) { + input_cursor++; + term_clear_cursor(); + cur_col++; + if (cur_col >= term_w) { cur_col = 0; cur_row++; } + term_redraw_cursor(); + } +} + +static void cursor_home(void) { + term_clear_cursor(); + int prompt_len = 0; + while (PROMPT[prompt_len]) prompt_len++; + cur_col = prompt_len; + input_cursor = 0; + term_redraw_cursor(); +} + +static void cursor_end(void) { + term_clear_cursor(); + int prompt_len = 0; + while (PROMPT[prompt_len]) prompt_len++; + cur_col = prompt_len + input_len; + input_cursor = input_len; + term_redraw_cursor(); +} + +static void kill_to_end(void) { + input_len = input_cursor; + input_buf[input_len] = 0; + redraw_input_line(); +} + +static void kill_line(void) { + input_len = 0; + input_cursor = 0; + input_buf[0] = 0; + redraw_input_line(); +} + +/* ---- 主入口 ---- */ +__attribute__((visibility("default"))) +void dsk_entry(const dsk_boot_context *ctx) { + __asm__ volatile("cli"); + logl("[shell] boot"); + + if (!ctx || ctx->magic != 0x44534B31424F4F54ULL) { + logl("[shell] bad context"); + for(;;) __asm__("hlt"); + } + + fb_a = ctx->framebuffer_address; + fb_w = ctx->framebuffer_width; + fb_h = ctx->framebuffer_height; + fb_p = ctx->framebuffer_pitch; + g_fb = (u32 *)(u64)fb_a; + + term_init_pos(); + term_clear(); + term_redraw_all(); + + /* 启动 banner */ + term_puts_color("=== Deshab OS v0.1.0 ===\n", PROMPT_FG); + term_puts_color("欢迎使用 Deshab Shell\n", OK_FG); + term_puts_color("输入 'help' 查看可用命令\n", DIM_FG); + term_putc('\n'); + + /* 主循环 */ + int shift = 0; + int e0 = 0; + for (;;) { + draw_prompt(); + input_len = 0; + input_cursor = 0; + input_buf[0] = 0; + int prompt_len = 0; + while (PROMPT[prompt_len]) prompt_len++; + cur_col = prompt_len; + term_redraw_cursor(); + + /* 等待命令输入 */ + int cmd_done = 0; + while (!cmd_done) { + u8 st = inb(0x64); + if (!(st & 1)) { __asm__("pause"); continue; } + u8 data = inb(0x60); + /* 忽略鼠标数据(AUX 位) */ + if (st & 0x20) continue; + u8 sc = data; + if (sc == 0xE0) { e0 = 1; continue; } + if (sc == 0x2A || sc == 0x36) { shift = 1; continue; } + if (sc == 0xAA || sc == 0xB6) { shift = 0; continue; } + if (sc & 0x80) { + /* 松开事件 — 仅处理扩展键 */ + if (e0 && sc == 0x9C) { e0 = 0; } /* Enter 释放 */ + else if (e0 && sc == 0xCB) { e0 = 0; } /* Left 释放 */ + else if (e0 && sc == 0xCD) { e0 = 0; } /* Right 释放 */ + else if (e0 && sc == 0xC8) { e0 = 0; } /* Up 释放 */ + else if (e0 && sc == 0xD0) { e0 = 0; } /* Down 释放 */ + else if (e0 && sc == 0xD3) { e0 = 0; } /* Delete 释放 */ + else if (e0 && sc == 0xC7) { e0 = 0; } /* Home 释放 */ + else if (e0 && sc == 0xCF) { e0 = 0; } /* End 释放 */ + e0 = 0; + continue; + } + /* 按下事件 */ + if (e0) { + /* 扩展键 */ + if (sc == 0x4B) cursor_left(); /* Left */ + else if (sc == 0x4D) cursor_right(); /* Right */ + else if (sc == 0x47) cursor_home(); /* Home */ + else if (sc == 0x4F) cursor_end(); /* End */ + else if (sc == 0x53) delete_char_fwd(); /* Delete */ + else if (sc == 0x48) { /* Up — 命令历史暂未实现 */ } + else if (sc == 0x50) { /* Down — 命令历史暂未实现 */ } + e0 = 0; + continue; + } + /* 控制键 */ + if (sc == 0x1C) { + /* Enter — 执行命令 */ + term_clear_cursor(); + input_buf[input_len] = 0; + term_putc('\n'); + execute_command(input_buf); + cmd_done = 1; + continue; + } + if (sc == 0x0E) { + /* Backspace */ + delete_char_back(); + continue; + } + if (sc == 0x01) { + /* Esc — 清行 */ + kill_line(); + continue; + } + /* Ctrl+U (kill line) */ + if (sc == 0x15 && !shift) { + kill_line(); + continue; + } + if (sc == 0x17 && !shift) { + /* Ctrl+W — 删除到行首 */ + while (input_cursor > 0) delete_char_back(); + continue; + } + if (sc == 0x0B && !shift) { + /* Ctrl+K — 删除到行尾 */ + kill_to_end(); + continue; + } + /* 普通字符 */ + char c = scan_to_ascii(sc, shift); + if (c && c >= 32 && c <= 126) { + insert_char(c); + } + } + } +} diff --git a/CODE/shell/main.o b/CODE/shell/main.o new file mode 100644 index 0000000..7b43b8c Binary files /dev/null and b/CODE/shell/main.o differ diff --git a/LESSONS_LEARNED.md b/LESSONS_LEARNED.md index 772e719..b69e7c8 100644 --- a/LESSONS_LEARNED.md +++ b/LESSONS_LEARNED.md @@ -528,5 +528,69 @@ 1. **反推物理地址不可靠**: 用内核高半虚拟地址(BSS)反推物理地址喂给设备 DMA,物理页与 CPU 映射不保证一致,是之前 e1000 一启用 RCTL/TCTL 就卡死的根因;DMA 缓冲必须来自明确的物理页分配。 2. **低 4G 约束要显式**: e1000 等 32 位 BAR 设备的 DMA 描述符/缓冲需落在低 4G,bitmap 分配器直接把上界钳到 `0x100000000` 并保留最低 1MiB,避免 legacy 区。 +## 2026-07-02 — e1000 DHCP 联网闭环 + +### 经验 + +1. **PCI I/O 汇编必须 volatile**: `outl(0xCF8)` / `inl(0xCFC)` 访问 PCI config space 时必须使用 `asm volatile` 和 memory clobber,否则 `-O2` 下可能丢弃或重排,导致读取 BAR0 时仍拿到 vendor/device ID。 +2. **验证 TX 要看 descriptor 完成位**: 只看到写 TDT 不代表网卡已发送;轮询 TX descriptor `DD` 位可以区分“API 调用成功”和“设备真正消费 descriptor”。 +3. **DHCP 包要兼容 BOOTP 最小长度**: DHCP/BOOTP payload 补齐到 548 字节并计算 UDP checksum 后,QEMU user-net 能正常返回 OFFER/ACK。 + +### 教训 + +1. **BAR 读错会伪装成 DMA/RX 问题**: BAR0 被读成 `0x100e8086` 时,MMIO 实际指向错误地址,表现为 `STATUS=0`、TX descriptor 不完成,容易误判为 ring 或 DHCP 问题。 +2. **早期网络调试要分层验证**: 先确认 PCI BAR → MMIO STATUS → TX DD → RX frame → DHCP 解析,避免在协议层反复修改掩盖底层设备访问错误。 + +## 2026-07-02 — FirstInit 无线网络选择界面 + +### 经验 + +1. **复用现有卡片/输入框最稳**: FirstInit 已有 `draw_rounded_input`、鼠标包解析和键盘轮询,直接替换旧网络选项页为 SSID 列表 + 密码框 + Connect 按钮,可以避免新增 UI 框架。 +2. **配置先写入 user.conf 缓冲**: 当前缺 FAT32 write API,网络选择结果先进入加密配置缓冲,字段使用 `network.mode=wifi`、`network.ssid`、`network.password`、`network.ip=dhcp`、`network.dns=auto`,后续写盘可直接复用。 + +### 教训 + +1. **真实 Wi-Fi 扫描 API 尚未存在**: 当前界面只能展示模拟检测到的 SSID,后续需要网卡/无线驱动和扫描 API 后再替换数据源。 +2. **FirstInit 仍不应接管系统联网**: UI 只收集用户选择和密码;实际联网仍由 netman/驱动负责,避免用户向导和系统初始化职责混在一起。 + +## 2026-07-02 — netman 完整早期联网闭环 + +### 经验 + +1. **联网闭环要分层验收**: DHCP 只证明拿到 IP,还需要 ARP 解析网关 MAC、DNS 查询外部域名,才能证明 TX/RX 与 QEMU user-net 实际可用。 +2. **QEMU user-net 网关与 DNS 分离**: DHCP 返回网关 `10.0.2.2`,DNS 常为 `10.0.2.3`,应从 DHCP option 3/6 读取,缺失时再 fallback。 +3. **保留正式阶段日志有助于验证**: `DHCP final rc`、`ARP final rc`、`DNS final rc`、`network final rc` 足以判断完整联网状态,不需要保留临时 debug 日志。 + +### 教训 + +1. **DNS parser 边界必须谨慎**: DNS response 使用压缩 name,解析 answer 时必须处理 `0xC0` 指针并检查 `rdlen`,否则容易越界。 +2. **崩溃判断必须靠串口证据**: 用户观察到“崩溃重启”后,插桩证明确认联网阶段成功并进入 FirstInit;不能在没有阶段锚点证据时直接回滚功能。 + +## 2026-07-02 — 修复 FirstInit 网络页 SSID 指针数组崩溃 + +### 经验 + +1. **FirstInit PIE 中避免全局指针数组**: DSK 的 PIE loader 只处理有限重定位场景,UI 页面里的字符串列表应优先使用二维字符数组或栈上字符数组,避免 `const char *arr[]` 解引用崩溃。 +2. **绘制函数要用阶段锚点定位**: 对网络页按 `redraw → row → fill → stroke → ssid → meta` 分层插桩,可以精确定位崩溃在第一条 SSID 文本绘制。 + +### 教训 + +1. **看似普通的字符串数组也是重定位风险**: `static const char *g_wifi_ssids[]` 在普通 C 程序中安全,但在当前 DSK 加载 FirstInit 的 PIE 环境下会变成运行期指针重定位风险。 +2. **不要用跳过流程的调试改动进入最终版本**: 为了快速复现可以临时跳过账号/偏好页,但验证后必须恢复真实流程,避免引入与用户路径不一致的行为。 + +## 2026-07-03 — 用 Consolas TTF 替换 8x8 位图字体 + +### 经验 + +1. **裸金属 OS "用系统的 TTF" 的正确解读**: Deshab 没有 TTF 光栅化服务,"系统的 TTF" 应解读为构建时用宿主机 Windows 系统 TTF 字体(`C:\Windows\Fonts\consola.ttf`)预渲染为灰度位图,嵌入 FirstInit ELF,运行时仍走 alpha-blend 位图渲染路径。 +2. **Consolas 18px 渲染参数**: advance=10px,'A' bbox=(0,3,10,14),'g' bbox=(0,5,10,18),11x18 格子完美适配(10px 字形 + 1px 间距,ASCII_STEP=11)。等宽字体适合 UI 输入框/菜单/按钮。 +3. **统一灰度位图渲染路径**: 中文字体已用 `fb_bitmap_alpha()` 8bpp 灰度 alpha-blend,ASCII 字体改用同样的灰度位图 + alpha-blend 后,字形抗锯齿效果与中文一致,且 `fb_char()` 逻辑大幅简化(移除 8x8→10x16 缩放和边缘检测)。 +4. **构建时预渲染脚本**: 新增 `CODE/font/render_ascii.py`,用 PIL 从 Consolas TTF 渲染 ASCII 32-126 共 95 个字符到 `CODE/firstInit/ascii_bitmaps.c`(18810 字节 glyph data),通过 `#include` 嵌入 main.c。 + +### 教训 + +1. **8x8 位图字体缩放到 10x16 形状怪异**: 手工 8x8 位图字体在小写字母上使用满高设计,2x2 近邻缩放后字形不自然;TTF 预渲染直接生成目标尺寸的灰度位图,避免缩放失真。 +2. **字符范围要覆盖全部可打印 ASCII**: 原 `fb_char` 用 `ch >= ' ' && ch <= 'z'`(0x7a)截断,导致 `{` `|` `}` `~` 显示为空格;改为 `ch > '~'`(0x7e)后覆盖全部 95 个可打印字符。 +3. **布局偏移要跟随字高调整**: ASCII_H 从 16 变为 18 后,硬编码的 `y+(h-16)/2` 需改为 `y+(h-ASCII_H)/2`,wifi 行的 `y+17`(选中标记居中)需改为 `y+16`((50-18)/2=16)。 diff --git a/SYSTEM/boot/limine.conf b/SYSTEM/boot/limine.conf index 6f6ebf5..83457a7 100644 --- a/SYSTEM/boot/limine.conf +++ b/SYSTEM/boot/limine.conf @@ -34,6 +34,8 @@ verbose: yes module_cmdline: dkm:e1000 module_path: boot():/driver/net/virtio_net.drv module_cmdline: dkm:virtio_net + module_path: boot():/driver/net/ath9k.drv + module_cmdline: dkm:ath9k module_path: boot():/driver/input/ps2kbd.drv module_cmdline: dkm:ps2kbd module_path: boot():/system/deshab64/deshab.elf diff --git a/SYSTEM/boot/utsm.elf b/SYSTEM/boot/utsm.elf index d8e30e3..68557ec 100644 Binary files a/SYSTEM/boot/utsm.elf and b/SYSTEM/boot/utsm.elf differ diff --git a/SYSTEM/driver/manifest.json b/SYSTEM/driver/manifest.json index d65002f..4bf2c39 100644 --- a/SYSTEM/driver/manifest.json +++ b/SYSTEM/driver/manifest.json @@ -155,6 +155,14 @@ "depends": ["pci", "irq"], "provides": ["netdev"] }, + { + "name": "ath9k", + "path": "driver/net/ath9k.drv", + "class": "net", + "required": false, + "depends": ["pci", "irq"], + "provides": ["netdev"] + }, { "name": "ps2kbd", "path": "driver/input/ps2kbd.drv", diff --git a/SYSTEM/driver/net/ath9k.drv b/SYSTEM/driver/net/ath9k.drv new file mode 100644 index 0000000..b183006 Binary files /dev/null and b/SYSTEM/driver/net/ath9k.drv differ diff --git a/SYSTEM/driver/net/e1000.drv b/SYSTEM/driver/net/e1000.drv index 53162fe..d507d64 100644 Binary files a/SYSTEM/driver/net/e1000.drv and b/SYSTEM/driver/net/e1000.drv differ diff --git a/SYSTEM/driver/net/virtio_net.drv b/SYSTEM/driver/net/virtio_net.drv index dd4cf4a..1d8871b 100644 Binary files a/SYSTEM/driver/net/virtio_net.drv and b/SYSTEM/driver/net/virtio_net.drv differ diff --git a/SYSTEM/system/deshab64/deshab.elf b/SYSTEM/system/deshab64/deshab.elf index 124c5e8..692c218 100644 Binary files a/SYSTEM/system/deshab64/deshab.elf and b/SYSTEM/system/deshab64/deshab.elf differ diff --git a/SYSTEM/system/deshab64/network/netman.elf b/SYSTEM/system/deshab64/network/netman.elf index 775219f..62d1563 100644 Binary files a/SYSTEM/system/deshab64/network/netman.elf and b/SYSTEM/system/deshab64/network/netman.elf differ diff --git a/SYSTEM/system/deshab64/shell.elf b/SYSTEM/system/deshab64/shell.elf new file mode 100644 index 0000000..de56ea0 Binary files /dev/null and b/SYSTEM/system/deshab64/shell.elf differ diff --git a/SYSTEM/system/user/use/FirstInit.elf b/SYSTEM/system/user/use/FirstInit.elf index ba2a0cf..eb34a84 100644 Binary files a/SYSTEM/system/user/use/FirstInit.elf and b/SYSTEM/system/user/use/FirstInit.elf differ diff --git a/build.ps1 b/build.ps1 index d8082f8..8608bb3 100644 --- a/build.ps1 +++ b/build.ps1 @@ -549,9 +549,28 @@ if (-not (Test-Path $NetmanOutput)) { } Write-Host "[build] netman Output: $NetmanOutput" +$ShellDir = Join-Path $Root 'CODE\shell' +$ShellOutput = Join-Path $SystemDir 'system\deshab64\shell.elf' + +Write-Host '[build] Building shell.elf...' +try { + & $make -C "$ShellDir" -f MAKEFILE "CC=$clang" "LD=$lld" + if ($LASTEXITCODE -ne 0) { + throw "make shell failed with exit code $LASTEXITCODE" + } +} +finally { +} + +if (-not (Test-Path $ShellOutput)) { + throw "shell build did not produce $ShellOutput" +} +Write-Host "[build] shell Output: $ShellOutput" + Write-Host '[build] Building DKM network drivers...' Build-DkmDriver (Join-Path $Root 'CODE\DKM\e1000\e1000.c') (Join-Path $SystemDir 'driver\net\e1000.drv') Build-DkmDriver (Join-Path $Root 'CODE\DKM\virtio_net\virtio_net.c') (Join-Path $SystemDir 'driver\net\virtio_net.drv') +Build-DkmDriver (Join-Path $Root 'CODE\DKM\ath9k\ath9k.c') (Join-Path $SystemDir 'driver\net\ath9k.drv') Write-Host "[build] Output: $Output" Write-Host "[build] DSK Output: $DskOutput"