文本控制器
12.1 概述
瓦片地图的 layer0 和 layer1(参见瓦片地图存储器)足以实现一个功能性的文本终端或控制台输出。然而,在 8 位系统上完全通过软件实现这一点可能并不简单。光标位置计算可能需要乘法、取模运算以及额外的滚动和换行逻辑。
文本控制器通过以下功能简化了这一过程:
- 硬件光标
- 自动滚动
- 换行处理
- 其他文本显示辅助功能
与其他控制器一样,文本控制器可以映射到 I/O 端口上起始地址为 0xA0 的设备分片中。其设备索引为 0。
12.2 硬件光标
文本控制器包含一个可定制的硬件光标。光标是一个瓦片(8×12 像素),可以显示来自字形存储器的任何字符,通过 CURSOR_CHARACTER 寄存器选择。
其颜色通过 CURSOR_COLORS 配置,其工作方式与 layer1 条目类似:
- 高半字节(4 位)= 背景颜色索引
- 低半字节(4 位)= 前景颜色索引
两个索引均引用调色板存储器中的前 16 个条目。
| Name | Description | Offset | Access |
| CURSOR_Y | 设置光标垂直位置。 | 0x01 | R/W |
| CURSOR_X | 设置光标水平位置。 | 0x02 | R/W |
| CURSOR_BLINK_TIMING | 光标闪烁速度。控制光标可见/不可见的帧数。 | 0x06 | R/W |
| CURSOR_CHARACTER | 用于显示光标的字符字形 | 0x07 | R/W |
| CURSOR_COLORS | 光标颜色,格式同CURRENT_COLOR | 0x08 | R/W |
光标闪烁
闪烁时序通过 CURSOR_BLINK_TIMING 设置,单位为帧:
- 例如,
30= 显示 30 帧(60 Hz 下约 500 毫秒),隐藏 30 帧 0是特殊值,表示始终隐藏255(0xFF)是特殊值,表示始终可见
当隐藏时,下方的字符仍然可见,使得光标的行为类似于覆盖在 layer0 之上的叠加层,类似于图形模式下的精灵。
位置跟踪
光标的当前列和行(以字符单位计,而非像素)存储在 CURSOR_X 和 CURSOR_Y 中。
这些寄存器是可读写的,例如,将 CURSOR_X 设置为 1 可将光标移动到第二列。
保存与恢复
CTRL 中的两个位可以轻松地临时重定位光标:
SAVE_CURSOR存储当前的 X 和 YRESTORE_CURSOR恢复存储的 X 和 Y
仅保存位置,光标的字符和颜色不受影响。
12.3 字符输出
向 PRINT_CHAR 写入任何 8 位值,都会使用 CURRENT_COLOR 中设置的颜色,在光标位置显示相应的字形(来自字形存储器)。
在内部,字符代码写入 layer0,CURRENT_COLOR 值写入 layer1,偏移量根据光标位置和当前滚动值计算得出。
写入后,光标 X 递增。如果 X 超过最后一列(640×480 模式下为 80,320×240 模式下为 40),它将换行到下一行的开头,如果已经在最后一行,则换行到屏幕顶部。
自动滚动
硬件也支持自动滚动。CTRL 寄存器的两个控制位修改此行为:
AUTO_SCROLL_X:设置后,到达最后一可见列时将水平滚动而非换行(光标 X 保持不变)AUTO_SCROLL_Y:设置后,到达最后一行时将垂直滚动
如果启用 AUTO_SCROLL_Y 后发生垂直滚动,CTRL 中的 SCYF 标志会被置位,通知 CPU 可能需要清除新行。
滚动偏移量存储在 SCROLL_X 和 SCROLL_Y 中。这些寄存器是可读写的,因此也可以手动写入,但不得超过边界:
- 80×40 模式:X ≤ 79,Y ≤ 39
- 40×20 模式:X ≤ 39,Y ≤ 19
光标位置不受滚动设置的影响。
在大多数情况下,为模拟终端,
AUTO_SCROLL_X被禁用,而AUTO_SCROLL_Y被启用。
12.4 换行
转到下一行
写入 PRINT_CHAR 的值不会被解释为 ASCII,它们直接按原样写入 layer0。
虽然默认的字形存储器使用类似 ASCII 的布局,但这纯粹是约定俗成的。
在软件中实现换行需要以下步骤:
- 读取
CURSOR_Y - 递增它并将
CURSOR_X设置为 0 - 如果已在最后一行:
- 如果启用了垂直滚动:递增
SCROLL_Y(必要时回绕),设置SCYF标志 - 否则:将
CURSOR_Y回绕到 0
- 如果启用了垂直滚动:递增
这在 8 位 CPU 上可能代价较高。为简化此过程,CTRL 中的 DO_NL 位可执行整个序列:
- 递增 Y,设置 X = 0
- 如果 Y ≤ 最大可见行:完成
- 否则:
- 如果启用了垂直滚动:递增
SCROLL_Y(必要时回绕),设置SCYF - 否则:设置 Y = 0
- 如果启用了垂直滚动:递增
延迟换行
在默认模式下,到达最后一列会导致立即换行到下一行的开头。 这可能导致"换行故障":连续打印 80 个字符后跟一个换行符,会导致跳过两行而不是一行。
以下示例可以轻松重现此问题:
- 从光标位置 (0,0) 开始
- 打印 80 个字符 → 光标换行到 (0,1)
- 打印换行符 → 光标移动到 (0,2)
许多终端在光标已经换行的情况下会忽略换行符。
为支持此行为,CTRL 中的 WAIT_AND_WRAP 位会延迟换行。到达最后一列后,光标在 X 方向上保持越界状态,直到下一次操作:
- 如果下一次操作是
DO_NL:忽略换行,光标仅换行到下一行 - 如果下一次操作是
PRINT_CHAR:先换行,再打印字符 - 如果下一次操作是写入
CURSOR_Y:Y 改变,X 保持越界状态
12.5 寄存器总览
| Name | Description | Base-relative address | Access |
| PRINT_CHAR | 在当前光标位置写入一个字符 | 0x00 | WO |
| CURSOR_Y | 设置光标垂直位置。 | 0x01 | R/W |
| CURSOR_X | 设置光标水平位置。 | 0x02 | R/W |
| SCROLL_Y | 文本层的垂直滚动。 | 0x03 | R/W |
| SCROLL_X | 文本层的水平滚动。 | 0x04 | R/W |
| CURRENT_COLOR | 打印字符的当前文本颜色 | 0x05 | R/W |
| CURSOR_BLINK_TIMING | 光标闪烁速度。控制光标可见/不可见的帧数。 | 0x06 | R/W |
| CURSOR_CHARACTER | 用于显示光标的字符字形 | 0x07 | R/W |
| CURSOR_COLORS | 光标颜色,格式同CURRENT_COLOR | 0x08 | R/W |
| CTRL | 文本控制器控制寄存器,包含行为标志和命令 | 0x09 | varies |
12.6 寄存器
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
写入时,手动插入换行,触发与自动换行相同的效果。 (R/W)