feat: WiFi驱动(ath9k) + TTF字体 + shell命令行 + FirstInit WiFi跳过

新增: ath9k PCIe WiFi驱动(被动扫描/beacon解析), shell命令行(help/clear/echo/version/date/reboot/halt), Consolas 18px TTF字体, render_ascii.py

修改: dkm_net_api扩展scan接口, FirstInit无WiFi设备时跳过, DSK加载shell.elf, 非首次启动直接进shell
This commit is contained in:
xyou
2026-07-03 12:59:59 +08:00
parent 129175a5d8
commit 3662345434
31 changed files with 4437 additions and 240 deletions
+911
View File
@@ -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 <stdint.h>
#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");
}
+101 -10
View File
@@ -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;i<len;i++) d[i]=s[i];
}
static void zero_bytes(void *dst, u32 len) {
u8 *d = (u8 *)dst;
for (u32 i=0;i<len;i++) d[i]=0;
}
static void mmio_flush(void) {
if (g_mmio) (void)g_mmio[E1000_STATUS/4];
}
static void dma_fence(void) {
__asm__ volatile("" ::: "memory");
}
static int e1000_init_rings(const struct dkm_dma_api *dma) {
if (!dma || !dma->alloc_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;i<E1000_TX_DESC_COUNT;i++) {
@@ -278,13 +323,16 @@ static int e1000_init_rings(const struct dkm_dma_api *dma) {
if (dma->alloc_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;
+20
View File
@@ -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)
+35
View File
@@ -4,15 +4,40 @@
#include <utsm/types.h>
#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);
+37 -1
View File
@@ -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) {
Binary file not shown.
+63 -31
View File
@@ -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;}
BIN
View File
Binary file not shown.
+1908
View File
@@ -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,
},
};
+325 -162
View File
@@ -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<ASCII_H; sy++) {
i64 yy = y + sy;
if (ch < ' ' || ch > '~') ch = ' ';
u32 idx = (u32)(ch - ' ');
const u8 *g = g_ascii[idx];
for (i64 r=0; r<ASCII_H; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= 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<ASCII_W; sx++) {
i64 src_x = sx * 7 / (ASCII_W - 1);
if (src_x > 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<ASCII_W; c++) {
u32 a = g[r * ASCII_W + c];
if (a == 0) continue;
i64 xx = x + c;
if (xx < 0 || (u64)xx >= 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; r<WIFI_SCAN_COUNT; r++) draw_wifi_row(fb, card_x, card_y, net, r, active, fg);
static void redraw_network_row(u32 *fb, i64 card_x, i64 card_y, setup_net *net, int row, int active, i64 mx, i64 my, u32 fg) {
u32 card = CARD_BG;
i64 ry = card_y + 150 + (i64)row * 88;
cursor_erase(fb);
fill_rect(fb, card_x + 36, ry - 8, 832, 58, card);
draw_network_row(fb, card_x, card_y, net, row, active, fg);
fill_rounded_rect(fb, card_x + 470, card_y + 120, 390, 260, 12, INPUT_BG);
stroke_rounded_rect(fb, card_x + 470, card_y + 120, 390, 260, 12, 1, 0xFFD2E3F0);
fb_text(fb, "Highlighted network", card_x + 494, card_y + 148, 0xFF7F97AC, INPUT_BG);
int preview = active < WIFI_SCAN_COUNT ? active : net->selected;
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<WIFI_SCAN_COUNT; r++) {
i64 ry = card_y + 132 + (i64)r * 64;
if (*mx >= 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;i<p;i++) conf[i]^=hash[i&31];
logl("[FirstInit] user.conf encrypted in memory (disk write pending)");
}
__attribute__((visibility("default"), noreturn))
__attribute__((visibility("default")))
void dsk_entry(const dsk_boot_context *ctx) {
__asm__ volatile("cli"); /* prevent IRQ1 (ps2kbd) from racing with our polling */
logl("[FirstInit] boot");
@@ -965,12 +1114,25 @@ void dsk_entry(const dsk_boot_context *ctx) {
prefs.keyboard = 0; /* US-QWERTY */
prefs.theme = 0; /* Light */
read_prefs_page(fb, card_x, card_y, &prefs, &mx, &my, bg, fg);
/* 查询真实 WiFi 扫描结果(来自 ath9k 驱动通过 kernel_api.net.scan_*) */
wifi_query_scan(ctx->dkm_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();
}
Binary file not shown.
Binary file not shown.
+54
View File
@@ -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)")
+218 -36
View File
@@ -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<n;i++) if(a[i]!=b[i]) return 0; return 1; }
static int neq11(const char *a, const char *b) { for(int i=0;i<11;i++) if(a[i]!=b[i]) return 0; return 1; }
static int str_eq(const char *a, const char *b) {
while (*a && *b) {
if (*a != *b) return 0;
a++;
b++;
}
return *a == 0 && *b == 0;
}
static u16 r16(const u8 *p) { return (u16)p[0]|((u16)p[1]<<8); }
static u32 r32(const u8 *p) { return (u32)p[0]|((u32)p[1]<<8)|((u32)p[2]<<16)|((u32)p[3]<<24); }
@@ -216,6 +224,8 @@ static u8 g_rxbuf[2048];
static u8 g_local_mac[6];
static u32 g_lease_ip;
static u32 g_server_ip;
static u32 g_gateway_ip;
static u32 g_dns_ip;
static u32 g_dhcp_xid = 0x21030201u;
/* 累加和,用于 IPv4 头部校验 */
@@ -227,8 +237,20 @@ static u16 ip_checksum(const u8 *data, u32 len) {
return (u16)(~sum);
}
static u16 udp_checksum_ipv4(const u8 *ip, const u8 *udp, u32 udp_len) {
u32 sum = 0;
for (u32 i=12;i<20;i+=2) sum += (u32)((ip[i]<<8)|ip[i+1]);
sum += 17;
sum += udp_len;
for (u32 i=0;i+1<udp_len;i+=2) sum += (u32)((udp[i]<<8)|udp[i+1]);
if (udp_len & 1) sum += (u32)(udp[udp_len-1]<<8);
while (sum >> 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<loops;i++) { __asm__ volatile(""); }
for (volatile u32 i=0;i<loops;i++) { __asm__ volatile("pause"); }
}
/* 构造并发送一个 DHCP 报文。type: 1=DISCOVER 3=REQUEST */
@@ -267,6 +289,7 @@ static int dhcp_send(u32 index, u8 type, u32 requested_ip, u32 server_ip) {
dhcp[opt++]=255;
u32 dhcp_len = opt;
if (dhcp_len < 548) dhcp_len = 548;
u32 udp_len = 8 + dhcp_len;
u32 ip_len = 20 + udp_len;
@@ -285,20 +308,28 @@ static int dhcp_send(u32 index, u8 type, u32 requested_ip, u32 server_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);
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<l;i++) dns[q++]=(u8)dot[i];
s = dot + l;
if (*s=='.') s++;
}
dns[q++]=0;
dns[q++]=0x00; dns[q++]=0x01;
dns[q++]=0x00; dns[q++]=0x01;
u32 dns_len=q;
u32 udp_len=8+dns_len;
u32 ip_len=20+udp_len;
udp[0]=0xC0; udp[1]=0x01;
udp[2]=0x00; udp[3]=0x35;
udp[4]=(u8)(udp_len>>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 (off<rlen && rdns[off]) off += 1 + rdns[off];
off += 5;
for (u16 ai=0; ai<an && off+12<=rlen; ai++) {
if ((rdns[off]&0xC0)==0xC0) off += 2; else { while (off<rlen && rdns[off]) off += 1 + rdns[off]; off++; }
if (off+10>rlen) 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<count;i++) {
nm_net_device_info info;
@@ -425,12 +602,15 @@ static void query_netdevs(void) {
if (info.flags & DKM_NET_F_TX_READY) logl("[netman] tx=ready");
if (info.flags & DKM_NET_F_RX_READY) logl("[netman] rx=ready");
/* 对第一个具备收发能力且 link up 的接口尝试真实 DHCP 连接 */
if ((info.flags & (DKM_NET_F_LINK_UP|DKM_NET_F_TX_READY|DKM_NET_F_RX_READY))
if (!dhcp_done && dhcp_enabled &&
(info.flags & (DKM_NET_F_LINK_UP|DKM_NET_F_TX_READY|DKM_NET_F_RX_READY))
== (DKM_NET_F_LINK_UP|DKM_NET_F_TX_READY|DKM_NET_F_RX_READY)) {
dhcp_run(i, info.mac);
int drc = network_full_connect(i, info.mac);
log_hex("[netman] network final rc=", (u64)(i8)drc);
dhcp_done = 1;
}
}
if (!dhcp_enabled) logl("[netman] DHCP skipped by config");
}
__attribute__((visibility("default")))
@@ -465,7 +645,9 @@ void dsk_entry(const dsk_boot_context *ctx) {
memset_nm(&cfg, 0, sizeof(cfg));
parse_conf(g_conf, &cfg);
log_cfg(&cfg);
if (cfg.mode[0]=='d' && cfg.mode[1]=='i') logl("[netman] networking disabled by config");
query_netdevs();
if (str_eq(cfg.mode, "disabled") || str_eq(cfg.mode, "off")) {
logl("[netman] networking disabled by config");
}
query_netdevs(&cfg);
logl("[netman] done");
}
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
CC := clang
LD := ld.lld
ROOT := ../..
OUT := $(ROOT)/SYSTEM/system/deshab64/shell.elf
CFLAGS := -target x86_64-unknown-none -std=c11 -ffreestanding -fno-builtin -fno-stack-protector -fpie -mno-red-zone -mno-sse -mno-sse2 -mno-mmx -msoft-float -m64 -Wall -Wextra -I../UTSM/include -O2
LDFLAGS := -nostdlib -static -pie -z max-page-size=0x1000 -T linker.ld
C_SOURCES := main.c
OBJECTS := $(C_SOURCES:.c=.o)
.PHONY: all clean
all: $(OUT)
$(OUT): $(OBJECTS) linker.ld
@if not exist "$(ROOT)\SYSTEM\system\deshab64" mkdir "$(ROOT)\SYSTEM\system\deshab64"
$(LD) $(LDFLAGS) -o $@ $(OBJECTS)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
powershell -NoProfile -Command "Get-ChildItem -Path . -Recurse -Filter *.o | Remove-Item -Force; if (Test-Path '$(OUT)') { Remove-Item '$(OUT)' -Force }"
+16
View File
@@ -0,0 +1,16 @@
ENTRY(dsk_entry)
PHDRS
{
text PT_LOAD FLAGS(5);
rodata PT_LOAD FLAGS(4);
data PT_LOAD FLAGS(6);
}
SECTIONS
{
. = 0x1000;
.text : ALIGN(4K) { *(.text*) } :text
.rodata : ALIGN(4K) { *(.rodata*) } :rodata
.data : ALIGN(4K) { *(.data*) *(COMMON) *(.bss*) } :data
}
+631
View File
@@ -0,0 +1,631 @@
/* Deshab Shell — 命令行交互界面
* 接收 PS/2 键盘输入,执行内置命令。
* 显示在 framebuffer 上,使用 Consolas 18px 等宽字体。
*/
#include "../UTSM/include/utsm/dsk.h"
typedef signed char i8;
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef long long i64;
#define COM1 0x3F8
static __inline__ void outb(u16 port, u8 value) { __asm__ volatile("outb %0,%1"::"a"(value),"Nd"(port)); }
static __inline__ u8 inb(u16 port) { u8 v; __asm__ volatile("inb %1,%0":"=a"(v):"Nd"(port)); return v; }
static void sputc(char c) {
for (unsigned int i=0; i<100000; i++) { if (inb(COM1+5)&0x20) break; }
outb(COM1, (unsigned char)c);
}
static void swrite(const char *s) { while(*s) { if(*s=='\n')sputc('\r'); sputc(*s++); } }
static void logl(const char *s) { swrite(s); swrite("\n"); }
/* ASCII 字体 — Consolas 18px 灰度位图(与 FirstInit 共享) */
#include "../firstInit/ascii_bitmaps.c"
#define ASCII_W 11
#define ASCII_H 18
#define ASCII_STEP 11
/* 终端尺寸 */
#define TERM_COLS 100
#define TERM_ROWS 35
#define TERM_MAX_CHARS (TERM_COLS * TERM_ROWS)
/* 颜色 — 深色主题,类似经典终端 */
#define BG_COLOR 0xFF0A1428u /* 深蓝黑 */
#define TEXT_FG 0xFFE0E8F0u /* 浅灰白 */
#define PROMPT_FG 0xFF66CCFFu /* 青色提示符 */
#define ERROR_FG 0xFFFF6680u /* 红色错误 */
#define OK_FG 0xFF66FF99u /* 绿色成功 */
#define DIM_FG 0xFF7080A0u /* 灰色注释 */
static u64 fb_a, fb_w, fb_h, fb_p;
static u32 *g_fb;
/* 终端缓冲区 — 字符 + 颜色 */
static u8 term_ch[TERM_MAX_CHARS];
static u32 term_fg[TERM_MAX_CHARS];
static int term_w = TERM_COLS;
static int term_h = TERM_ROWS;
static int cur_col = 0;
static int cur_row = 0;
static int cur_visible = 1;
/* 颜色混合 */
static u32 blend(u32 c1, u32 c2, u32 a) {
u32 na=256-a;
u32 r=((c1&0xFF)*na+(c2&0xFF)*a)>>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<ASCII_H; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= fb_h) continue;
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
for (i64 c=0; c<ASCII_W; c++) {
u32 a = g[r * ASCII_W + c];
if (a == 0) continue;
i64 xx = x + c;
if (xx < 0 || (u64)xx >= 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<h; r++) {
i64 yy = y + r;
if (yy < 0 || (u64)yy >= fb_h) continue;
u32 *line = (u32 *)((u8 *)fb + (u64)yy * fb_p);
for (i64 c=0; c<w; c++) {
i64 xx = x + c;
if (xx < 0 || (u64)xx >= 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 <text> 显示文本\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);
}
}
}
}
BIN
View File
Binary file not shown.
+64
View File
@@ -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)。
+2
View File
@@ -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
Binary file not shown.
+8
View File
@@ -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",
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+19
View File
@@ -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"