Skip to content

About

基于 HTML 的 DOS 仿真模拟器.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

31 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HTML-DOS 6.22 — 浏览器里的 MS-DOS 仿真模拟器

纯原生 HTML5 / CSS / JavaScript 实现的 MS-DOS 风格命令行环境:80×25 文本模式、VGA Mode 13h 图形模式、批处理解释器、带持久化的虚拟磁盘,以及致敬 NES《超级马里奥兄弟》1-1 关卡的原创小游戏 MARIO.COM。无第三方库、无构建步骤。

免责声明:MARIO.COM 是非商业的致敬性原创实现。所有像素图与音符均为本项目原创绘制/作曲, 未使用任何任天堂 ROM 或受版权保护的素材文件,仅在风格与玩法上致敬原作;本项目与任天堂(Nintendo) 无任何关联,也未获其认可。MS-DOS 是 Microsoft 的商标,本项目同样与 Microsoft 无关联。

运行

方式 说明
GitHub Pages 在仓库 Settings → Pages 选择分支根目录发布,访问 https://<user>.github.io/HTML-dos-simulator/(index.html 会跳转到 dos.html)。
本地直接打开 双击 dos.html(file:// 下可用,不需要服务器)。
本地服务器 npx http-server . 或 python3 -m http.server,再打开 /dos.html。

启动后会显示模拟 BIOS 自检(按 Esc 跳过),随后执行 AUTOEXEC.BAT 并进入 C:\> 提示符。 输入 MARIO 回车即可开始游戏(MARIO /M 关闭背景音乐,MARIO /A 开局即开启自动通关,MARIO /? 查看按键)。 游戏中按 P 随时开关自动通关。

支持最新版 Chrome、Firefox、Safari、Edge,以及手机/平板浏览器(见下方“手机与平板”)。音频在首次按键/点按后启用(浏览器自动播放限制)。

手机与平板

触屏设备会自动启用触屏界面(顶栏的 Touch 开关可强制开/关):

  • 命令行:点按屏幕弹出软键盘(支持输入法逐字/联想输入);屏幕下方的按键条提供 Esc、Tab、Ctrl+C、 方向键(按住连发)、退格、F3、Enter,以及 MARIO / DIR / MENU / HELP 快捷命令;在屏幕上上下滑动回看历史输出; 开机自检时点按屏幕即可跳过。
  • 游戏手柄:运行 MARIO、PLASMA、BALL 等图形程序时自动出现:十字键、A(跳)、B(跑)、START、AUTO(自动通关)、EXIT。 支持多点触控(按住 ▶ 的同时按 A),手指在按键间滑动会自动切换按键,有震动反馈(设备支持时)。
  • 布局:竖屏时画面在上、手柄在下;横屏时隐藏顶栏和状态栏,画面居中、手柄悬浮在两侧留白处。 小屏上图形画面允许非整数倍缩放以充分利用屏幕;文本模式 80 列在竖屏上偏小,建议横屏。
  • 兼容性:脚本只使用 ES2018 语法(已用解析器检查),CSS 新特性(min()、env() 等)都有回退值, 照顾较老的手机内核和 App 内置浏览器;脚本带版本号防止新页面混用旧缓存。若浏览器仍无法启动, 页面顶部会显示红色提示和具体原因(微信/QQ 内可点“在浏览器中打开”)。
  • 已在 Playwright 的 iPhone 13 / iPhone SE / Pixel 7 模拟中验证:无横向溢出,控件不遮挡画面, 触摸按住、多点触控、输入法输入、滑动回看均可用(未在真机上测试)。

功能一览

文本模式:80×25、16 色 CGA/EGA 调色板、9×16 字符单元的位图字体(8×8 字形 + 程序生成的 CP437 制表/方块字形);闪烁块状光标(Insert 切换插入/覆盖,覆盖模式为下划线光标);1000 行滚动缓冲; 可选 CRT 效果(扫描线、光晕、暗角);4 种主题(Classic VGA 灰 #AAAAAA/黑、Green、Amber、Blue)。

页面 HUD:顶栏有主题色板、CRT 开关、帮助与出厂重置;显示器下方有铭牌、电源/硬盘/软驱/AUTO 指示灯 (硬盘和软驱灯随真实的虚拟磁盘读写闪烁)、“消磁”(切换 CRT)与“电源”(重启,文件保留)按钮; 最下方的状态栏显示视频模式、当前目录、INS/OVR 与 CAPS、DOS 时钟、C: 剩余空间。 运行 MARIO 时会出现游戏面板:分数、金币、生命、时间、自动通关开关与规划器实时统计。

行编辑:←/→(Ctrl 按词)、Home/End、Backspace/Delete、Insert、↑/↓ 历史(100 条)、F1/F3(逐字/整行 复制上一条命令)、Tab/Shift+Tab 补全命令与文件名并循环候选、Esc 清行、Ctrl+C 中止、F6/Ctrl+Z 输入 ^Z、 Ctrl+L 清屏、Shift+PgUp/PgDn 或鼠标滚轮回看、F12 快捷键帮助面板。命令行上限 127 字符(与 DOS 一致)。

虚拟文件系统:A:(1.44MB 空软盘)与 C:(512MB),8.3 文件名、大小写不敏感、统一大写显示; 文本按字符串、二进制按 Base64 存储;每次修改同步写入 localStorage;FORMAT 清空磁盘; 工具栏 Factory reset 恢复出厂内容。初始 C::COMMAND.COM、IO.SYS/MSDOS.SYS(隐藏)、 CONFIG.SYS、AUTOEXEC.BAT、README.TXT、GAMES\(PLASMA.COM、BALL.COM)、 UTILS\(示例批处理 DEMO、HELLO、ARGS、COUNT、FOREVER、MENU、GFX)、MARIO\(MARIO.COM、MARIO.TXT)、TEMP\。

内置命令(46 个,表驱动注册)

类别 命令
文件 DIR(/W /P /A[:属性] /O /S /B,*/?)、CD/CHDIR、MD/MKDIR、RD/RMDIR(/S /Q)、TYPE、COPY(通配符、/Y /-Y、a+b 合并、COPY CON)、DEL/ERASE(/P)、REN/RENAME(通配符模板)、MOVE、ATTRIB(±R ±H ±S ±A /S)、TREE(/F /A)、FC(/C /N)
系统 ECHO、CLS、VER、VOL、DATE、TIME(真实时钟,可设置偏移)、SET、PATH、PROMPT($P $G $N $_ $T $D $V $L $B $Q $H $$)、HELP、PAUSE、REM、CALL、EXIT、MEM(/C)、CHKDSK(/V)、FORMAT(/S /Q /V)、LABEL、DOSKEY /HISTORY
批处理 IF(EXIST / ERRORLEVEL / 字符串比较 / NOT / /I)、GOTO、FOR %%A IN (...) DO(含通配符)、SHIFT、CHOICE(/C /N /S /T)
过滤器 MORE、SORT(/R /+n)、FIND(/V /C /N /I),支持管道 `
图形/娱乐/界面 MODE 13 / MODE 3(MODE 80、CO80)、BEEP [Hz] [ms]、MARIO、COLOR、THEME、CRT

每个命令都支持 /?(或 HELP 命令名)。未知命令显示 Bad command or file name。 外部命令查找顺序:内部命令 → 当前目录 → PATH 中各目录,依次尝试 .COM、.EXE、.BAT。 .COM/.EXE 是带签名 HTMLDOS:<ID> 的二进制存根,执行时按签名分派到 JS 实现,因此复制到 A: 也能运行。

批处理:@ECHO OFF/ECHO ON|OFF、REM、::、%0–%9、%*、%%、%VAR%、IF、GOTO/:label、 FOR、CALL(嵌套返回)、直接调用另一个 .BAT 时链式跳转(不返回,与 DOS 一致)、SHIFT、CHOICE、 Ctrl+C 时提示 Terminate batch job (Y/N)?。

图形模式:MODE 13 进入 320×200×256(VGA 默认调色板结构),此时命令行以 40×25、8×8 字体在 图形缓冲中显示;MODE 3 返回文本模式。画面按整数倍缩放并居中(保持像素比例)。 JS API(machine.gfx):pset/pget、line(Bresenham)、rect、fillRect、circle、fillCircle、 text(8×8 位图字体)、blit(颜色 0 透明,支持翻转与颜色重映射)、setPalette/getPalette/resetPalette、 swap(双缓冲提交)、waitVBlank(≈60Hz);按键状态查询 machine.input.isDown(code)。 程序返回或按 Esc 时自动回到 80×25 文本模式并恢复原屏幕内容。示例程序:PLASMA.COM(调色板循环等离子)、 BALL.COM(弹球;方向键改变重力、空格加球、C 清空,屏幕下方实时显示方向键按下状态)。

MARIO.COM

按键 作用
← / →(或 A / D) 移动
↓(或 S) 下蹲(大体)/ 进入水管
Z 或 Space 跳跃(A 键,按住跳得更高)
X 或 Shift 奔跑(B 键)
Enter 开始 / 暂停(显示 PAUSE)
P 自动通关开 / 关(画面右上显示 AUTO 徽标,页面 HUD 上也有开关)
Esc 退出回到 DOS
  • 分辨率取舍:使用 NES 原生 256×240 的 256 色索引缓冲(MODE 13 风格的调色板与绘图 API),由界面层整数倍缩放、 上下/左右加黑边居中(letterbox)。缩放进 320×200 会破坏 16px 图块的像素网格,裁剪 40 行又会丢失 HUD 或地面, 因此选择保持原生网格(说明同样写在 js/mario.js 顶部注释中)。
  • 关卡 1-1:长度 212 图块;坑洞位于 69–70、86–88、153–154;问号方块/砖块(含蘑菇、金币、10 连金币砖、星星砖)、 位于 64 的隐藏 1UP 方块、6 根水管(57 号可进入地下奖励房,163 号为出口)、四组阶梯、Goomba ×16、Koopa ×1、 旗杆(198)与城堡。数据在 js/mario-data.js 中以数组描述,便于核对。
  • 物理:固定 60Hz 逻辑步长(与渲染分离,渲染帧率不影响手感);走/跑两档加速度与最高速度、滑行转向减速、 三档起跳速度与“按住 A / 松开 A”两种重力(静止短跳约 20px、长跳约 66px,奔跑长跳约 83px)、空中惯性、 先水平后垂直的 AABB 图块碰撞。数值由 NES 原版的子像素常数换算而来。
  • 玩法:顶问号块出金币/蘑菇;蘑菇变大,大体受伤变小并短暂无敌;小体顶砖只会弹动、大体可顶碎; 踩 Goomba 弹跳并按连踩递增计分;踩 Koopa 变壳,再碰/踩可踢壳,移动的壳会撞死其他敌人;侧撞死亡(跳起后落下的动画)、 掉坑死亡、TIME 归零死亡;死亡后重开本关并减命,生命为 0 显示 GAME OVER;100 金币加命; 旗杆按高度计分、沿杆滑下、走进城堡、剩余时间换分,显示 COURSE CLEAR! 并提示 Enter 再玩 / Esc 返回 DOS; 最高分保存在 localStorage(htmldos.mario.hiscore)。
  • 音频:Web Audio 方波/三角波合成:跳跃、踩踏、金币、蘑菇出现、变大、1UP、顶砖、碎砖、踢壳、受伤、死亡、 旗杆、过关、暂停、时间警告;原创的 8 小节背景音乐(主旋律 + 和声 + 三角波低音),时间少于 100 时加速。

自动通关(Autopilot)与 AI 风格

按 P(或页面 HUD 上的 Autopilot 开关、或 MARIO /A)后由 js/mario-ai.js 中的规划器代打, 玩家仍可用 Enter 暂停、Esc 退出。它不只是“冲向旗杆”:AI 分两层,会顶问号块、吃蘑菇和隐藏 1UP、 踩敌人踢龟壳、钻隐藏水管进地下奖励房,并且可以切换“性格”。

策略层——程序化人格(Procedural Personas)。参考 Holmgård、Liapis、Togelius、Yannakakis 的 Procedural Personas(MiniDungeons / Mario 上的“效用函数即人格”):每种人格只是对各种收益的权重不同。 需要新子目标时,列出画面内可得的机会(问号块、隐藏方块、有内容的砖块、道具、可踩的敌人、静止的龟壳、 地下的金币、隐藏水管),按 人格权重 × 价值 − 路程 打分取最高(带滞后防抖),都不值得时就去旗杆; 够不着的目标会被暂时拉黑。按 C 或 HUD 上的按钮切换,MARIO /A:HUNTER 直接指定(前几个字母即可),选择会记住。

所有风格都会去吃顶出来的蘑菇变成大马里奥,并在关底瞄准旗杆最顶端拿 5000 分:

风格 偏好 1-1 实测(确定性测试设置)
ALL-ROUND 全能 方块、蘑菇、隐藏 1UP,顺路踩敌人;不赶时不跑 顶约 10 个方块、拿到隐藏 1UP
SPEEDRUN 速通 直奔旗杆,只吃路上的蘑菇 不走水管的风格里最快(约 2130 帧)
COLLECTOR 收集者 金币第一:问号块、金币砖、地下奖励房 进奖励房,约 22 枚金币
HUNTER 猎手 踩栗子、踢龟壳、追星星 消灭敌人最多之一
EXPLORER 探索者 隐藏水管、隐藏方块 进奖励房
CAUTIOUS 稳健 不追敌人和秘密,走路为主,受伤惩罚 ×4 较慢、最稳
  • 拦截移动的蘑菇:蘑菇会沿砖块走、掉下、撞水管折返,但这些都是确定的。规划器先只推进道具,算出它 未来 240 帧的轨迹,启发式估计“最早在哪一帧能截住它”,而不是追着它当前的位置跑。
  • 旗杆顶端 5000 分:抓旗时脚高于 y=64 才是 5000 分,只有从最高一级台阶起跳才够得着。进入关底范围后, 目标换成“旗杆顶端”,A* 先把马里奥带上高台(启发式按落脚高度减去起跳高度计算),站上高台后枚举 “后退 k 帧 → 助跑 n 帧 → 按住跳跃”的宏动作,用前向模型逐个验证,取第一个能拿 5000 分的跳法; 都不行再用 8 倍预算的 A* 搜几次,最后才退回普通抓旗。

执行层——A* 前向模型规划。参考 2009 年 Mario AI Competition 冠军 Robin Baumgarten 的 A* 智能体 (源码镜像):

  • 前向模型:MarioGame.clone() 复制游戏状态(区域图块写时复制),用游戏自己的物理代码推进; 游戏是确定性的,所以预测与实际完全一致。
  • A* 搜索:动作集为 {左/右/不动} × {跳} × {跑}(去水管时加“下”),每个节点重复同一动作 2 帧; 优先级 = 已用帧数 × 0.9 + 到子目标的估计帧数(从当前速度加速到最高速、需要爬升的高度)+ 惩罚; 离目标还远时高度按“落地后”计算,避免半路乱跳造成启发式平台。死亡分支直接剪掉,受伤按人格加重惩罚; 相同状态只展开一次;节点惰性模拟。
  • 活路检查:达成子目标的节点(比如踩完一只栗子)还要通过生存检查——几种简单后续操作里至少有一种 能安全撑过 16 帧,免得落进另一群敌人中间;没到达目标时只采纳“之后被证实还能活”的部分计划,且只取前 32 帧(滚动时域),避免带着惯性冲过头。搜不到时先从真实状态重规划,再用 3 倍预算加深一次,最后才放弃该目标。
  • 计划延续:剩余计划不足 12 帧时从计划末尾的预测状态继续搜索并拼接。每次搜索限 300 个节点 / 6 ms。
  • 像人一样玩:Mario AI Championship 2012 图灵测试赛道的结论是,机器人露馅在“永远全速奔跑、 总在坑边最后一个像素起跳”。因此拟人人格给按住 B 加了少量代价(不赶时间就走路,启发式同比放大以免搜索膨胀), 贴着坑边起跳会被惩罚,完成子目标后偶尔停顿片刻(先用前向模型确认停顿是安全的)。
  • 路线叠加层:按 V 显示 AI 正在执行的计划路线(品红色点)和当前子目标(“行军蚁”虚线框 + 名称), 画面右上角的徽标显示当前风格;HUD 上显示当前目标、搜索节点数/耗时和顶块/踩敌/秘密计数。
  • 在测试中,六种风格都能从起点无伤通关 1-1,并且各自表现出不同的行为(见 tests/tests.js)。

与原版的已知差异

  • 美术为原创像素画,只在风格上接近:马里奥采用红衣/蓝背带裤/白手套配色,帧数与细节与原版不同; 地面、云、山丘、城堡等图块均为自绘。
  • 背景音乐与所有乐句(死亡、过关、Game Over、1UP 等)为原创作曲,不是原版旋律的转写—— 这与“所有音符数据均为原创”的约束一致;MarioData.MUSIC 是普通的音符表,可自行替换。
  • 未实现火焰花(大体顶出蘑菇块仍给蘑菇,仅加分)、中途检查点、烟花、城堡升旗、角色重叠时的精灵闪烁限制、 走出画面左侧后敌人回收等细节;星星已实现(无敌并撞飞敌人),但没有专用音乐。
  • 视差(远景云 0.5×、山丘 0.7×、灌木 0.85×)是按要求新增的效果,原版背景与前景同速滚动。
  • 奖励房与出口水管为简化布局;砖块碎片/顶起动画为近似实现。

代码结构

所有模块挂在全局 DOS 命名空间下,可变状态集中在 DOS.Machine 实例中(浏览器控制台可用 machine 访问)。

模块 文件 职责
Terminal js/terminal.js 字符网格、光标、滚动缓冲、文本模式渲染器、MODE 13 控制台渲染、颜色主题
Input js/input.js 键盘事件规范化、按键队列/状态、行编辑器、历史、Tab 补全
FileSystem js/filesystem.js、js/disk-image.js 虚拟磁盘、路径解析、通配符、持久化;出厂磁盘内容
Parser js/parser.js、js/batch.js 命令行切分、开关、变量展开、管道/重定向、IF/FOR;批处理解释器
Commands js/commands.js 表驱动内置命令注册表(Commands.register({...}) 即可新增命令)
Graphics js/graphics.js 索引色帧缓冲、256 色调色板、绘图原语、双缓冲
Audio js/audio.js BEEP、音效表、乐句与循环音乐合成
Programs js/programs.js 外部程序注册表:COMMAND、PLASMA、BALL、MARIO
MarioGame js/mario.js、js/mario-data.js、js/mario-ai.js MarioGame(状态与物理,可 clone())、MarioRenderer(只读渲染)、MarioProgram(宿主循环);精灵/关卡/物理/音乐数据;MarioAutoPilot(人格化效用规划 + A* 自动通关)
Machine js/machine.js 集中状态、执行管线、外壳循环、显示模式切换、BIOS 自检
— js/font.js、js/main.js 位图字体;浏览器接线(布局、渲染循环、顶栏、状态 HUD、指示灯、帮助面板、aria-live)

测试

node tests/run-tests.js            # 全部 66 项(无依赖,Node 18+)
node tests/run-tests.js Mario      # 按名称过滤

浏览器中打开 tests/test.html 运行同一套用例。覆盖 Parser、FileSystem、批处理解释器(含 Ctrl+C 终止)、 内置命令的正常/错误路径、行编辑与历史、Graphics 原语与程序进出图形模式,以及 MarioGame 物理: 短跳与长跳高度差、奔跑速度与跳跃、转向滑行、墙体碰撞、踩踏弹跳、Koopa 壳、侧撞死亡、掉坑死亡与 GAME OVER、 蘑菇变大与受伤变小(含无敌时间)、顶砖、时间耗尽、暂停、1-1 数据核对、水管往返奖励房,以及一个自动驾驶 脚本从起点跑完整个 1-1 并到达 COURSE CLEAR;自动驾驶部分验证六种 AI 风格都能无伤通关且行为各不相同 (每种都吃到蘑菇以大马里奥过关、抓到旗杆顶端 5000 分;速通在不走水管的风格里最快、收集者进奖励房拿到 15 枚以上金币、 探索者走隐藏水管、全能拿到隐藏 1UP、猎手消灭敌人最多之一、稳健跳过秘密),以及路线叠加层数据与 /A:风格 开关解析。

手动验证清单(结果)

验证环境:Node 22(单元测试)+ Playwright 驱动的 Chromium(真实键盘事件、截图检查),2026-10-20。

项目 结果
每个内置命令的正常路径与错误路径(Bad command or file name、File not found、Invalid directory、Unable to create directory、Required parameter missing、Access denied、Invalid drive specification 等) ✅ 通过(单元测试 + 手工执行)
DIR *.TXT、COPY C:\UTILS\*.BAT A:(7 个文件)、DEL A?.TMP、DEL *.* 确认提示 ✅ 通过
空输入、不存在的路径、超长命令(>127 字符被截断并响铃)、连续退格、根目录 CD .. ✅ 通过
Tab 补全(ty+Tab → TYPE,REA+Tab → README.TXT)、↑ 历史、Insert 覆盖、Shift+PgUp 回看 ✅ 通过(Chromium 真实按键)
MODE 13 后 PLASMA、BALL 正常运行,Esc 返回 80×25 文本模式并恢复原屏幕 ✅ 通过(截图确认)
MARIO 完整走完 1-1:开始、跳坑、顶问号方块、踩 Goomba、过水管、旗杆、COURSE CLEAR ✅ 通过(自动驾驶脚本在 Node 中完整通关;浏览器中截图确认旗杆滑落、进城堡、时间结算与 COURSE CLEAR 画面)
死亡后扣命重开,生命为 0 显示 GAME OVER ✅ 通过(单元测试 + 浏览器截图)
刷新页面后文件保留、最高分保留;FORMAT 后磁盘清空(/S 写回系统文件) ✅ 通过
含 IF、GOTO、FOR、CALL、CHOICE 的批处理(DEMO hello world)正确执行,FOREVER.BAT 用 Ctrl+C → Y 中止 ✅ 通过
主题切换、CRT 开关、F12 帮助面板、窄屏横向滚动、无 JS 控制台错误、file:// 直接打开 ✅ 通过
自动通关:MARIO /A 在浏览器中实时跑完 1-1(不丢命),P 键与 HUD 开关都能切换;硬盘/软驱指示灯随磁盘访问闪烁 ✅ 通过(Chromium,2026-10-29)
AI 风格:MARIO /A:COLL 在浏览器中实时(6 ms 预算)进入奖励房、21 枚金币后无伤过关;C 键 / HUD 按钮切换风格并记住选择,V 键开关路线叠加层,画面外的目标标签带方向箭头 ✅ 通过(Chromium,2026-10-29)
手机:iPhone 13 / SE / Pixel 7 竖横屏无溢出、控件不遮挡画面;点按跳过自检、软键盘(输入法)输入 ver、按键条 ↑ 取回历史、手柄按住移动、多点跳跃、AUTO、EXIT、滑动回看 ✅ 通过(Playwright 设备模拟;未在真机测试)
音频(BEEP、音效、音乐)的实际听感 ⚠️ 未自动验证:无头浏览器无音频输出,仅确认代码路径无报错;Firefox/Safari/Edge 未实机测试

已知限制

  • 不是 x86 模拟器:.COM/.EXE 只能运行内置 JS 程序(按签名分派),任意二进制会提示 Program too big to fit in memory。
  • 只支持英文键盘布局的可打印字符;手机端的触屏支持仅在浏览器设备模拟中验证过,未在真机测试。
  • EXIT 不能退出主命令解释器;不支持 XCOPY、EDIT、DELTREE、ANSI.SYS 转义序列。
  • localStorage 容量约 5MB,超过时写入会静默失败(内存中的数据仍有效)。
  • Chrome 等浏览器可能不允许网页拦截 F12(开发者工具),此时可用工具栏上的 Help (F12) 按钮。

About

基于 HTML 的 DOS 仿真模拟器.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages