Merge branch 'next-release'

This commit is contained in:
Jeff Parsons 2016-07-25 10:22:51 -07:00
commit 5cb007200c
88 changed files with 0 additions and 4863 deletions

7
404.md
View file

@ -1,7 +0,0 @@
---
layout: page
title: Page Not Found
permalink: /404.html
---
{% include random-publication.html text="This is not the page you are looking for. You found a piece of paper instead." %}

1
CNAME
View file

@ -1 +0,0 @@
www.pcjs.org

View file

@ -1,4 +0,0 @@
source 'https://rubygems.org'
gem 'github-pages'
gem 'jekyll-sitemap'
gem 'jekyll-redirect-from'

View file

@ -1,128 +0,0 @@
# Site settings
title: "PCjs Machines"
email: Jeff@pcjs.org
description: >
PCjs: Home of the original IBM PC emulator in a browser.
Classic computer simulations in JavaScript, including the IBM PC and other x86-based machines, 6502-based
machines such as the Ohio Scientific Challenger 1P, and 8080-based machines such as Space Invaders.
Includes an archive of historical PC software and publications.
baseurl: "" # "/pcjs" when using http://jeffpar.github.io or "" when using http://www.pcjs.org
url: "http://www.pcjs.org" # "http://jeffpar.github.io" or "http://www.pcjs.org"
twitter_username: jeffpar
github_username: jeffpar
left_brace: "{"
right_brace: "}"
encoding: UTF-8
# Build settings
exclude: ["**/index.html", "**/archive", "**/c64", "logs", "node_modules", "projects", "src", "**/src", "tmp", "**/tmp", "videos", "web", ".git", ".idea"]
markdown: kramdown
kramdown:
input: GFM
hard_wrap: false
gems:
- jekyll-sitemap
- jekyll-redirect-from
# Custom site settings
pcjs:
domain: pcjs.org # whereas site.url is used for linking purposes, site.pcjs.domain is used for display purposes
version: 1.23.2 # IMPORTANT: keep pcjs.version in sync with package.json:version
compiled: true # by default, the compiled pcjs.version scripts will be used (eg, pcx86.js or pcx86-dbg.js)
c1p_scripts: # if pcjs.compiled is false, the following scripts will be included instead, in the order listed
- /modules/shared/lib/defines.js
- /modules/shared/lib/nodebug.js
- /modules/shared/lib/dumpapi.js
- /modules/shared/lib/reportapi.js
- /modules/shared/lib/strlib.js
- /modules/shared/lib/usrlib.js
- /modules/shared/lib/weblib.js
- /modules/shared/lib/component.js
- /modules/c1pjs/lib/defines.js
- /modules/c1pjs/lib/panel.js
- /modules/c1pjs/lib/cpu.js
- /modules/c1pjs/lib/rom.js
- /modules/c1pjs/lib/ram.js
- /modules/c1pjs/lib/keyboard.js
- /modules/c1pjs/lib/video.js
- /modules/c1pjs/lib/serial.js
- /modules/c1pjs/lib/disk.js
- /modules/c1pjs/lib/debugger.js
- /modules/c1pjs/lib/computer.js
- /modules/shared/lib/embed.js
pcx86_scripts:
- /modules/shared/lib/defines.js
- /modules/shared/lib/nodebug.js
- /modules/shared/lib/diskapi.js
- /modules/shared/lib/dumpapi.js
- /modules/shared/lib/reportapi.js
- /modules/shared/lib/userapi.js
- /modules/shared/lib/strlib.js
- /modules/shared/lib/usrlib.js
- /modules/shared/lib/weblib.js
- /modules/shared/lib/component.js
- /modules/pcx86/lib/defines.js
- /modules/pcx86/lib/x86.js
- /modules/pcx86/lib/interrupts.js
- /modules/pcx86/lib/messages.js
- /modules/pcx86/lib/panel.js
- /modules/pcx86/lib/bus.js
- /modules/pcx86/lib/memory.js
- /modules/pcx86/lib/cpu.js
- /modules/pcx86/lib/x86seg.js
- /modules/pcx86/lib/x86cpu.js
- /modules/pcx86/lib/x86fpu.js
- /modules/pcx86/lib/x86func.js
- /modules/pcx86/lib/x86help.js
- /modules/pcx86/lib/x86mods.js
- /modules/pcx86/lib/x86ops.js
- /modules/pcx86/lib/x86op0f.js
- /modules/pcx86/lib/chipset.js
- /modules/pcx86/lib/rom.js
- /modules/pcx86/lib/ram.js
- /modules/pcx86/lib/keyboard.js
- /modules/pcx86/lib/video.js
- /modules/pcx86/lib/parallelport.js
- /modules/pcx86/lib/serialport.js
- /modules/pcx86/lib/mouse.js
- /modules/pcx86/lib/disk.js
- /modules/pcx86/lib/fdc.js
- /modules/pcx86/lib/hdc.js
- /modules/pcx86/lib/debugger.js
- /modules/pcx86/lib/state.js
- /modules/pcx86/lib/computer.js
- /modules/shared/lib/embed.js
- /modules/shared/lib/save.js
pc8080_scripts:
- /modules/shared/lib/defines.js
- /modules/shared/lib/dumpapi.js
- /modules/shared/lib/reportapi.js
- /modules/shared/lib/userapi.js
- /modules/shared/lib/strlib.js
- /modules/shared/lib/usrlib.js
- /modules/shared/lib/weblib.js
- /modules/shared/lib/component.js
- /modules/pc8080/lib/defines.js
- /modules/pc8080/lib/cpudef.js
- /modules/pc8080/lib/messages.js
- /modules/pc8080/lib/panel.js
- /modules/pc8080/lib/bus.js
- /modules/pc8080/lib/memory.js
- /modules/pc8080/lib/cpu.js
- /modules/pc8080/lib/cpustate.js
- /modules/pc8080/lib/cpuops.js
- /modules/pc8080/lib/chipset.js
- /modules/pc8080/lib/rom.js
- /modules/pc8080/lib/ram.js
- /modules/pc8080/lib/keyboard.js
- /modules/pc8080/lib/video.js
- /modules/pc8080/lib/debugger.js
- /modules/pc8080/lib/state.js
- /modules/pc8080/lib/computer.js
- /modules/shared/lib/embed.js
- /modules/shared/lib/save.js

View file

@ -1,4 +0,0 @@
developer: true
url: "http://localhost:4000"
pcjs:
compiled: false

View file

@ -1,11 +0,0 @@
{% capture page_url_without_index_html %}{{ page.url | remove: "/index.html" | remove: "/404.html" }}{% endcapture %}
{% assign splitted_url_parts = page_url_without_index_html | split: '/' %}
{% capture forLoopMaxInt %}{{ splitted_url_parts.size | minus:1 }}{% endcapture %}
{% for i in (1..forLoopMaxInt) %}
{% capture url %}{{ url }}{{ splitted_url_parts[i] }}/{% endcapture %}
{% capture path %}{{ path }}\<a href="/{{ url }}">{{ splitted_url_parts[i] | upcase }}</a>{% endcapture %}
{% if splitted_url_parts[i] == "blog" %}
{% break %}
{% endif %}
{% endfor %}
<h4>Directory of C:\<a href="{{ site.baseurl }}/">PCJS.ORG</a>{{ path }}</h4>

View file

@ -1,61 +0,0 @@
<footer class="site-footer">
<div class="wrapper">
<div class="footer-col-wrapper">
{% comment %}
<div class="footer-col footer-col-1">
<ul class="contact-list">
<li>{{ site.title }}</li>
<li><a href="mailto:{{ site.email }}">{{ site.email }}</a></li>
</ul>
</div>
<div class="footer-col footer-col-2">
<ul class="social-media-list">
{% if site.github_username %}
<li>
<a href="https://github.com/{{ site.github_username }}">
<span class="icon icon--github">
<svg viewBox="0 0 16 16">
<path fill="#828282" d="M7.999,0.431c-4.285,0-7.76,3.474-7.76,7.761 c0,3.428,2.223,6.337,5.307,7.363c0.388,0.071,0.53-0.168,0.53-0.374c0-0.184-0.007-0.672-0.01-1.32 c-2.159,0.469-2.614-1.04-2.614-1.04c-0.353-0.896-0.862-1.135-0.862-1.135c-0.705-0.481,0.053-0.472,0.053-0.472 c0.779,0.055,1.189,0.8,1.189,0.8c0.692,1.186,1.816,0.843,2.258,0.645c0.071-0.502,0.271-0.843,0.493-1.037 C4.86,11.425,3.049,10.76,3.049,7.786c0-0.847,0.302-1.54,0.799-2.082C3.768,5.507,3.501,4.718,3.924,3.65 c0,0,0.652-0.209,2.134,0.796C6.677,4.273,7.34,4.187,8,4.184c0.659,0.003,1.323,0.089,1.943,0.261 c1.482-1.004,2.132-0.796,2.132-0.796c0.423,1.068,0.157,1.857,0.077,2.054c0.497,0.542,0.798,1.235,0.798,2.082 c0,2.981-1.814,3.637-3.543,3.829c0.279,0.24,0.527,0.713,0.527,1.437c0,1.037-0.01,1.874-0.01,2.129 c0,0.208,0.14,0.449,0.534,0.373c3.081-1.028,5.302-3.935,5.302-7.362C15.76,3.906,12.285,0.431,7.999,0.431z"/>
</svg>
</span>
<span class="username">{{ site.github_username }}</span>
</a>
</li>
{% endif %}
{% if site.twitter_username %}
<li>
<a href="https://twitter.com/{{ site.twitter_username }}">
<span class="icon icon--twitter">
<svg viewBox="0 0 16 16">
<path fill="#828282" d="M15.969,3.058c-0.586,0.26-1.217,0.436-1.878,0.515c0.675-0.405,1.194-1.045,1.438-1.809
c-0.632,0.375-1.332,0.647-2.076,0.793c-0.596-0.636-1.446-1.033-2.387-1.033c-1.806,0-3.27,1.464-3.27,3.27 c0,0.256,0.029,0.506,0.085,0.745C5.163,5.404,2.753,4.102,1.14,2.124C0.859,2.607,0.698,3.168,0.698,3.767 c0,1.134,0.577,2.135,1.455,2.722C1.616,6.472,1.112,6.325,0.671,6.08c0,0.014,0,0.027,0,0.041c0,1.584,1.127,2.906,2.623,3.206 C3.02,9.402,2.731,9.442,2.433,9.442c-0.211,0-0.416-0.021-0.615-0.059c0.416,1.299,1.624,2.245,3.055,2.271 c-1.119,0.877-2.529,1.4-4.061,1.4c-0.264,0-0.524-0.015-0.78-0.046c1.447,0.928,3.166,1.469,5.013,1.469 c6.015,0,9.304-4.983,9.304-9.304c0-0.142-0.003-0.283-0.009-0.423C14.976,4.29,15.531,3.714,15.969,3.058z"/>
</svg>
</span>
<span class="username">{{ site.twitter_username }}</span>
</a>
</li>
{% endif %}
</ul>
</div>
{% endcomment %}
<div class="footer-col footer-col-3">
<p class="text">
<span style="float:right"><a href="{{ site.url }}">{{ site.pcjs.domain }}</a> website © 2012-2016 by <a href="https://twitter.com/{{ site.twitter_username }}">@{{ site.twitter_username }}</a></span><br/>
<span style="float:right">PCjs and C1Pjs released under <a href="http://gnu.org/licenses/gpl.html">GPL version 3</a></span>
</p>
</div>
</div>
</div>
</footer>

View file

@ -1,3 +0,0 @@
{% unless site.developer %}
<!-- Replace this comment with your Google Analytics script -->
{% endunless %}

View file

@ -1,23 +0,0 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width initial-scale=1" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>{% if page.title %}PCjs: {{ page.title }}{% else %}{{ site.title }}{% endif %}</title>
<meta name="description" content="{{ site.description }}">
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="{{ "/css/main.css" | prepend: site.baseurl }}">
<link rel="canonical" href="{{ page.url | replace:'index.html','' | prepend: site.baseurl | prepend: site.url }}">
<link rel="apple-touch-icon" sizes="57x57" href="{{ site.baseurl }}/versions/icons/current/pc-icon-57.png">
<link rel="apple-touch-icon" sizes="72x72" href="{{ site.baseurl }}/versions/icons/current/pc-icon-72.png">
<link rel="apple-touch-icon" sizes="76x76" href="{{ site.baseurl }}/versions/icons/current/pc-icon-76.png">
<link rel="apple-touch-icon" sizes="114x114" href="{{ site.baseurl }}/versions/icons/current/pc-icon-114.png">
<link rel="apple-touch-icon" sizes="120x120" href="{{ site.baseurl }}/versions/icons/current/pc-icon-120.png">
<link rel="apple-touch-icon" sizes="144x144" href="{{ site.baseurl }}/versions/icons/current/pc-icon-144.png">
<link rel="apple-touch-icon" sizes="152x152" href="{{ site.baseurl }}/versions/icons/current/pc-icon-152.png">
<link rel="apple-touch-icon" sizes="180x180" href="{{ site.baseurl }}/versions/icons/current/pc-icon-180.png">
<link rel="apple-touch-icon" sizes="192x192" href="{{ site.baseurl }}/versions/icons/current/pc-icon-192.png">
<link rel="apple-touch-icon" href="{{ site.baseurl }}/versions/icons/current/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="192x192" href="{{ site.baseurl }}/versions/icons/current/pc-icon-192.png">
<link rel="shortcut icon" type="image/x-icon" href="{{ site.baseurl }}/versions/icons/current/favicon.ico">
{% include machine-styles.html %}
</head>

View file

@ -1,32 +0,0 @@
<header class="site-header">
<div class="wrapper">
<a class="site-title" href="{{ site.baseurl }}/">{{ site.title }}</a>
<nav class="site-nav">
<a href="#" class="menu-icon">
<svg viewBox="0 0 18 15">
<path fill="#424242" d="M18,1.484c0,0.82-0.665,1.484-1.484,1.484H1.484C0.665,2.969,0,2.304,0,1.484l0,0C0,0.665,0.665,0,1.484,0 h15.031C17.335,0,18,0.665,18,1.484L18,1.484z"/>
<path fill="#424242" d="M18,7.516C18,8.335,17.335,9,16.516,9H1.484C0.665,9,0,8.335,0,7.516l0,0c0-0.82,0.665-1.484,1.484-1.484 h15.031C17.335,6.031,18,6.696,18,7.516L18,7.516z"/>
<path fill="#424242" d="M18,13.516C18,14.335,17.335,15,16.516,15H1.484C0.665,15,0,14.335,0,13.516l0,0 c0-0.82,0.665-1.484,1.484-1.484h15.031C17.335,12.031,18,12.696,18,13.516L18,13.516z"/>
</svg>
</a>
<div class="trigger">
{% assign sorted_pages = site.pages | sort:"menu_order" %}
{% for page in sorted_pages %}
{% if page.menu_title %}
<a class="page-link" href="{{ page.url | prepend: site.baseurl }}">{{ page.menu_title }}</a>
{% endif %}
{% endfor %}
</div>
</nav>
</div>
<div class="wrapper">
{% include breadcrumbs.html %}
</div>
</header>

View file

@ -1 +0,0 @@
[<img src="{{ site.baseurl }}{{ include.src }}" width="{{ include.width }}" height="{{ include.height }}"/>]({{ include.link }} "{{ include.alt }}")

View file

@ -1 +0,0 @@
<div class="buildpc" id="{{ include.id }}"></div>

View file

@ -1,146 +0,0 @@
{% comment %}
As discussed in /modules/markout/lib/markout.js, our Node web server recognizes the following machine properties
in the Front Matter of our Markdown documents, for compatibility with the Jekyll web server:
'id' (eg, "ibm5150")
'name' (eg, "IBM PC (Model 5150) with Monochrome Display")
'type' (eg, "c1p", "pcx86", "pc8080")
'debugger' (default is false)
'config' (default is "machine.xml")
'template' (default is "machine.xsl")
'debug' (default is false; true enables asserts and is very slow)
'uncompiled' (default is false; true is useful for debugging but slow)
'parms' (stringified object that collects additional machine properties listed below)
Note that if you want BACKTRACK support enabled in a machine, both 'debugger' and 'uncompiled' must be true.
Also note that while multiple machines ARE supported on a single page, there are some limitations; for example,
loading two or more machines of the same 'type' but with different 'debug', 'uncompiled', or 'debugger' settings
may not work as desired.
The following optional machine properties will be added to the 'parms' property:
'autoMount' (eg, {"A":{"name":"OS/2 FOOTBALL (v7.68.17)","path":"/disks/pc/os2/misc/football/FOOTBALL-76817.json"}})
'autoPower' (eg, true)
'autoStart' (eg, true)
'drives' (eg, '[{name:"68Mb Hard Disk",type:4,path:"http://archive.pcjs.org/disks/pc/fixed/68mb/win95.json"}]')
'state' (eg, "state.json")
'messages' (eg, "fault")
Finally, all our JavaScript components expect multi-word property names to use camelCase, so we automatically convert
any lower-case forms to camelCase, both here and in markout.js and components.xsl, in case we've gotten sloppy in any
of our Markdown or XML documents. Examples: 'automount' becomes 'autoMount', 'autopower' becomes 'autoPower', etc.
{% endcomment %}
{% for machine in page.machines %}
{% capture machine_type %}{{ machine.type | remove:"-dbg" }}{% endcapture %}
{% if machine_type != "c1p" %}
{% capture machine_app %}{{ machine_type }}{% endcapture %}
{% else %}
{% capture machine_app %}{{ machine_type }}js{% endcapture %}
{% endif %}
{% if machine.debugger %}
{% capture machine_file %}{{ machine_type }}-dbg{% endcapture %}
{% assign machine_debugger = true %}
{% else %}
{% capture machine_file %}{{ machine.type }}{% endcapture %}
{% if machine.type != machine_type %}
{% assign machine_debugger = true %}
{% else %}
{% assign machine_debugger = false %}
{% endif %}
{% endif %}
{% capture machine_embed %}embed{{ machine_type | upcase | replace:"X86","x86" }}{% endcapture %}
{% if machine.automount != nil %}
{% if machine.automount == "" %}
{% assign machine_autoMount = "{}" %}
{% else %}
{% capture machine_autoMount %}{{ machine.automount|jsonify }}{% endcapture %}
{% endif %}
{% else %}
{% if machine.autoMount == "" %}
{% assign machine_autoMount = "{}" %}
{% else %}
{% capture machine_autoMount %}{{ machine.autoMount|jsonify }}{% endcapture %}
{% endif %}
{% endif %}
{% if machine.autopower != nil %}
{% capture machine_autoPower %},autoPower:{{ machine.autopower }}{% endcapture %}
{% elsif machine.autoPower != nil %}
{% capture machine_autoPower %},autoPower:{{ machine.autoPower }}{% endcapture %}
{% else %}
{% assign machine_autoPower = "" %}
{% endif %}
{% if machine.autostart != nil %}
{% capture machine_autoStart %},autoStart:{{ machine.autostart }}{% endcapture %}
{% elsif machine.autoStart != nil %}
{% capture machine_autoStart %},autoStart:{{ machine.autoStart }}{% endcapture %}
{% else %}
{% assign machine_autoStart = "" %}
{% endif %}
{% unless machine.config %}
{% assign machine_config = "machine.xml" %}
{% else %}
{% assign machine_config = machine.config %}
{% endunless %}
{% if machine.drives != nil %}
{% if machine.drives == "" %}
{% assign machine_drives = ",drives:[]" %}
{% else %}
{% capture machine_drives %},drives:{{ machine.drives }}{% endcapture %}
{% endif %}
{% else %}
{% assign machine_drives = "" %}
{% endif %}
{% unless machine.template %}
{% assign machine_template = "" %}
{% else %}
{% assign machine_template = machine.template %}
{% endunless %}
{% capture machine_parms %}{{ site.left_brace }}autoMount:{{ machine_autoMount }}{{ machine_autoPower }}{{ machine_autoStart }}{{ machine_drives }},state:"{{ machine.state }}",messages:"{{ machine.messages }}"{{ site.right_brace }}{% endcapture %}
{% if site.pcjs.compiled == true and machine.uncompiled != true %}
{% capture machine_script %}<script type="text/javascript" src="{{ site.baseurl }}/versions/{{ machine_app }}/{{ site.pcjs.version }}/{{ machine_file }}.js"></script>{% endcapture %}
{% unless machine_scripts contains machine_script %}
{{ machine_script }}
{% endunless %}
{% capture machine_scripts %}{{ machine_scripts }}{{ machine_script }}{% endcapture %}
{% if machine_template == "" %}
{% capture machine_template %}{{ site.baseurl }}/versions/{{ machine_app }}/{{ site.pcjs.version }}/components.xsl{% endcapture %}
{% endif %}
{% else %}
{% if machine_type == "c1p" %}{% assign array_scripts = site.pcjs.c1p_scripts %}{% endif %}
{% if machine_type == "pcx86" %}{% assign array_scripts = site.pcjs.pcx86_scripts %}{% endif %}
{% if machine_type == "pc8080" %}{% assign array_scripts = site.pcjs.pc8080_scripts %}{% endif %}
{% for script in array_scripts %}
{% if script != "/modules/shared/lib/nodebug.js" or machine.debug != true %}
{% capture machine_script %}<script type="text/javascript" src="{{ site.baseurl }}{{ script }}"></script>{% endcapture %}
{% unless machine_scripts contains machine_script %}
{{ machine_script }}
{% if script contains "shared/lib/defines.js" %}
{% if site.pcjs.private %}
{{ machine_script | replace:"defines.js","private.js" }}
{% endif %}
{% endif %}
{% if script contains "js/lib/defines.js" %}
{% if machine_debugger != true %}
{{ machine_script | replace:"defines.js","nodebugger.js" }}
{% endif %}
{% endif %}
{% endunless %}
{% capture machine_scripts %}{{ machine_scripts }}{{ machine_script }}{% endcapture %}
{% endif %}
{% endfor %}
{% if machine_template == "" %}
{% if machine_app != "c1pjs" %}
{% capture machine_template %}{{ site.baseurl }}/modules/shared/templates/components.xsl{% endcapture %}
{% else %}
{% capture machine_template %}{{ site.baseurl }}/modules/{{ machine_app }}/templates/components.xsl{% endcapture %}
{% endif %}
{% endif %}
{% endif %}
<script type="text/javascript">{{ machine_embed }}('{{ machine.id }}','{{ machine_config }}','{{ machine_template }}','{{ machine_parms }}');</script>
{% if page.build %}
<script type="text/javascript" src="{{ site.baseurl }}/modules/build/lib/build.js"></script>
<script type="text/javascript">buildPC("{{ page.build }}");</script>
{% endif %}
{% endfor %}

View file

@ -1,21 +0,0 @@
{% for machine in page.machines %}
{% capture machine_type %}{{ machine.type | remove:"-dbg" }}{% endcapture %}
{% if machine_type != "c1p" %}
{% capture machine_app %}{{ machine_type }}{% endcapture %}
{% else %}
{% capture machine_app %}{{ machine_type }}js{% endcapture %}
{% endif %}
{% unless site.pcjs.compiled == true and machine.uncompiled != true %}
{% if machine_type != "c1p" %}
{% capture machine_style %}{{ site.baseurl }}/modules/shared/templates/components.css{% endcapture %}
{% else %}
{% capture machine_style %}{{ site.baseurl }}/modules/{{ machine_app }}/templates/components.css{% endcapture %}
{% endif %}
{% else %}
{% capture machine_style %}{{ site.baseurl }}/versions/{{ machine_app }}/{{ site.pcjs.version }}/components.css{% endcapture %}
{% endunless %}
{% unless machine_styles contains machine_style %}
<link rel="stylesheet" type="text/css" href="{{ machine_style }}">
{% endunless %}
{% capture machine_styles %}{{ machine_styles }}{{ machine_style }}{% endcapture %}
{% endfor %}

View file

@ -1,10 +0,0 @@
<div class="machine" id="{{ include.id }}">
{% assign machine_name = "PCjs: The Virtual IBM PC" %}
{% for machine in page.machines %}
{% if include.id == machine.id and machine.name%}
{% assign machine_name = machine.name %}
{% break %}
{% endif %}
{% endfor %}
[{{ machine_name }}]
</div>

View file

@ -1,8 +0,0 @@
{% comment %}
Used by pages like the one in /api/v1/dump/ to simulate a client-side API.
{% endcomment %}
{% for script in page.scripts %}
<script type="text/javascript" src="{{ site.baseurl }}{{ script }}"></script>
{% endfor %}

View file

@ -1,42 +0,0 @@
{% assign random_id = "random" %}
<div id="{{ random_id }}"></div>
<script id="randomize" type="text/javascript">
(function() {
var p = document.getElementById('{{ random_id }}');
if (p) {
var mags = ['BYTE-1975-11:98',
'MSJ-1986-10:34', 'MSJ-1987-05:90',
'PCTJ-1983-07:210', 'PCTJ-1983-09:224', 'PCTJ-1983-11:238',
'PCTJ-1984-01:214', 'PCTJ-1984-02:206', 'PCTJ-1984-03:216', 'PCTJ-1984-04:232',
'PCTJ-1984-05:234', 'PCTJ-1984-06:230', 'PCTJ-1984-07:210', 'PCTJ-1984-08:214',
'PCTJ-1984-09:215', 'PCTJ-1984-10:226', 'PCTJ-1984-11:228', 'PCTJ-1984-12:240',
'PCTJ-1985-01:214', 'PCTJ-1985-02:194', 'PCTJ-1985-03:202', 'PCTJ-1985-04:204',
'PCTJ-1985-05:244', 'PCTJ-1985-06:216', 'PCTJ-1985-07:184', 'PCTJ-1985-08:200',
'PCTJ-1985-09:196', 'PCTJ-1985-10:204', 'PCTJ-1985-11:220', 'PCTJ-1985-12:218',
'PCTJ-1986-01:212', 'PCTJ-1986-02:214', 'PCTJ-1986-03:220', 'PCTJ-1986-04:220',
'PCTJ-1986-05:222', 'PCTJ-1986-06:222', 'PCTJ-1986-07:198', 'PCTJ-1986-08:212',
'PCTJ-1986-09:230', 'PCTJ-1986-10:228', 'PCTJ-1986-11:228', 'PCTJ-1986-12:232',
'PCTJ-1987-01:216', 'PCTJ-1987-02:222', 'PCTJ-1987-03:214', 'PCTJ-1987-04:218',
'PCTJ-1987-05:240', 'PCTJ-1987-06:236', 'PCTJ-1987-07:230', 'PCTJ-1987-08:250',
'PCTJ-1987-09:252', 'PCTJ-1987-10:231', 'PCTJ-1987-11:263', 'PCTJ-1987-12:242',
'PCTJ-1988-01:200', 'PCTJ-1988-02:200', 'PCTJ-1988-03:182',
'PCTJ-1988-05:196', 'PCTJ-1988-06:180', 'PCTJ-1988-07:164', 'PCTJ-1988-08:166',
'PCTJ-1988-09:174', 'PCTJ-1988-10:170', 'PCTJ-1988-11:176', 'PCTJ-1988-12:168',
'PCTJ-1989-04:148'
];
var p2 = document.createElement('p');
p2.appendChild(document.createTextNode('{{ include.text }}'));
var div1 = document.createElement('div'); div1.setAttribute('class', 'common-image-gallery');
var div2 = document.createElement('div'); div2.setAttribute('class', 'common-image-frame'); div1.appendChild(div2);
var div3 = document.createElement('div'); div3.setAttribute('class', 'common-image-link'); div2.appendChild(div3);
var mag = mags[Math.floor(Math.random() * mags.length)];
var parts = mag.split(':'), issue = parts[0], page = Math.floor(Math.random() * parseInt(parts[1])) + 1;
parts = mag.split('-');
var name = parts[0].toLowerCase();
var a = document.createElement('a'); a.setAttribute('href', '/pubs/pc/magazines/' + name + '/' + issue + '/#page-' + page); div3.appendChild(a);
var img = document.createElement('img'); img.setAttribute('src', 'http://archive.pcjs.org/pubs/pc/magazines/' + name + '/' + issue + '/thumbs/' + issue + ' ' + page + '.jpeg'); img.setAttribute('width', '200'); a.appendChild(img);
p.parentNode.insertBefore(p2, p.nextSibling);
p2.parentNode.insertBefore(div1, p2.nextSibling)
}
})();
</script>

View file

@ -1,16 +0,0 @@
{% assign include_width = "" %}
{% assign include_height = "" %}
{% if include.width %}
{% capture include_width %} width="{{ include.width }}"{% endcapture %}
{% endif %}
{% if include.height %}
{% capture include_height %} height="{{ include.height }}"{% endcapture %}
{% endif %}
<div class="screenshot-frame">
<div class="screenshot-image">
<a href="{{ site.baseurl }}{{ include.link }}">
<img src="{{ site.baseurl }}{{ include.src }}"{{ include_width }}{{ include_height }} alt="{{ include.title }}"/>
</a>
</div>
<span class="screenshot-label">{{ include.title }}</span>
</div>

View file

@ -1,26 +0,0 @@
---
layout: default
---
<div class="home">
{{ content }}
<h1 class="page-heading">Posts</h1>
<ul class="post-list">
{% for post in site.posts %}
<li>
<span class="post-meta">{{ post.date | date: "%b %-d, %Y" }}</span>
<h2>
<a class="post-link" href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a>
</h2>
{{ post.excerpt | replace:'</p>','...</p>'}}
</li>
{% endfor %}
</ul>
<p class="rss-subscribe">subscribe <a href="{{ "/feed.xml" | prepend: site.baseurl }}">via RSS</a></p>
</div>

View file

@ -1,26 +0,0 @@
<!DOCTYPE html>
<html>
{% include head.html %}
<body>
{% include header.html %}
<div class="page-content">
<div class="wrapper">
{{ content }}
</div>
</div>
{% include footer.html %}
{% include machine-engines.html %}
{% include page-scripts.html %}
{% include google-analytics.html %}
</body>
</html>

View file

@ -1,16 +0,0 @@
---
layout: default
---
<div class="post">
{% if page.display_title %}
<header class="post-header">
<h1 class="post-title">{{ page.title }}</h1>
</header>
{% endif %}
<article class="post-content">
{{ content }}
</article>
</div>

View file

@ -1,15 +0,0 @@
---
layout: default
---
<div class="post">
<header class="post-header">
<h1 class="post-title">{{ page.title }}</h1>
<p class="post-meta">{{ page.date | date: "%b %-d, %Y" }}{% if page.author %} • {{ page.author }}{% endif %}{% if page.meta %} • {{ page.meta }}{% endif %}</p>
</header>
<article class="post-content">
{{ content }}
</article>
</div>

View file

@ -1,78 +0,0 @@
<!DOCTYPE html>
<html>
{% include head.html %}
<body>
{% include header.html %}
<div class="page-content">
<div class="wrapper">
{{ content }}
<div style="width:680px;margin-left:auto;margin-right:auto;">
<div style="float:left;margin-top:350px;margin-right:8px;">
<button id="prevPDF" style="font-size:large;">&lt;</button>
</div>
<div style="float:left;">
<iframe id="framePDF" src="" width="550" height="800"></iframe>
</div>
<div style="float:left;margin-top:350px;margin-left:8px;">
<button id="nextPDF" style="font-size:large;">&gt;</button>
</div>
</div>
</div>
</div>
{% include footer.html %}
<script id="initFrame" type="text/javascript">
(function() {
var aParms = {};
var sParms = window.location.search.substr(1);
var match, pl = /\+/g, search = /([^&=]+)=?([^&]*)/g;
var decode = function(s) { return decodeURIComponent(s.replace(pl, " ")); };
while ((match = search.exec(sParms))) aParms[decode(match[1])] = decode(match[2]);
var pdf = aParms['url'];
var curPage = parseInt(aParms['page'], 10) || 1;
var totalPages = parseInt(aParms['total'], 10) || 999;
var frame = document.getElementById('framePDF');
if (pdf && frame) {
pdf = pdf.replace('/archive/', '/');
var i = pdf.indexOf("/pages/");
if (i > 0) {
var sReturnLink = pdf.substr(0, i+1);
var sReturnPath = sReturnLink.slice(0, -1).toUpperCase().replace(/\//g, '\\');
var e = document.getElementById('returnLink');
if (e) e.setAttribute('href', sReturnLink);
e = document.getElementById('returnPath');
if (e) e.textContent = sReturnPath;
}
i = pdf.indexOf('%20');
if (i < 0) i = pdf.indexOf(' ');
if (i > 0) pdf = pdf.substr(0, i);
var setPage = function(page) {
frame.setAttribute('src', 'http://archive.pcjs.org' + pdf + ' ' + curPage + '.pdf');
};
setPage(curPage);
var buttonPrev = document.getElementById('prevPDF');
if (buttonPrev) {
buttonPrev.onclick = function() {
if (curPage > 1) setPage(--curPage);
};
}
var buttonNext = document.getElementById('nextPDF');
if (buttonNext) {
buttonNext.onclick = function() {
if (curPage < totalPages) setPage(++curPage);
};
}
}
})();
</script>
{% include google-analytics.html %}
</body>
</html>

View file

@ -1,56 +0,0 @@
---
layout: post
title: "A Blog That's Not A Blog"
date: 2013-11-20 11:00:00
category: News
permalink: /blog/2013/11/20/
---
As you may have noticed (or not), the [JSMachines](http://jsmachines.net/) website had a very modest makeover
recently.
Originally, the site was a smattering of HTML files, along with some XML files that I was rendering as HTML
using some simple XSL stylesheets. However, I was tired of having one set of files for the website to explain
things and a different set of files on [GitHub](http://github.com) that explained other things -- many
of those things being the SAME things.
So this month, I decided to eliminate all the HTML files. As you browse the site, you're simply navigating
folders from the **GitHub** project and reading the project's **README.md** files.
There's a single PHP script responsible for transforming a folder's default document (either **README.md**
or **machine.xml**) to HTML, as well as displaying the current directory across the top and a directory listing
down the left-hand side.
The same script provides support for a subset of the [Markdown](http://daringfireball.net/projects/markdown/)
syntax, which is more than sufficient to handle all the site's **README.md** files. I probably should
have used a third-party Markdown library, but this was more educational, and it was easy to add extra features,
like the ability to embed JavaScript machines with a single Markdown-style link; eg:
[IBM PC](/devices/pcx86/machine/5150/mda/64kb/ "PCjs:ibm5150")
The script takes care of the rest, adding the appropriate stylesheets and PCjs scripts automatically.
I had more grandiose plans, including a command-line prompt written in JavaScript that would allow you to
navigate the site exactly as you would an IBM PC hard drive from a "DOS prompt", and I may try something
like that later, but don't hold your breath.
I've tried to improve the organization of all the [Machine Configuration Files](/devices/pcx86/machine/) as well.
The variety of configurations was getting out of hand. It's a bit tidier now, but there's still room for
improvement.
My workflow is improving, too. I'm more comfortable with [GitHub](http://github.com) now,
and I recently switched from Eclipse to JetBrains' [WebStorm](http://www.jetbrains.com/webstorm) (well,
actually [PhpStorm](http://www.jetbrains.com/phpstorm), since it's a superset of WebStorm, although I did start
with WebStorm), and the new development environment is feeling pretty good now. I've had zero problems with
[JetBrains](http://www.jetbrains.com) products and I'm seriously impressed with their quality and completeness,
so I have no qualms about moving from the "free" Eclipse platform to the $99 PhpStorm IDE.
The nice thing about the new GitHub-centric approach is that it's easy to "push" changes to both the repository
and the website. I update one or more **README.md** files, "Commit and Push" from the IDE, then "pull" from GitHub
on the web server.
This so-called blog is more of the same: **README.md** files in a series of folders. The only question now:
will this actually evolve into a series...?
*[@jeffpar](http://twitter.com/jeffpar)*
*November 20, 2013*

View file

@ -1,19 +0,0 @@
---
layout: post
title: New Year, New Directions
date: 2014-01-01 11:00:00
category: Goals
permalink: /blog/2014/01/01/
---
Initial goals for 2014 include
- Setting up a new web server running node.js, using either AWS, Google Compute Engine or Windows Azure;
- Porting this project to work with node.js, which includes rewriting all the PHP code as server-side JavaScript;
- Deciding whether to separate PCjs from C1Pjs for the new web site, or simply make PCjs.org a mirror of jsmachines.net;
- Deciding how best (or even whether) to accomodate PCjs running from both **node** and **non-node** web servers.
I guess we'll learn more as the year progresses.
*[@jeffpar](http://twitter.com/jeffpar)*
*January 20, 2014*

View file

@ -1,56 +0,0 @@
---
layout: post
title: Running on Azure
date: 2014-03-30 11:00:00
category: Web Servers
permalink: /blog/2014/03/30/
---
Publishing a Node-based site to [Azure](http://azure.com) was painless, thanks to their friendly web portal and
GitHub integration. Getting a fully operational site, however, took a bit more time.
Unfortunately, because Azure's underlying server technology is Windows-based (IIS), some of the same Windows/Unix
portability problems that have plagued us for decades still plague us today: **carriage returns** and **backslashes**.
Even though all PCjs text files in my project contain only linefeeds, IIS would serve them up with CR/LF
instead. I first noticed this on the client side, when an XML file retrieved via *XMLHttpRequest()* came back
full of CR/LFs, and later on the server side, when *fs.readFile()* returned a Markdown file filled with CR/LFs.
I wondered if the CR/LF transformation had happened when Azure pulled all my files from GitHub, because
while I can understand some whitespace inconsistencies across web servers, I would never expect file system calls
on the server to modify file contents.
I finally confirmed that the files were indeed modified on the server, by using Azure's FTP browser. For example,
[us83-buttons-minimal.xml](/devices/pcx86/keyboard/us83-buttons-minimal.xml) is currently 622 bytes locally, but on the
Azure server, the reported size is 632 bytes -- one extra CR for each of the file's 10 lines. After a little more
digging, I [learned something new](http://git-scm.com/book/ch7-1.html#Formatting-and-Whitespace) about **Git**: it
has a setting called `core.autocrlf` which, for me on OS X, defaults to `input` (meaning "convert CR/LF to LF on commit
but do NOT convert LF back to CR/LF on check-out"). But Azure apparently sets this to `true`, causing all LFs to be
converted to CR/LF.
Regarding slashes, even when path components contained only slashes, *path.join()* would return paths with
backslashes. And unfortunately, this behavior varies from Node module to module. For example, I use the NPM
[glob](https://www.npmjs.org/package/glob) module, and even when the input path to *glob()* contains backslashes,
its output paths do not.
In the process of fixing those portability issues, I also had some trouble getting Azure logging to work as
documented. Setting `loggingEnabled: true` in **/IISNode.yml** would generate logs in **/site/wwwroot/iisnode/**,
but the logs were numerous and poorly organized.
And the Azure command-line tool that was *supposed* to enable real-time log-streaming to the console:
azure site log tail pcjs
would happily report:
Welcome, you are now connected to log-streaming service
but it would NEVER display anything but deployment information. Instead, I had to browse the log files using Azure's
"FTP DIAGNOSTIC LOGS" link on the web portal -- which presents the logs as one big, ugly, fragmented mess:
![Azure Logs](/blog/images/iisnode-logs.jpg)
Sigh. But at least the site is up and fully operational now.
*[@jeffpar](http://twitter.com/jeffpar)*
*March 30, 2014*

View file

@ -1,47 +0,0 @@
---
layout: post
title: Browser Compatibility Woes
date: 2014-03-31 11:00:00
category: JavaScript
permalink: /blog/2014/03/31/
---
While JavaScript has been doing a good job of delivering on the old "write once, run everywhere" promise that its
[unrelated namesake](http://www.java.com) coined, the "hook once, deliver everywhere" promise seems less fulfilled.
Not that anyone ever made such a promise.
Specifically, I'm talking about DOM events. Take the HTML5 &lt;canvas&gt; element, for example. If I give
it a `contenteditable="true"` attribute, it will play nicely with my JavaScript app in a Mobile Safari browser,
by popping up the device's soft keyboard in response to the canvas element receiving focus (ie, when you tap on it).
The Silk browser on a Kindle Fire, however, is another story -- it acts like it has no idea what `contenteditable`
means. Even if I display an actual &lt;input&gt; text field alongside the &lt;canvas&gt;, and attach all the same
input event handlers to the text field instead of the canvas, the Kindle Fire's soft keyboard will pop up, but my
input event handlers still won't fire.
I've done what testing I can with the Android SDK and the "Android Virtual Device Manager", and in general, support
looks fine -- you click/tap on the PC's screen, the soft keyboard pops up, and typing works. So I guess Silk is just
an outlier. I'm not sure what I'll do about it yet (or even what I can do).
![PCjs in AVD](/blog/images/avd-tablet.jpg)
---
Here's another issue I've yet to resolve.
When Apple released iOS 7.0, PCjs went from being a rock-solid web application on
2nd/3rd/4th-generation iPads to a very flaky web application. It's still rock-solid on 5th-generation iPads
(the iPad Air and iPad Mini w/Retina Display), and so I suspect a bug in Apple's JavaScript engine that's specific
to their older A5 processor.
It may be possible to work around the bug, but I haven't yet isolated exactly what code
sequence(s) are failing. For now, this is the most serious unresolved PCjs bug I'm aware of.
---
If you're having a problem (or trouble with a device) that I've not already mentioned, [let me know](mailto:Jeff@pcjs.org).
Thanks.
*[@jeffpar](http://twitter.com/jeffpar)*
*March 31, 2014*

View file

@ -1,23 +0,0 @@
---
layout: post
title: The Latest in Emulator Technology
date: 2014-04-01 11:00:00
category: JavaScript
permalink: /blog/2014/04/01/
---
Announcing **InternetJS: The Internet Emulator**, the world's smallest JavaScript application capable of emulating the entire Internet.
And like all PCjs applications, there's nothing to install. It runs safely and securely from any web browser.
Check out the ALPHA release demo below.
<iframe width="720" height="512" src="http://bing.com/" style="border-webkit-transform:scale(0.5);-moz-transform-scale(0.5);border:1px solid black;border-radius:15px;overflow:auto;width:100%;background-color:#FAEBD7;"></iframe>
Disclaimer: InternetJS may not be suitable for everyone. Ask your doctor if InternetJS is right for you. Side-effects may include:
- Increased awareness
- Loss of appetite after eating large meals
- Inability to forget things you never wanted to remember
*[@jeffpar](http://twitter.com/jeffpar)*
*April 1, 2014*

View file

@ -1,26 +0,0 @@
---
layout: post
title: "What's New in 1.13.0"
date: 2014-04-12 11:00:00
category: Releases
permalink: /blog/2014/04/12/
---
The latest version adds support for "software manifests", which you can read more about [here](/apps/). Basically, manifests
are simple XML files that describe a piece of software (an application, an operating system, whatever). They can also
link to a PCjs machine configuration capable of running the software, along with a "ready-to-run" machine state file.
Conversely, a PCjs machine XML file can refer back to the manifest, to obtain a list of disk images.
Here are some [Demos](/apps/pc/) of "ready-to-run" apps on [PCjs](/docs/about/).
There have been lots of server-side changes recently, including API improvements that make it easy (well, *easier*)
to dynamically create diskette images from a list of files, or even an entire folder (including all subfolders),
as long as the total size of all the files will fit on a PCjs-supported diskette image. Support for creating hard disk
images is still on the "TODO" list (the original **convdisk** PHP script supported hard disk images, but that functionality
hasn't been ported to the newer **diskdump** Node module yet).
Almost nothing has changed in the PCjs client-side code (which is where the emulator runs), except for changes to use
the new **diskdump** API.
*[@jeffpar](http://twitter.com/jeffpar)*
*April 12, 2014*

View file

@ -1,156 +0,0 @@
---
layout: post
title: "Node + Express != Safari"
date: 2014-04-14 11:00:00
category: Browsers
permalink: /blog/2014/04/14/
---
There's something very odd going on with between Node+Express and Safari, resulting in blank web pages.
Don't believe me? Just ask [Google](https://www.google.com/#q=node+express+safari+blank+page).
[{{ site.pcjs.domain }}]({{ site.url }}/) contains a lot of XML files that are rendered as web pages using XML
stylesheets. And occasionally Safari -- and ONLY Safari -- will render those XML files as blank pages.
For example, here's the
[machine.xml](/devices/pcx86/machine/5150/mda/64kb/machine.xml) file that's also embedded on the
[{{ site.pcjs.domain }}]({{ site.url }}/) home page.
When Safari fetched that XML file from an Apache web server (what I used before switching to Node),
the request would look like:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14
and the response would look like:
Date: Mon, 14 Apr 2014 22:11:20 GMT
Last-Modified: Sun, 13 Apr 2014 01:59:06 GMT
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.24 mod_ssl/2.2.26 OpenSSL/0.9.8y
Etag: "37a10b3-492-4f6e2e96b2e80"
Content-Type: text/xml
Connection: Keep-Alive
Accept-Ranges: bytes
Keep-Alive: timeout=5, max=100
Content-Length: 1170
with a status code of 200 ("OK"). And no matter how many times I hit Safari's Reload button, the response was the same.
Now with Node+Express, the same exact request would look like:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14
with a response of:
Date: Mon, 14 Apr 2014 22:16:42 GMT
Etag: "1170-1397354346000"
Last-Modified: Sun, 13 Apr 2014 01:59:06 GMT
X-Powered-By: Express
Content-Type: application/xml
Cache-Control: public, max-age=0
Connection: keep-alive
Accept-Ranges: bytes
Content-Length: 1170
HOWEVER, as soon as I used Safari's Back button to return to the home page, and then pressed the Forward button to return to
the XML file, the XML request changed to:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Cache-Control: max-age=0
If-None-Match: "1170-1397354346000"
If-Modified-Since: Sun, 13 Apr 2014 01:59:06 GMT
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14
with a response of 304 ("Not Modified") and the following response headers:
Date: Mon, 14 Apr 2014 22:18:26 GMT
Cache-Control: public, max-age=0
Etag: "1170-1397354346000"
Last-Modified: Sun, 13 Apr 2014 01:59:06 GMT
Connection: keep-alive
Accept-Ranges: bytes
X-Powered-By: Express
And here's where the "blank page" problem occurs: pressing Safari's Reload button. Again, the request looks the same as before,
and the response is still 304 ("Not Modified"), but the page is blank, and the response now looks like:
Date: Mon, 14 Apr 2014 22:21:15 GMT
Cache-Control: public, max-age=0
Last-Modified: Sun, 13 Apr 2014 01:59:06 GMT
Connection: keep-alive
Accept-Ranges: bytes
X-Powered-By: Express
Etag: "1170-1397354346000"
and no matter how many times I press Reload, the response is the same (except for an updated *Date*), and the page is still blank.
So, here's the kludge I've added to my Express server code, to prevent Safari from displaying blank pages for those XML files:
{% highlight javascript linenos %}
/*
* The Safari "blank page" problem continues to plague us. Our first work-around was for directory
* "index.html" documents, which we resolved by always sending the document ourselves, along with an
* "ok" (200) response, instead of letting next() handle it, which would result in a "not modified"
* (304) response.
*
* However, the problem also extends to any XML files that we serve to an initial Safari request
* (eg, the machine.xml and manifest.xml files that we style as web pages). Safari includes
* "Cache-Control max-age=0" in the request, and if the response is "Cache-Control public, max-age=0"
* along with a 304 response code, Safari may once again display a blank page.
*
* This problem appears limited to the initial resource request for a particular URL. When these XML
* files are requested by Safari while loading another web page, Safari's caching logic is different
* (eg, it doesn't include the same "Cache-Control" setting).
*/
if (sBaseName == "machine.xml" || sBaseName == "manifest.xml") {
var sAgent = req.headers['user-agent'];
if (sAgent && sAgent.indexOf("Safari/") >= 0 && sAgent.indexOf("Chrome/") < 0 && sAgent.indexOf("OPR/") < 0) {
var sCacheControl = req.headers['cache-control'];
if (sCacheControl && sCacheControl.indexOf("max-age=0") >= 0) {
fs.readFile(sPath, {encoding: "utf8"}, function doneReadFile(err, sData) {
if (err) {
next(); // alternatively: res.status(404).send("Cannot GET " + req.path);
} else {
/*
* HACK: Express may still modify our response, turning our 200 status code into a 304
* and adding an Etag, unless we ALSO change the req.method from "GET" to something else.
* Supposedly, we could also use app.disable('etag'), but I'm not sure that would prevent
* Express from changing the status code, and I'm tired of testing work-arounds for this
* irritating behavior in Safari.
*/
req.method = "NONE";
res.set("Content-Type", "application/xml");
res.status(200).send(sData);
}
});
return;
}
}
}
{% endhighlight %}
I should add that this problem wasn't limited to XML files. It's a problem for the first resource requested by
Safari for any URL on the site (eg, URLs that default to "index.html" files).
I'm also rather surprised that no one yet seems to have figured out exactly what's going on here between Node+Express
and Safari. Or maybe they have, and I haven't been keeping my Node.js config up-to-date. I've tried to avoid changing
too many variables.
Lots of people have run into this problem. For example, on
[StackOverflow](http://stackoverflow.com/questions/18811286/nodejs-express-cache-and-304-status-code), someone
concluded that the [node-fresh](https://github.com/visionmedia/node-fresh) module should be changed. And for a while,
it was changed, until the change was [reverted](https://github.com/visionmedia/node-fresh/issues/8) -- along with a
lengthy discussion about why the change was wrong and that this was really a bug in Safari.
This "blank page" behavior may well be a bug in Safari, but that doesn't mean Express server components can't
or shouldn't provide a work-around for that behavior in the meantime. It also seems that some people who decided
this was a bug in Safari did not actually reproduce the bug themselves.
I don't know the right answer, but I do know that the current situation adversely affects users of other Node-powered
websites, who will probably get blank pages when they shouldn't, and other developers, who must all discover/debug/work-around
this problem on their own.
*[@jeffpar](http://twitter.com/jeffpar)*
*April 14, 2014*

View file

@ -1,45 +0,0 @@
---
layout: post
title: Heading to New York
date: 2014-04-30 11:00:00
category: JavaScript
permalink: /blog/2014/04/30/
---
Lots of tinkering has been going on here at pcjs.org the past couple of weeks, but with nothing substantial to show for it.
Fixing lots of little problems requires only small bits of time, whereas buckling down and tackling "the next BIG thing" for
PCjs requires a much more serious time commitment, with limited interruptions.
And I certainly can't start on "the next BIG thing" now, because I'm heading to New York tomorrow, for [EmpireJS](http://2014.empirejs.org),
my first JavaScript conference *and* my first trip to New York in about 20 years.
I'm going to the conference partly because it was a great excuse to finally visit New York again (and with the whole family,
since all three of us are computer nerds), but also to get a more up-close-and-personal sense of where this whole JavaScript
renaissance is headed.
Is it headed for a fiery crash? This recent blog post
("[you have ruined javascript](http://codeofrob.com/entries/you-have-ruined-javascript.html)")
suggests it already has for some people. Is it drowning in the Sea of Endless Proliferation, as this still-relevant two-year-old
[parody](http://www.webmonkey.com/2012/05/jokes-for-nerds-html9-responsive-boilerstrap-js/) implies? Excerpt:
> *If youre feeling overwhelmed by the endless proliferation of responsive grids, adaptive images, HTML boilerplates,
CSS frameworks and JavaScript whirligigs then what you need is the HTML9 Responsive Boilerstrap JS.*
> *To install HTML9 Responsive Boilerstrap JS just “attackclone the grit repo pushmerge, then rubygem the lymphnode js shawarma
module — and presto!”*
> *If youre wondering what H9RBS.js actually is, well, you can abandon any hopes of one day being hip. But if you must know,
H9RBS.js is a “flexible, dependency-free, lightweight, device-agnostic, modular, baked-in, component framework MVC library
shoelacestrap to help you kickstart your responsive CSS-based app architecture backbone kitchensink tweetybirds.”*
Seriously, I'm concerned about how the JavaScript language (rather than the endless procession of frameworks) is going to evolve,
and whether it even can. Two examples: at a high-level, we have Microsoft pushing [TypeScript](http://www.typescriptlang.org/),
and at a much lower level, we have Mozilla pushing [asm.js](http://asmjs.org). And while I like aspects of both those efforts,
I'm relunctant to go very far down either of those paths right now, because I don't want to get suckered. Inevitably, someone will
see a *different* shiny object along another fork in the road, and everyone will chase after that instead.
Will [EmpireJS](http://2014.empirejs.org) answer any of these big questions? It doesn't really matter. I just expect to learn stuff,
and learning is fun!
*[@jeffpar](http://twitter.com/jeffpar)*
*April 30, 2014*

View file

@ -1,33 +0,0 @@
---
layout: post
title: Chrome Kicks Butt
date: 2014-05-12 11:00:00
category: JavaScript
permalink: /blog/2014/05/12/
---
I haven't been closely monitoring the performance of PCjs across various browsers. Most of my browser testing has
been limited to "Does the latest version still work in all current web browsers?"
However, at some point during the last couple months, Chrome's performance suddenly jumped through the roof. On my
2.8GHz Intel Core i7 MacBook Pro, Chrome v34.0.1847.131 easily punches through the 120Mhz barrier on a PCjs machine
running PC-DOS 2.00.
That's roughly a 3x-4x increase over previous versions of Chrome. Safari used to be the performance champ, capable
of running a PCjs machine at a top speed of around 70-80MHz, while Chrome was less than half that, and Firefox was
slower still.
Chrome has now leap-frogged Safari in a big way, almost doubling Safari's speed. And with Chrome's superior
Developer Tools, Chrome is clearly the "Browser of Choice," whether you're just playing with PCjs or actually
debugging it.
Firefox is a bit of a disappointment, given all the hoopla over [asm.js](http://asmjs.org/) and other investments
that Mozilla is making. I've taken a "wait-and-see" attitude toward asm.js, because PCjs is hand-coded JavaScript,
and I'm not prepared to build a preprocessor that converts the code to asm.js semantics solely for the benefit of a
single browser.
Chrome's approach to making regular JavaScript run faster seems to be a winning strategy so far, at least for apps
like PCjs.
*[@jeffpar](http://twitter.com/jeffpar)*
*May 12, 2014*

View file

@ -1,81 +0,0 @@
---
layout: post
title: Halt and Catch Liar
date: 2014-06-14 11:00:00
category: TV Shows
permalink: /blog/2014/06/14/
---
I had high hopes for the new AMC series "[Halt and Catch Fire](http://www.amctv.com/shows/halt-and-catch-fire),"
but it has proven to be an utter disappointment. I think I can suspend my disbelief as well as anyone, but this
show requires you to completely turn your brain off in order to be believed. I'm also baffled by the show's
high [IMDb score](http://www.imdb.com/title/tt2543312/), which is currently 8.4 (out of 10). Either AMC has figured
out how to game the system, or viewers are easily turned on by clichés, like the know-it-all Hot Programmer,
the self-assured Sales Guy, and the non-plot-advancing sex that they almost instantly engage in.
The premise: ex-IBM Sales Guy waltzes into a fictional computer company, smooth-talks his way into a top
marketing position without so much as a resumé, and then immediately risks all, including a huge potential lawsuit
with IBM, because he has dreams of building IBM clones that are "2x fast" at "1/2 price" -- with *handles*!
And the first thing they must do to achieve this dream is clone the IBM PC ROM BIOS, which the show pretends
was so secret that you couldn't even tell which chips on the IBM PC motherboard contained the ROM. Never mind
that IBM published the entire ROM BIOS listing in their Technical Reference Manual, which also included system
diagrams identifying every chip in the machine. I think if you're going to weave facts into your fiction,
the least you can do is get your facts right.
And centerpiece of this whole conceit -- the cloning of the IBM PC ROM BIOS. What a farce! Check out this
scene from Episode 2, where Brilliant Engineer looks at Hot Programmer's whiteboard in awe. Apparently, he
is easily awed, because he did the same thing when Sales Guy wrote "2x fast, 1/2 price" on an earlier whiteboard.
[<img src="/blog/images/halt-and-catch-liar-small.jpg" alt='"Halt and Catch Fire" Scene from Episode 2'/>](/blog/images/halt-and-catch-liar.jpg)
So if you deconstruct the code on this whiteboard, you quickly notice that while it IS assembly language, it is
NOT the sort of assembly language you would find in a ROM BIOS, let alone ANYTHING that would leave you in awe.
Here are some excerpts:
Initialization
MOV AX,CX ; set up DS
MOV DS,AX
MOV SS,AX ; and SS
LEA AX,BEGINSTACK
MOV SP,AX
MOV AL,OUTINT
...
TSTART
LEA AX,BEGTRACE
MOV POINT,AX
MOV AL,YES ; TURN TRACE ON
MOV TFLAG,AL
MOV AL,NO ; NOT WRAPPED
MOV WRAP,AL
MOV AX,CS ; CONVERT ADDRESS FOR OUTPUT
LEA SI,LOADCS
CALL HEXPRT
MOV AX,100H
LEA SI,LOADIP
CALL HEXPRT
LEA DX,SIGNIN
MOV AH,9
INT DOSINT
LEA DX,OUTPUT
MOV AL,OUTPUT (?)
MOV AH,25H ; Set Interrupt Vector
INT DOSINT ; Have DOS place the interrupt...
...
This is clearly **NOT** code for a PC ROM BIOS, because no PC BIOS would ever issue a DOS interrupt.
A BIOS is designed to be called *by* DOS, not the other way around.
This turns out to be code largely copied from a file I found online: [PCTRACE.ASM](http://ftpmirror.your.org/pub/misc/dos/RbbsInABoxVol1No2_640/files/007P/PCTRACE.ZIP-contents/PCTRACE.ASM)
Another curiosity is that searching for this resulted in a "[Googlewhack](http://en.wikipedia.org/wiki/Googlewhack)"
of sorts:
![Googlewhack](/blog/images/googlewhack.jpg)
Technically, a Googlewhack (a two-word search that yields exactly one result) must use two words found in an actual
dictionary. But dictionaries are so passé.
*[@jeffpar](http://twitter.com/jeffpar)*
*June 14, 2014*

View file

@ -1,18 +0,0 @@
---
layout: post
title: More Under-The-Hood Changes
date: 2014-06-26 11:00:00
category: Releases
permalink: /blog/2014/06/26/
---
v1.13.7 of PCjs contains a few minor improvements, mostly in terms of rendering video modes a little more
efficiently. The rest of the changes to the website involved beefing up support for both "software manifests"
and "document manifests."
To that end, there's a new [/pubs/](/pubs/) directory for old documents, and [/disks/pcx86/](/disks/pcx86/) contains
more disk images, with more on the way. I have a TON of old diskette images, and it has taken more time to organize
them and create manifests than I would like.
*[@jeffpar](http://twitter.com/jeffpar)*
*June 26, 2014*

View file

@ -1,48 +0,0 @@
---
layout: post
title: EGA Support
date: 2014-07-30 11:00:00
category: Video
permalink: /blog/2014/07/30/
---
PCjs v1.14.0 now includes basic EGA support. It emulates the EGA hardware well enough to pass the IBM EGA BIOS
diagnostics and run [Windows 1.01](/devices/pcx86/machine/5160/ega/640kb/win101/) in color. Check out our
[Windows 1.01 "Server Array"](/devices/pcx86/machine/5160/ega/640kb/array/) demo.
[<img src="/blog/images/win101-array-demo-small.jpg" alt='Windows 1.01 "Server Array" Demo'/>](/blog/images/win101-array-demo.jpg)
EGA support is added to a **machine.xml** file using two XML elements; eg:
```xml
<video id="videoEGA" model="ega" memory="0x20000" screenwidth="640" screenheight="350"/>
```
The *model* attribute must be set to "ega" and the *memory* attribute should be set to the amount of memory
desired on the card; valid memory sizes are:
- 0x10000 (64Kb)
- 0x20000 (128Kb)
- 0x40000 (256Kb)
As with the MDA and CGA video cards, the *screenwidth* and *screenheight* attributes specify the size of display
window, which the browser will then scale up or down, unless a specific overall size has been specified on the
&lt;machine&gt; element.
The second required XML element is a &lt;rom&gt; element to load the EGA ROM; eg:
```xml
<rom id="romEGA" addr="0xc0000" size="0x4000" file="/devices/pcx86/video/ibm-ega.json" notify="videoEGA"/>
```
The *notify* attribute must match the *id* of the &lt;video&gt; element, so that the Video component can load
the initial 8x14 and 8x8 fonts from the ROM. Support for dynamic loading of fonts from plane 2 of the EGA's memory
will be added later; however, current support works well enough to allow switching from 25-line mode to 43-line mode,
which essentially switches from the 8x14 font to the 8x8 font.
The &lt;video&gt; element also supports a *switches* attribute to specify the type of monitor connected to the EGA;
this attribute corresponds to the actual switch settings on the EGA card; our default *switches* setting is "0110",
which selects an Enhanced Color Monitor, enabling the EGA's maximum resolution of 640x350.
*[@jeffpar](http://twitter.com/jeffpar)*
*July 30, 2014*

View file

@ -1,32 +0,0 @@
---
layout: post
title: PC Tech Journal, 1987
date: 2014-08-01 11:00:00
category: PC Tech Journal
permalink: /blog/2014/08/01/
---
As part of an ongoing effort to make classic PC technical literature more accessible, I just finished
scanning and posting the 12 issues of [PC Tech Journal](/pubs/pc/magazines/pctj/) from 1987.
![PC Tech Journal, Jan 1987](http://archive.pcjs.org/pubs/pc/magazines/pctj/PCTJ-1987-01/thumbs/PCTJ-1987-01 1.jpeg "link:/pubs/pc/magazines/pctj/PCTJ-1987-01/:200:260")
Future PC Tech Journal postings will include:
- Vol. 1, No. 1, July-August 1983
- Vol. 3, No. 12, December 1985
- Vol. 4, Nos. 1-12, 1986
- Vol. 6, Nos. 1-12, 1988
I'm missing the rest of 1983, all of 1984, most of 1985, and all of 1989 (well, through April 1989, which was
apparently the last issue).
For a nice overview and brief history of PC Tech Journal, check out the [OS/2 Museum](http://www.os2museum.com/wp/?p=2478).
In the meantime, if you have old PC Tech Journal issues that would fill any of the above holes, let me know.
I'd be happy to buy them, scan them, and recycle them.
Thanks.
*[@jeffpar](http://twitter.com/jeffpar)*
*August 1, 2014*

View file

@ -1,53 +0,0 @@
---
layout: post
title: Supporting the 80286
date: 2014-08-28 11:00:00
category: 80286
permalink: /blog/2014/08/28/
---
The next milestone for PCx86 is complete 80286 emulation. My hope is to have it working by the end of the year.
PCx86 version 1.15.0 is the first step on the path to full 80286 support. It includes changes to the physical
memory manager and separate real-mode and protected-mode address evaluators. The Debugger supports physical
addresses (eg, %FE05B is the same as F000:E05B, assuming real-mode operation), along with breakpoint commands that
stop execution on port input/output operations. And the ChipSet component now contains "infrastructure" (a
fancy way of saying "partial support") for multiple PICs, DMA controllers, the 8042 keyboard controller (including
A20 support), and a bit more -- but not much.
One of the challenges is creating a single "universal" version of PCx86 that can adapt itself to different machine
types without impacting performance. There will not be a **pc8088.js** or a **pc80286.js** or whatever. There will
only be **pcx86.js**.
Up until now, all PCx86 machine XML files assumed an 8088 CPU with a 20-bit bus and a model 5150 or 5160 motherboard.
But now, a machine XML file can specify:
```xml
<computer name="IBM PC AT" buswidth="24"/>
<cpu model="80286"/>
<chipset model="5170"/>
...
```
Conventional emulators are usually NOT able to run original BIOS images, or simulate original PC hardware,
or even run at the same speed as the original PC, making some software difficult or impossible to use. PCx86 takes a
different approach, by attempting to simulate an entire PC as it originally existed. Which is why a PCx86 simulation
of an IBM PC does NOT run at whatever speed your modern PC happens to run in V86-mode or whatever speed your
browser's JavaScript engine tops out at.
No, a PCx86 simulation of a 4.77Mhz IBM PC runs at 4.77Mhz. And a PCx86 simulation of a 6Mhz IBM PC AT will run at
6Mhz. If you want to run the simulation faster, you have that option, but that's not the default. And I'm not saying
that PCx86 is *exact* -- exactness is an exercise I'm leaving for another day and/or to other developers who are even
more obsessive than I am. I'm just saying that original PCs represent the targets that PCx86 is shooting for.
PCx86 1.15.0 can now load and run the IBM 5170 ROM BIOS up to the first 80286-specific opcode, so it's off and running.
Although "running" isn't quite the right metaphor, because the process of bringing a new machine simulation to
completion is a *very* long series of baby steps.
Also, in preparation for this new phase, I recently dug up a variety of old [80286 CPU Documentation](/pubs/pc/reference/intel/80286/)
and posted excerpts. I'm sure none of this information is "new" at this point, but it might have some historical interest.
Enjoy.
*[@jeffpar](http://twitter.com/jeffpar)*
*August 28, 2014*

View file

@ -1,32 +0,0 @@
---
layout: post
title: Minor Fixes and Additions
date: 2014-09-02 11:00:00
category: Releases
permalink: /blog/2014/09/02/
---
The following fixes were made in PCjs v1.15.1
1. Using the "User-defined URL" option when loading a disk image from a 3rd-party server was broken if the URL
contained certain special characters; that should be fixed now, but be aware that only web servers (ie, URLs
using the HTTP protocol) are supported. URLs that trigger a redirect may also not work (more testing required).
2. Any errors that occur during the call to either *embedPC()* or *embedC1P()* should be properly displayed on the
caller's page now.
3. Two embedding helper functions have been added to provide more control over the machine startup
and shutdown process:
+ *enableEvents(boolean)*: pass *false* to disable delivery of all page events to all machines on the page,
or *true* to re-enable;
+ *sendEvent(string)*: pass *"init"*, *"show"* or *"exit"* to simulate the corresponding browser event
(*onload*, *onpageshow* or *onbeforeunload*, respectively).
If a page calls *enableEvents(false)* before calling *embedPC()*, all machine layouts will be instantiated
but the machines themselves will not be initialized. When the page is ready, call *enableEvents(true)* to restore
normal event processing, and if the browser has already sent the *onload* event, then call *sendEvent("init")*
to manually initialize the machine(s).
These two new functions are designed to assist in testing the starting up, shutting down and restarting of machines,
by allowing scripts to control the overall process, without requiring use of the browser's back/forward/close controls.
*[@jeffpar](http://twitter.com/jeffpar)*
*September 2, 2014*

View file

@ -1,71 +0,0 @@
---
layout: post
title: "The IBM PC AT: Alive and Booting"
date: 2014-09-13 11:00:00
category: Releases
permalink: /blog/2014/09/13/
---
My first IBM PC AT (Model 5170) [Test Configuration](/devices/pcx86/machine/5170/ega/640kb/rev1/debugger/) finally
boots to a PC-DOS prompt. The configuration uses the original [IBM Model 5170 ROM BIOS](/devices/pcx86/rom/5170/),
dated January 10, 1984.
Getting through the BIOS "POST" (Power-On Self Test) diagnostics was like running an obstacle course, with various
tests derailing the simulation at every turn.
Sometimes the problems were as simple as missing hardware. For example, I knew that the PC AT contained two DMA
controllers, for a total of 8 DMA channels, but what I didn't know (or had forgotten) is that it also contained 16
DMA page registers, some of which the BIOS uses as scratch registers. Since DMA page registers are accessed with
I/O instructions that work identically in both real-mode and protected-mode, they obviously offer some advantages
over RAM, especially when the BIOS hasn't yet tested all the RAM, or determined how much RAM is installed, or set up
descriptors that allow the RAM to be accessed from protected-mode.
Aside from the additional DMA Controller, other major new motherboard components on the PC AT included a second
8259 Interrupt Controller, an 8042 Keyboard ("Kitchen Sink") Controller, and an MC146818 Real-Time Clock/CMOS chip.
The Keyboard Controller and Real-Time Clock/CMOS components required the most tinkering to pass through the ROM BIOS
gauntlet.
For example, at one point, the BIOS ("[TEST.21](http://archive.pcjs.org/pubs/pc/reference/ibm/5170/techref/1984-03/pages/IBM-5170-TECHREF 202.pdf)")
reset the keyboard ("[KBD_RESET](http://archive.pcjs.org/pubs/pc/reference/ibm/5170/techref/1984-03/pages/IBM-5170-TECHREF 212.pdf)"),
which unmasked the keyboard IRQ and waited for an interrupt, using a loop where CX was initialized to zero and then
decremented until either CX wrapped around to zero again *or* an interrupt occurred. The "TEST.21" code then assumed
that if "KBD_RESET" returned zero in CX, no interrupt had occurred.
Unfortunately, my Keyboard Controller was a bit too fast: it generated an interrupt as soon as the keyboard IRQ was
unmasked; as a result, CX was never decremented, leaving it at zero.
---
Most of the effort getting to this point involved adding support for 80286 protected-mode. That work is still far
from complete, but getting through multiple real-mode/protected-mode round trips in the BIOS was an important
milestone. Some of the work was outside the CPU component, such as A20 support and processor reset via the 8042
controller. Work inside the CPU component included:
- New 80286 general-purpose instructions (eg, ENTER, LEAVE, new PUSH SP behavior, etc)
- New protected-mode instructions (eg, ARPL, LGDT, LIDT, LAR, LSL, VERR, VERW, etc)
- Protected-mode segment loading, addressing, and fault handling
Another "feature" I spent considerable time on was ensuring that 80286 protected-mode support did not adversely
real-mode performance, so that the PC and PC XT simulations still run (almost) as fast as before. PCjs dynamically
reconfigures itself according to the requirements of the processor and platform it's emulating.
---
There's still no support for [LOADALL](/pubs/pc/reference/intel/80286/loadall/) or triple-fault resets, nor for
call gates or task gates, nor for conforming code segments or expand-down data segments. 80286-specific cycle
counts haven't been incorporated yet, either. The list of remaining 80286 features is long.
And there's plenty of hardware support left to do: I haven't looked at the AT hard drive controller yet (which
I believe is significantly different from the XT hard drive controller), and the keyboard *barely* works; the 8042
Keyboard Controller and AT keyboard had a number of features that older PC/XT keyboards did not (like LEDs and
programmable repeat rate).
And there are plenty of issues to investigate. For example, PC-DOS is picking up the correct RTC time, but not the
date (PCjs initializes the RTC to the browser's current date/time, unless a hard-coded date/time is specified in the
machine XML). And diskette I/O seems a bit slow; I'm concerned that the BIOS is spinning its wheels somewhere
unnecessarily. And even though the test machine is configured with 640Kb of RAM, the BIOS is reporting only 64Kb.
Hmmmm.
*[@jeffpar](http://twitter.com/jeffpar)*
*September 13, 2014*

View file

@ -1,246 +0,0 @@
---
layout: post
title: PCjs Coding Conventions
date: 2014-09-30 11:00:00
category: JavaScript
permalink: /blog/2014/09/30/
---
Here are a few highlights of the (evolving) JavaScript coding conventions used in PCjs.
### Tabs vs. Spaces
I've configured my IDE ([WebStorm](http://www.jetbrains.com/webstorm/)) to NEVER use tab characters in .js files
(spaces only) and to ALWAYS use tab characters in almost every other type of text file. This is largely because
when a web browser displays a JavaScript file (either in the main window or in the Developer Tools window), tabs
usually screw up the formatting, which I find annoying when I'm debugging. XML files, on the other hand,
are usually reformatted by the browser anyway, so in those cases, I opt for smaller files and use real tabs.
Note that most of the JavaScript delivered by a PCjs production server will have been compiled by Google's
Closure Compiler, which completely eliminates all non-essential whitespace, so this is just a development
preference, with little to no impact on production files.
Regardless of the choice of tab character however, I almost always use 4-column tab stops, except in legacy .asm
files, where 8-column tab stops were the norm.
I've noticed that 2-column tab stops have recently become popular, especially in Node projects; NPM, for example,
will rewrite package.json files, replacing my 4-column spacing with 2-column spacing. I don't fight that trend -- I
just ignore it.
### Constants
Property names with all UPPER-CASE letters (with optional numbers and/or underscores) represent constants.
I originally adopted this rule in part because it's a popular C language convention, but also because it
made it easy to write a preprocessing script (see the PCjs Grunt task **prepjs** in /modules/grunts/prepjs/)
that replaced all such property references with the corresponding property values and then removed the original
property definitions. Of course, this convention also depended on the properties never being modified *or* enumerated.
I later discovered that Google's Closure Compiler does an excellent job of automatically inlining properties
that are never modified or enumerated, so the **prepjs** preprocessing script is no longer used, but I've stuck
with the UPPER-CASE convention.
I don't bother with JSDoc *@const* annotations, because 1) the project contains far too many constants, 2)
all the constants are already effectively annotated by virtue of being UPPER-CASE, and 3) there is no noticeable
improvement in the Closure Compiler's inlining capability with the addition of *@const*.
All constants associated with a component are normally attached to the component's constructor; ie, as properties of
the constructor. If you think of a JavaScript constructor as a "class', then constants attached to the constructor
can be thought of as "class constants".
For example, the ChipSet component, which manages (among other things) Programmable Interrupt Controllers or PICs,
*could* define the constant for an EOI command like this:
``` javascript
ChipSet.EOI = 0x20; // non-specific EOI (end-of-interrupt)
```
but since the EOI command is actually one of a number Operation Command Words (specifically, OCW2), I include an
"OCW2_" prefix in the constant name:
``` javascript
ChipSet.OCW2_EOI = 0x20; // non-specific EOI (end-of-interrupt)
```
and since I also like to group constants that are associated with a particular register or port, and since I don't
want the ChipSet constructor becoming littered with property constants, I first define a constant object; in this
case, **PIC_LO**:
``` javascript
ChipSet.PIC_LO = {};
ChipSet.PIC_LO.OCW2_EOI = 0x20; // non-specific EOI (end-of-interrupt)
ChipSet.PIC_LO.OCW2_EOI_SPEC = 0x60; // specific EOI
ChipSet.PIC_LO.OCW2_EOI_ROT = 0xA0; // rotate on non-specific EOI
ChipSet.PIC_LO.OCW2_EOI_ROTSPEC = 0xE0; // rotate on specific EOI
```
By using fully-qualified property names for each constant, the code has a more C-like appearance (think *#define*)
that's also easier to preprocess.
However, I've gradually switched to the more conventional JavaScript object notation for class constants:
``` javascript
ChipSet.PIC_LO = {
OCW2_EOI: 0x20, // non-specific EOI (end-of-interrupt)
OCW2_EOI_SPEC: 0x60, // specific EOI
OCW2_EOI_ROT: 0xA0, // rotate on non-specific EOI
OCW2_EOI_ROTSPEC: 0xE0 // rotate on specific EOI
};
```
because, again, the Closure Compiler does an excellent job inlining such constants (or indeed any property that is
never modified *or* enumerated).
### DEBUG vs. RELEASE
While we're talking about constants, it's important to be aware of constants that are not scoped to
any particular component.
In [/modules/shared/lib/defines.js](/modules/shared/lib/defines.js), **DEBUG** is set to **TRUE**,
enabling all debug-only code by default. It is also declared as a *@define* so that the Closure Compiler can
override it, setting it to **FALSE** and disabling debug-only code.
To ensure that debug-only code is not simply *disabled* but also *removed*, the code should be wrapped with:
``` javascript
if (DEBUG) {
[code to be removed by the Closure Compiler]
}
```
In many cases, the compiler is able to completely remove calls to debug-only class methods; eg:
``` javascript
Component.assert(off >= 0 && off < this.cb);
```
However, calls to debug-only instance methods seem to be more problematic, so all such calls are wrapped; eg:
``` javascript
if (DEBUG) this.log('load("' + sFileURL + '")');
```
There are a number of other important shared constants in [/modules/shared/lib/defines.js](/modules/shared/lib/defines.js)
and PCjs-specific constants in [/modules/pcx86/lib/defines.js](/modules/pcx86/lib/defines.js); refer
to those files for more information.
### Braces and Parentheses
Most opening braces appear at the end of the line containing the associated "if", "while", "for", "switch",
"function", etc, preceded by a single space. And most opening parentheses are also preceded by a single space,
except when following "function" or a function name, in which case there is NO space.
There's always the occasional exception. For example, the opening brace of all the top-level (documented)
functions in a module may appear on its own line, because the extra whitespace can make the code a bit more
readable.
It's also important to be aware of JavaScript's automatic semicolon insertion feature and the associated danger of
putting an opening brace below a *return* statement that wants to return an object literal. As long as you (and
your IDE) are aware of that specific danger, there's no need to be dogmatic about opening braces.
### Variable Names
I still tend to follow Charles Simonyi's "[Hungarian](http://en.wikipedia.org/wiki/Hungarian_notation)" naming
conventions -- or rather, a naming convention loosely inspired by Hungarian.
For example, if I need a string or numeric variable representing a "thing," I will name it "sThing" if it's a
string or "iThing" if it's a number (or possibly "nThings" if it represents a total of Things or "cThings"
if it's a counter of Things). If a string or numeric variable has a very short-term use, I'll probably just name
it "s" or "i".
As I mention [below](./#quotation-marks), I still tend to distinguish single characters from strings too,
which means I may sometimes prefix character variables with "ch" and character counters with "cch".
Of course, variable name prefixes like "s" and "n" are irrelevant if you've already given your variables meaningful
names like "nameOfPerson" or "numberOfPeople". And that's fine -- I sometimes do that as well. But in general,
I still prefer variable names like "sPerson" and "nPeople".
I don't try to come up with special prefixes for Objects. If there's a Person object, for example, I'll probably
use colloquial names like "personHere" or "personThere". I am stricter with Arrays though: I prefix array variables
with "a", arrays of strings and numbers with "as" and "ai" (or "an"), arrays of arrays with "aa", etc. As for Arrays
of anything else, I usually don't bother with anything more than an "a" prefix.
### Quotation Marks
Because of my C background, I prefer to use double-quotes around multi-character strings and single quotes
around single-character strings. While the reasons for doing so are largely historical and currently irrelevant,
characters are STILL the building blocks of strings, and even the JavaScript String class contains methods that
deal with individual characters (eg, charCodeAt() and fromCharCode()). So for any code that deals explicitly with
individual characters, I like to reinforce that with single quotes.
Also, to emphasize that object property names aren't really strings (even though strings can be used as property
names), I tend to use single quotes when quoting property names. That does make me somewhat inconsistent with
the JSON standard, which insists that property names be double-quoted, but JSON.stringify() takes care of that, so
it's not really a problem. Besides, I have a lot of quibbles with the JSON standard, like its "disapproval" of
comments and hexadecimal constants, and its failure to faithfully serialize and deserialize uninitialized Array
objects, but I'll leave my gripes about JSON for another post.
Generally speaking, the only time I quote property names is when I have to. I'll use the "dot" syntax; eg:
``` javascript
obj.prop = true;
```
instead of:
``` javascript
obj['prop'] = true;
```
unless the property name doesn't conform to variable name syntax (eg, if it starts with a digit) or if it's a
"public" property and therefore I can't risk Google's Closure Compiler "minifying" the property name to something
else.
I break my own quoting rules slightly when dealing with strings that *contain* double-quotes, since it's more readable
to put double-quotes inside single-quoted strings than to "escape" every double-quote with a backslash.
For code that I originally wrote in PHP and later ported to JavaScript, there was a tendency in the original
code to always use double-quotes around strings and "escape" double-quotes regardless, and that tendency may linger
in code I didn't feel like rewriting much, but the tendency was due more to idiosyncrasies of PHP than any convention
of mine; for example:
- single-quoted PHP strings may not include any escaped characters (except for single-quote and backslash)
- single-quoted PHP strings cannot resolve references to string variables (eg, "the value of foo is {$foo}")
Because of PHP's restrictions on single-quoted strings, I tended to avoid them. However, in JavaScript, those
restrictions/features don't exist.
### JSDoc
Most of the PCjs code is documented with [JSDoc](http://usejsdoc.org/) annotations -- not
because I want to be able to generate documentation (although that's something to think about), but because
it's the only way to tell both the Closure Compiler and my IDE exactly what data types are passed around.
The goals are to minimize the number of "code inspection" warnings in the IDE and produce warning-free
compilations.
In order to use the Closure Compiler's ADVANCED_OPTIMIZATIONS option and get maximum performance (and maximum
"minification", a form of "uglification"), every function and its parameters needs to be fully typed; otherwise,
the Compiler generates way too many warnings/errors -- at least, that was the case when I first started using
it a couple of years ago.
I've adopted a zero-tolerance policy for warnings: nothing gets checked in if the Closure Compiler generates even
a single warning.
And finally, speaking of warnings, I've had to tell [WebStorm](http://www.jetbrains.com/webstorm/) to "shut up"
about a few:
- Unfiltered for…in loop
- Bitwise operator usage
- Comma expressions
- loop statement that doesn't loop
- “throw” of exception caught locally
I acknowledge those those features can introduce bugs if you're not careful, so I make sure I'm careful. I don't
subscribe to the dogmatic approach that others (eg, the author of JSLint) take about so-called "risky" features.
I agree that it's always a good idea to walk to the crosswalk before crossing a street, but I don't agree that it's
*never* a good idea to cross in the middle sometimes, too.
I've also made the following "weak warnings" instead of "warnings":
- Unused JavaScript / ActionScript local symbol
because it's a useful warning, but I don't like being penalized for functions that have been "prototyped" a specific
way but can't always be implemented exactly as prototyped.
*[@jeffpar](http://twitter.com/jeffpar)*
*September 30, 2014*

View file

@ -1,27 +0,0 @@
---
layout: post
title: PCjs Released on GitHub
date: 2014-10-12 11:00:00
category: Releases
permalink: /blog/2014/10/12/
---
I've decided the time has come to make the [PCjs Project](https://github.com/jeffpar/pcjs) an open source project on
[GitHub](http://github.com/).
This doesn't mean PCjs is done -- not by a long shot. But I promised to release it on GitHub by the end of
the year, which is fast approaching, and I didn't really want to do this in December.
I feel I've made pretty good progress on my goals for the year -- primarily EGA and PC AT support. PC AT machines
can boot and run in real-mode now, but there's still a lot of protected-mode work to do. The big remaining goal for
this year is to boot OS/2 1.0.
There are also a lot of rough edges left to polish. Holes in EGA emulation remain to be filled (support for dynamically
loaded fonts is one of the bigger ones). And the PC AT Keyboard, along with the 8042 and Hard Drive controllers, are all
just limping along at the moment.
Longer term, I'm looking forward to building some new tools, including an "IBM PC Configurator" that will take some
of the pain out of building your own bootable PCjs machine configurations, and make them easier to share, embed, etc.
*[@jeffpar](http://twitter.com/jeffpar)*
*October 12, 2014*

View file

@ -1,34 +0,0 @@
---
layout: post
title: The 8Mhz IBM PC AT 5170
date: 2014-10-13 11:00:00
category: JavaScript
permalink: /blog/2014/10/13/
---
I just added my first [8Mhz IBM PC AT](/devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/) machine configuration
to the list of [IBM PC Machine Configurations](/devices/pcx86/machine/), and not surprisingly, the new machine
fails to boot.
This machine uses the 3rd [ROM BIOS](/devices/pcx86/rom/5170/) that IBM released for the PC AT, a revision that
included support for 3.5-inch 1.44Mb diskettes -- which will be nice, because I have a number of 1.44Mb diskette
images I would like to be able to read in a PCjs machine.
Since machines with this BIOS also ran at 8Mhz, I've bumped the CPU speed up to 8,000,000 cycles/second.
It'll be interesting to see whether this BIOS also increased any of its hard-coded timing delay-loops as a result.
Anyway, when I enabled ChipSet I/O port messages in the Debugger:
m chipset on
m port on
I can see that the BIOS Power-On Self Test (POST) progresses nicely until it starts generating lots of port 0x61
activity:
chipset.inPort(0x0061,8042_RWREG): 0x30 at F000:05A8
chipset.inPort(0x0061,8042_RWREG): 0x20 at F000:05AE
I know what I'm going to be doing this afternoon now.
*[@jeffpar](http://twitter.com/jeffpar)*
*October 13, 2014*

View file

@ -1,58 +0,0 @@
---
layout: post
title: Improved support for PC AT machines
date: 2014-10-17 11:00:00
category: Releases
permalink: /blog/2014/10/17/
---
The [8Mhz IBM PC AT](/devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/) machine configuration now boots in
[PCjs v1.15.5](https://github.com/jeffpar/pcjs/releases/tag/v1.15.5), which includes the following fixes:
+ The BIOS expects memory refresh to occur roughly every 16us, which I've resolved by tying the state
of the refresh bit in port 0x61 to bit 6 of the CPU cycle count (see *in8042RWReg()* in [chipset.js](/modules/pcx86/lib/chipset.js));
the original AT BIOS was satisfied with a refresh bit that merely alternated, whereas the new AT BIOS
is much more particular about the rate at which that bit changes, since many hard-coded delay-loops have
now been replaced with code that waits for a specific number of refresh cycles.
+ The 8042 Keyboard Controller emulation needed a few more tweaks, mainly with respect to what happens
when the keyboard's "clock" line is toggled (see *set8042CmdData()* in [chipset.js](/modules/pcx86/lib/chipset.js)).
+ The Floppy Drive Controller needed to add support for the "READ ID" command, in order for the BIOS
"double-stepping" test to work (double-stepping is required on an 80-track drive when attempting to read
a 40-track diskette).
+ The BIOS Diskette Reset function does something odd after resetting the Floppy Drive Controller: it
issues not one but *four* "SENSE INTERRUPT STATUS" commands to the FDC, and expects each response to
return an incrementally larger drive number. I found this a bit mystifying, considering that IBM's
own FDC/HDC "combo card" supports a maximum of *two* diskette drives. But, there's no point arguing
with a BIOS that's almost 30 years old.
+ The BIOS attempts to detect what its authors must have considered a common problem: the user's failure
to run SETUP after installing a second hard drive. So, when the CMOS reports only one hard drive installed,
the BIOS probes for a second hard drive anyway, and it does so by simply writing the drive number to the ATC's
"DRVHD" register and then immediately reading the "STATUS" register, without issuing any intervening command.
It was an easy fix to *outATCDrvHd()* in [hdc.js](/modules/pcx86/lib/hdc.js), but I was surprised
to discover that the ATC had this behavior, and now I'm wondering if there are any other I/O operations
that must immediately update the "STATUS" register.
This PCjs release also fixes a problem reported by a user: if you disable **localStorage** support in your
browser, previous versions of PCjs would fault. While every browser that supports PCjs also supports
**localStorage**, I didn't consider what might happen if a user decided to turn it off.
The only downside to turning off **localStorage** is that none of your PCjs machines will be able save/restore
their state when you leave/return to the page; they will always reboot.
Browser's don't always refer to the **localStorage** feature by its actual name, either. For example, in
Chrome, the setting that enables/disables **localStorage** is hidden under "Advanced Settings" => "Privacy" =>
"Content Settings" => "Cookies" => "Allow local data to be set (recommended)". Which is somewhat misleading
and a little annoying, because **localStorage** is *not* a **cookie**.
PCjs *never* sets any cookies. Cookies are bits of data that your browser saves and then automatically sends
off to the server every time you make a request. **localStorage** is nothing more than local storage; it is
*not* automatically sent anywhere. Granted, a JavaScript application could abuse it and send it out just like a
cookie, but PCjs does *not* do that; the only exception is when PCjs detects a problem, and even then, you must
first agree to submit your machine's state as part of the bug report.
*[@jeffpar](http://twitter.com/jeffpar)*
*October 17, 2014*

View file

@ -1,27 +0,0 @@
---
layout: post
title: Improved PC-DOS 7.00 Support
date: 2014-10-23 11:00:00
category: Releases
permalink: /blog/2014/10/23/
---
[PCjs v1.15.6](https://github.com/jeffpar/pcjs/releases/tag/v1.15.6) is a fairly minor update that fixes a few
Floppy Drive Controller (FDC) issues and one CPU emulation bug that prevented [PC-DOS 7.00](/disks/pcx86/dos/ibm/7.00/)
from working properly.
There are also some Debugger improvements; for example, if you turn on "fdc" and "int" messages in the
Debugger using the "m fdc on" and "m int on" commands, all FDC (INT 0x13) software interrupts will be logged,
including descriptions and register values.
PC-DOS 7.00 still can't be setup from its specially-formatted 1.84Mb
[XDF](http://www.os2museum.com/wp/the-xdf-diskette-format/) distribution disk images, "PC-DOS 7.00 (Disk 2)"
through "PC-DOS 7.00 (Disk 5)", so your best bet is to boot from the 1.44Mb "PC-DOS 7.00 (1.44M Boot)".
Note that you must also use a fairly new 80286 machine configuration, like this
[8Mhz IBM PC AT](/devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/),
in order to use 1.44Mb diskette images; previous models did not support 3.5-inch diskette drives, unless they had been
retrofitted with a newer [BIOS](/devices/pcx86/rom/5170/).
*[@jeffpar](http://twitter.com/jeffpar)*
*October 23, 2014*

View file

@ -1,83 +0,0 @@
---
layout: post
title: JavaScript Negativity
date: 2014-10-26 11:00:00
category: JavaScript
permalink: /blog/2014/10/26/
---
Coming from the C programming language, it's easy to be "negative" about how JavaScript deals with 32-bit integers.
As a newcomer, you quickly learn that JavaScript supports only one numeric data type -- 64-bit floats -- and you groan.
Then you learn that all the "bitwise" operators (**~**, **|**, **&**, **^**, **&lt;&lt;**, **&gt;&gt;** and
**&gt;&gt;&gt;**) treat their operands as 32-bit integer values and produce 32-bit integer results, and you breathe
a sigh of relief.
But then you start noticing oddities. In C, you can take any 32-bit value, such as -1526726656 (which is equivalent
to 0xA5000000), mask it with 0x80808080, and get 0x80000000. However, in JavaScript, you actually get -0x80000000,
which, sadly, is not equal to 0x80000000.
To verify, type the following into any JavaScript REPL (eg, Node):
> n = -1526726656
-1526726656
> n &= 0x80808080
-2147483648
> n == 0x80000000
false
> n == -0x80000000
true
The sign (bit 31) of every 32-bit result is always extended into the entire 52 "significand" bits of the underlying
64-bit float. And it's impossible to simply "mask away" those additional sign bits, thanks to a fundamental
restriction of JavaScript bitwise operators: they operate *only* on the low 32 bits.
With one exception: the unsigned right-shift operator. It does more than simply shift zero bits in from
the left; it also zeros all the bits above the sign bit. This means that `n >>> 0`, while leaving the low 32 bits
unchanged, also clears the upper bits, resulting in a value that is positive, albeit outside the signed 32-bit range.
It is equivalent to adding the 33-bit value 0x100000000 to a negative 32-bit number:
> n = (n < 0? n + 0x100000000 : n)
2147483648
> n.toString(16)
'80000000'
These operations work because JavaScript is perfectly capable of representing 0x80000000, or any other 32-bit value,
as a positive number, but it must use a floating point value to do so. And be careful, because as soon as you perform
*any* bitwise operation on a value with bit 31 set, even an operation as innocuous-looking as:
> n |= 0
-2147483648
> n.toString(16)
'-80000000'
the result will be negative again. This is simply how all bitwise operators (except for unsigned right-shift) operate:
they truncate the result to a signed 32-bit value.
This might tempt you to think that the right way to write negative 32-bit constants in hex is to simply precede
them with a minus sign. But that would be wrong. For example, if you wrote the constant 0x80000080 as "-0x80000080",
JavaScript would treat that as negation of 2147483776, resulting in a value whose low 32 bits are 0x7FFFFF80, not
0x80000080.
The safest way to write a 32-bit constant like 0x80000080 is "0x80000080|0", which will produce -2147483520. If you
write all your negative 32-bit constants that way, then you won't have to resort to using either unsigned right-shifts
or 33-bit addition, which in turn avoids the use of floating point values.
To continue the fun, try setting bit 0 of 0x80000000, which should give you 0x80000001:
> n |= 1
-2147483647
> n.toString(16)
'-7fffffff'
WTF? Have all the low 32 bits flipped instead?
Actually, no, this time, I'm pulling your leg. The low 32 bits of the internal value are exactly what you would
expect: 0x80000001 (the internal representation is more like 0xFFFFF80000001). But as the
[MDN Docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString)
explain, for a negative number, toString() returns the positive representation of the number, preceded by a - sign,
*not* the "two's complement" of the number.
*[@jeffpar](http://twitter.com/jeffpar)*
*October 26, 2014 (Updated September 8, 2015)*

View file

@ -1,23 +0,0 @@
---
layout: post
title: Limited Support for XDF Diskettes
date: 2014-10-28 11:00:00
category: Releases
permalink: /blog/2014/10/28/
---
[PCjs v1.15.7](https://github.com/jeffpar/pcjs/releases/tag/v1.15.7) adds support for the
[XDF Diskette Format](http://www.os2museum.com/wp/the-xdf-diskette-format/), which was used in
[PC-DOS 7.00](/disks/pcx86/dos/ibm/7.00/).
However, this support is referred to as "fake" XDF support, because it requires using JSON disk images created
by DiskDump *without* the experimental "--xdf" option, which is an option that attempts to encode XDF sectors as they
existed on the original diskettes (ie, with varying lengths and non-standard sector IDs).
"Fake" XDF support works by using conventional 80-track disk images with 23 sectors/track. No standard PC floppy disk
format ever used 23 sectors/track, but in this case, by distributing the XDF track data across 23 conventional 512-byte
sectors, the PC-DOS 7.00 Setup code that reads XDF disks succeeds. This was probably one of several fall-back options
built into the PC-DOS XDF code.
*[@jeffpar](http://twitter.com/jeffpar)*
*October 28, 2014*

View file

@ -1,55 +0,0 @@
---
layout: post
title: OS/2 1.0
date: 2014-12-04 11:00:00
category: OS/2
permalink: /blog/2014/12/04/
---
Exciting news for OS/2 fans: PCjs (v1.16.1) is now able to run OS/2 1.0 on
[IBM PC AT Machine Configurations](/devices/pcx86/machine/#model-5170-machine-configurations). This is the culmination
of recent work in PCjs to fully emulate the Intel 80286 processor and 16-bit protected-mode, including undocumented
features like [LOADALL](/pubs/pc/reference/intel/80286/loadall/) and triple-fault resets.
For a quick demo, try the [OS/2 1.0 Debugger Disk](/disks/pcx86/os2/misc/1.0/88286/). In a few seconds,
you'll see a very rudimentary OS/2 shell (a slimmed-down version of the OS/2 Program Selector) that allows you to
start the protected-mode command interpreter ("Start a Program") or the real-mode command interpreter ("command.com").
[<img src="/blog/images/os2-debugger.jpg" alt="OS/2 1.0 With Kernel Debugger"/>](/disks/pcx86/os2/misc/1.0/88286/)
As an added bonus, the Model 5170 machines feature two serial ports, with COM1 connected to a simulated serial
mouse and COM2 connected to the **Control Panel** output window.
Once you've booted the [OS/2 1.0 Debugger Disk](/disks/pcx86/os2/misc/1.0/88286/) from the assortment of
[OS/2 Prototype Disks](/disks/pcx86/os2/misc/), you can click on the **Control Panel** output window, press Ctrl-C, and
find yourself magically transported into the OS/2 Kernel Debugger. The **Control Panel** display is functioning
as both the output window for all PCjs messages and PCjs Debugger commands, as well as a serial input/output device
(aka "Dumb Terminal") for any software inside the machine communicating via COM2: in this case, the OS/2 Kernel Debugger.
> SIDEBAR: You can perform similar tricks with DOS in these machines. Boot any DOS disk (version 2.00 and up)
and type "CTTY COM2" at the DOS prompt. All DOS input/output will now be routed to the **Control Panel** display.
To restore control to the the machine's keyboard and video display, type "CTTY CON".
Type "?" for a list of all OS/2 Kernel Debugger commands. Type "g" to continue running OS/2. Make sure you type all
OS/2 Kernel Debugger commands into the **Control Panel** output window. Commands typed into the input box *beneath*
the output window are processed only by the PCjs Debugger.
When a fault occurs, OS/2 normally displays a "TRAP" message; however, when the Kernel Debugger is running, it
intercepts the fault and displays the faulting instruction. But the PCjs Debugger has ultimate control: using
the "m fault on" and "m halt on" commands, the PCjs Debugger will display and halt on any fault first. If you want
PCjs to deliver the fault to OS/2, single-step over the faulting instruction and then continue.
There are still a number of known issues running OS/2. For example, when attempting to install OS/2 1.0 from the
installation diskette images onto a hard disk image, OS/2 successfully formats the hard disk and copies the files from
the first two diskettes, but usually while copying files from either the second or third diskette, the process stops.
There's no crash -- it simply stops copying files and never finishes. My best guess at this point is that some
interrupts are being dropped.
Similarly, if a machine running OS/2 1.0 is left unattended for a few minutes, it may stop responding. Again, there's
no crash or other indication of a problem. The machine simply appears hung. "Ctrl-Alt-Del" and "Reset" buttons still
work.
The journey continues.
*[@jeffpar](http://twitter.com/jeffpar)*
*December 4, 2014*

View file

@ -1,71 +0,0 @@
---
layout: post
title: Canvas Performance and ContentEditable
date: 2014-12-05 11:00:00
category: HTML5
permalink: /blog/2014/12/05/
---
From the beginning of the [JavaScript Machines](/docs/about/) Project, I've always used an HTML5
[Canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) object for both machine output
and input. It's the obvious choice for output, because the Canvas provides a 2D drawing API that's
essential both for drawing bitmappped graphics and for faithfully rendering individual characters
using the machine's original bitmapped fonts.
The Canvas is perhaps a less obvious choice for input, but the theory was that by adding a
"[contenteditable](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_Editable)" attribute
to the Canvas object, the user could simply click (or tap) the Canvas to give it focus, and then all the
usual *onkeydown*, *onkeyup*, and *onkeypress* event handlers would work as expected. The advantage of
this approach is that it eliminated the need for another on-screen control that would no serve no visual
purpose.
The "contenteditable" attribute had some issues, but mainly only on mobile devices, so I left those
issues for another day. For example, using PCjs on an Android device is problematic, in part because
it doesn't honor the "contenteditable" attribute on a Canvas, but also because Android's built-in
"soft keyboard" doesn't deliver any keys to the application until you press Enter. So for now, you
have to use PCjs machines that come with their own "soft keyboard".
However, today I noticed an oddity with Safari on the desktop. For the most part, Safari and Chrome
perform comparably, and are generally the best browsers to use with PCjs. Firefox used to be a great
option a couple years ago, but ever since Mozilla started focusing heavily -- perhaps *too* heavily -- on
[asm.js](http://asmjs.org/) performance, they seem to have fallen behind in overall performance.
But I digress. What I noticed in Safari was that text-scrolling in both DOS and OS/2 was significantly
slower than Chrome. This seemed very odd -- they should have been almost equally fast. Then I made
an important discovery: while the machine was scrolling, if I clicked on some other part of the page,
taking focus *away* from the Canvas, scrolling dramatically sped up. When I clicked on the Canvas
again, it slowed way down again.
Long story short: when I removed the "contenteditable" attribute from the Canvas, drawing performance
was consistently fast. The only problem, of course, is that I couldn't type anything into the machine.
So I resurrected some old code I'd written that creates a transparent
&lt;[textarea](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea)&gt; on top of the Canvas,
and now I use the &lt;textarea&gt; to provide all keyboard, mouse, and touch events (and pointer locking,
for the handful of browsers that support it).
That seemed to work well, until I tested Safari on an iPad, where I noticed a blinking cursor in the top
left corner of the machine's screen; that is, the top left corner of the transparent &lt;textarea&gt;. I tried
all sorts of work-arounds suggested online -- setting the textarea's "color" attribute to "transparent",
on the theory that the cursor used the same color, or setting the "cursor" attribute to "none" -- but none
of those work-arounds seemed to, um, work.
I had almost settled on adding iOS detection code, and reverting to the old Canvas input code for iOS only,
when I noticed that even a Canvas on iOS displayed a blinking cursor -- it was just slightly less annoying
because the cursor was flush with the left edge of the Canvas. More importantly, it was also as tall as
the full height of the Canvas.
At this point, it seemed clear that iOS was trying to display the cursor based on what it believed the
line height to be (ie, the full height of the Canvas). So I switched back to the transparent &lt;textarea&gt;
again, set its "line-height" attribute to zero, and viola: no more blinking cursor.
So that, in a nutshell, is why v1.16.2 of PCjs comes one day after v1.16.1: because I happened to noticed
that drawing performance in desktop Safari was suffering, and that there was a fairly straightforward solution.
Safari's behavior should probably be considered a bug, as it's probably doing something it shouldn't,
like trying to account for an "invisible" blinking cursor. Chrome certainly doesn't have this problem,
so unless I was the only person in the world who used "contenteditable" Canvases, this is probably something
Safari will want to fix.
*[@jeffpar](http://twitter.com/jeffpar)*
*December 5, 2014*

View file

@ -1,48 +0,0 @@
---
layout: post
title: PCx86 Uncompiled
date: 2015-01-17 11:00:00
category: Features
permalink: /blog/2015/01/17/
machines:
- type: pcx86
id: at-ega-1152k-rev3
debugger: true
uncompiled: true
config: /devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/machine.xml
---
Most PCx86 machines on [{{ site.pcjs.domain }}](/) run with a compiled version of PCx86, which is produced
by running the PCx86 JavaScript source code through Google's Closure Compiler, yielding a smaller (minified)
version that loads and runs much faster than the original source code.
However, certain features are disabled in the compiled versions, including a new BACKTRACK feature that
makes it possible to track the contents of memory locations and registers back to their source (eg, to a ROM
or file location). Once the BACKTRACK feature is finished, it will be folded into the compiled code, but until
then, the only way to experiment with it is by running the uncompiled code.
To make it easier to launch machines with uncompiled code, a PCx86 machine definition can now set `uncompiled`
to *true*, overriding the value of `site.pcjs.compiled` in **_config.yml**.
Here's what a typical Markdown file would look like:
{% raw %}
---
...
machines:
- type: pcx86
id: at-ega-1152k-rev3
debugger: true
uncompiled: true
config: /devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/backtrack/machine.xml
---
...
{% include machine.html id="at-ega-1152k-rev3" %}
{% endraw %}
In fact, that's what we've done in the Markdown file you are reading right now.
{% include machine.html id="at-ega-1152k-rev3" %}
*[@jeffpar](http://twitter.com/jeffpar)*
*January 17, 2015 (Updated December 10, 2015 to reflect the new `uncompiled` property)*

View file

@ -1,23 +0,0 @@
---
layout: post
title: New PCx86 Control Panel
date: 2015-01-28 11:00:00
category: Control Panel
permalink: /blog/2015/01/28/
machines:
- type: pcx86
id: at-ega-1152k-rev3
debugger: true
uncompiled: true
config: /devices/pcx86/machine/5170/ega/1152kb/rev3/debugger/backtrack/machine.xml
---
A new PCx86 Control Panel is under development, featuring a new "Display Panel" that will provide a variety of
information about the machine, in real-time, and operate more efficiently than previous DOM-based Control Panels.
A preview of the layout is shown below. There's not much to see yet, as this is very much a work-in-progress.
{% include machine.html id="at-ega-1152k-rev3" %}
*[@jeffpar](http://twitter.com/jeffpar)*
*January 28, 2015*

View file

@ -1,30 +0,0 @@
---
layout: post
title: COMPAQ DeskPro 386
date: 2015-02-22 11:00:00
category: 80386
permalink: /blog/2015/02/22/
machines:
- type: pcx86
id: deskpro386
debugger: true
uncompiled: true
config: /devices/pcx86/machine/compaq/deskpro386/ega/2048kb/debugger/machine.xml
---
I finally dumped the [COMPAQ DeskPro 386/16 ROMs](/devices/pcx86/rom/compaq/deskpro386/) from the motherboard I bought
on ebay last year, so I'm ready to begin adding 80386 support to PCx86.
I'd also like to locate a copy of the "COMPAQ DeskPro 386 Technical Reference Guide, Volumes 1 and 2". It's not hard
to find COMPAQ Maintenance and Service guides online, but their Technical Reference guides are much rarer, perhaps because
they were expensive ($149) and not many were sold. Anyway, I'm hoping to either borrow or buy a copy, and then scan and
post it.
A [COMPAQ DeskPro 386](/devices/pcx86/machine/compaq/deskpro386/ega/2048kb/debugger/) test configuration is displayed below.
The configuration doesn't run, and the debugger can't disassemble 80386-specific code yet, but this is what I will be
using to test and debug my changes over the next few months.
{% include machine.html id="deskpro386" %}
*[@jeffpar](http://twitter.com/jeffpar)*
*February 22, 2015*

View file

@ -1,116 +0,0 @@
---
layout: post
title: Early 80386 CPUs
date: 2015-02-23 11:00:00
category: 80386
permalink: /blog/2015/02/23/
---
Assembling a detailed and accurate history of the Intel 80386 CPU, including a complete listing of all the
"steppings" (revisions), when they were released, what "errata" (problems) each stepping suffered from, and
which of those problems were fixed by a later stepping, seems virtually impossible at this late date.
See [Intel 80386 CPU Information](/pubs/pc/reference/intel/80386/) for what the PCjs Project has been able to
collect about 80386 steppings so far, including how some of them were externally marked and internally identified,
along with lists of associated errata, much of it based on Intel's own documents.
Of course, there's also an eclectic mix of information about early 80386 processors available from various online
sources. A few of those are highlighted below.
---
Excerpt from "Inside Track", [PC Magazine, February 24, 1987](https://books.google.com/books?id=phxlBt4dX3oC&lpg=PA67&pg=PA67#v=onepage),
by John C. Dvorak:
> **80386 Bug Stopper Dept.**: If you buy an 80386 machine, card, or chip, make sure you **get the B1 revision of
the chip** or something newer (B2, B3, and so on). There are **far too many bugs** in the A1 and A2 versions of the
chip to be acceptable. Here's what to look for: On the top line of the chip you'll see the designation A80386-16.
If it says A80386-16ES, then it's an engineering sample and the vendor is a *cheapskate*. The samples have the revision
number on the top line as A1, A2, or B1. Look no further.
> For the rest of you, look at the second line on the chip. If it's S40344 then you have a B1 chip. S40334 is the
A2 revision and S40276 is the A1 revision....
---
Excerpt from "Tutor", [PC Magazine, October 15, 1991](https://books.google.com/books?id=tSLe3yMjc-AC&pg=PT438&hl=en&sa=X&ved=0ahUKEwjR9MH4gOHJAhVT0mMKHc4tD0YQ6AEIKjAA#v=onepage),
by Jeff Prosise:
> You can tell if you have a B0 or B1 Step level 386 by looking at the markings on the chip. If it has the ID number
S40336 or S40337 stamped on it, then it's a Step B0; if it's marked with S40343, S40344, or S40362, it's a Step B1.
Some B0 and B1 chips were marked B0 or B1 rather than with an ID number.
---
Excerpt from "[CPU Identification by the Windows Kernel](http://www.geoffchappell.com/studies/windows/km/cpu/index.htm)", by Geoff Chappell:
> Finer identification of 80386 processors is largely academic. Whatever the model or stepping, the 80386 processor
is unsupported since [Windows NT] version 4.0, and soon causes the bug check UNSUPPORTED_PROCESSOR (0x5D), though not
without the kernel having worked its way through more tests for defects to identify models and steppings. For any 80386
processor that passes all tests, the model and stepping leap ahead to 3 and 1. Version 3.51, which was the last to
support the 80386 (and only then in a single-processor configuration), rejects any 80386 that does not pass all these
tests.
Family Model Stepping Test
------ ----- -------- ----
3 0 0 32-bit MUL not reliably correct
3 1 0 supports XBTS instruction
3 1 1 set TF bit (0x0100) in EFLAGS causes Debug exception (interrupt 0x01) only at completion of REP MOVSB
3 3 1
> The particular multiplication that distinguishes model 0 is of 0x81 by 0x0417A000. This same test was used by Microsoft
at least as far back as Windows 3.10 Enhanced Mode, to advise:
The Intel 80386 processor in this computer does not reliably execute 32-bit
multiply operations. Windows usually works correctly on computers with this
problem but may occasionally fail. You may want to replace your 80386 processor.
Press any key to continue...
> The instruction whose support is tested for model 1 stepping 0 has opcode bytes 0x0F 0xA6 followed by a Mod R/M byte
and by whatever more this byte indicates is needed for the operand. This opcode is disassembled as XBTS by Microsofts
DUMPBIN utility from Visual C++, and has been since at least the mid-90s. However, the same opcode was apparently reused
for the CMPXCHG instruction on some 80486 processors. The confusion seems to have left a lasting mark: Intels opcode
charts leave 0x0F 0xA6 unassigned even now. The specific test performed by the Windows kernel is to load EAX and EDX
with zero and ECX with 0xFF00. If executing XBTS ECX,EDX does not cause an Invalid Opcode exception and clears ecx to
zero (which CMPXCHG ECX,EDX would not), then XBTS is deemed to be supported and the processor is model 1 stepping 0.
This case of 80386 processor also was known to Windows 3.10 Enhanced Mode, and was rejected as fatal:
Windows may not run correctly with the 80386 processor in this computer.
Upgrade your 80386 processor or start Windows in standard mode by typing
WIN /s at the MS-DOS prompt.
> When string instructions such as MOVSB are repeated because of a REP prefix, each operation is ordinarily interruptible.
As Intel says (for REP in the [Intel 64 and IA-32 Architectures Software Developers Manual Volume 2B: Instruction Set Reference N-Z](http://www.intel.com/design/processor/manuals/253667.pdf)),
this “allows long string operations to proceed without affecting the interrupt response time of the system.” It ordinarily
applies also to the Debug exception, such as raised by the processor at the end of executing an instruction for which the TF
bit is set in the EFLAGS when the instruction started. Programmers may have noticed this in the real world of assembly-language
debugging. If the debugger actually does implement its trace command as a trace, as opposed to setting an INT 3 breakpoint
where the instruction is calculated to end, then a two-byte REP MOVSB may take many keystrokes to trace through! That
model 1 stepping 1 traces through a REP MOVSB without interruption may be helpful when debugging, but it is surely a defect.
---
More examples of problems with early 80386 CPUs are posted in "[The Old New Thing](http://blogs.msdn.com/b/oldnewthing/)"
blog. Here are some highlights from "[My, what strange NOPs you have!](http://blogs.msdn.com/b/oldnewthing/archive/2011/01/12/10114521.aspx)",
by Raymond Chen:
> [I]f the instruction following a string operation (such as movs) uses opposite-sized addresses from that in the string
instruction (for example, if you performed a movs es:[edi], ds:[esi] followed by a mov ax, [bx]) or if the following
instruction accessed an opposite-sized stack (for example, if you performed a movs es:[edi], ds:[esi] on a 16-bit stack,
and the next instruction was a push), then the movs instruction would not operate correctly....
> [T]here was one bug that manifested itself in incorrect instruction decoding if a conditional branch instruction
had just the right sequence of taken/not-taken history, and the branch instruction was followed immediately by a selector load,
and one of the first two instructions at the destination of the branch was itself a jump, call, or return. The easy workaround:
Insert a NOP between the branch and the selector load....
> [T]he B1 stepping did not support virtual memory in the first 64KB of memory. Fine, don't use virtual memory there....
> If virtual memory was enabled, if a certain race condition was encountered inside the hardware prefetch, and if you executed
a floating point coprocessor instruction that accessed memory at an address in the range 0x800000F8 through 0x800000FF,
then the CPU would end up reading from addresses 0x000000F8 through 0x0000000FF instead. This one was easy to work around:
Never allocate valid memory at 0x80000xxx.
*[@jeffpar](http://twitter.com/jeffpar)*
*February 23, 2015*

View file

@ -1,210 +0,0 @@
---
layout: post
title: JavaScript Idiosyncrasies
date: 2015-03-26 11:00:00
category: JavaScript
permalink: /blog/2015/03/26/
---
Time to mention a few JavaScript idiosyncrasies, and how I deal with them.
Also, see my previous posts on [PCjs Coding Conventions](/blog/2014/09/30/) and [JavaScript Negativity](/blog/2014/10/26/).
### Strict Equality
Many JavaScript websites will advise you to *never* use the "==" and "!=" JavaScript operators, because when they compare
variables containing different data types, JavaScript will coerce one of the operands to a matching type, sometimes in
unexpected ways. We can thank the early days of JavaScript for this feature, when it was trying to be extraordinarily
forgiving of sloppy code. I'm not going to list all the odd results that can arise from JavaScript's operand coercion,
because there are more than enough examples on the web already.
To avoid unexpected type coercion, and thus unexpected matches and/or mismatches, the usual advice is to *always* use
strict equality operators ("===" and "!==").
I disagree.
In well-written code, the variable data types should always be clear. In fact, the more you're able to
use [JSDoc](http://developers.google.com/closure/compiler/docs/js-for-compiler) types to declare the data types
of all your parameters, return values, and other variables, the fewer errors you'll have. As long as you're always
comparing variables with matching types, there shouldn't be any unexpected coercions.
Obviously, there will be times when a polymorphic variable is required, especially when dealing with APIs that can
return multiple types. But those should be the exception, not the rule.
Another exception is optional parameters. When I write a method with optional parameters, I generally allow those
parameters to either be omitted (ie, *undefined*) or set to *null*. Using "==", you can check for either value with
a single comparison:
``` javascript
if (parameter == null) { ... }
```
whereas strict equality requires more work:
``` javascript
if (parameter === undefined || parameter === null) { ... }
```
This is one of those times when coercion (of *undefined* to *null*), and the use of "non-strict" operators, is beneficial.
Here's another:
``` javascript
if (!b) { ... }
```
Coercing a value to *boolean* is a popular way of checking for all "falsy" values (ie, *undefined*, *null*,
0, false, "", NaN, etc). It is shorthand for:
``` javascript
if (b == false) { ... }
```
yet I suspect the proponents of strict equality would embrace the former while rejecting the latter.
However, I don't recommend "falsy" checks for optional parameters:
``` javascript
if (!parameter) { ... }
```
because often a valid numeric parameter might include 0, or a valid string parameter might include "", so it's better
to do this:
``` javascript
if (parameter == null) { ... }
```
and obviously if *null* is also a acceptable value, then you should definitely use strict equality:
``` javascript
if (parameter === undefined) { ... }
```
Problems with type coercion are **NOT** problems caused by a poor choice of operators, so trying to make
those problems go away by artificially limiting your choice of operators seems like the wrong solution.
Type coercion problems are, by definition, problems involving mismatched types. Solutions include:
- Avoid comparing variables of different types; or
- Convert your variables to matching types first; or
- Use strict equality operators (just don't use them mindlessly)
Explicitly convert variables to a single type whenever possible. For example, I might define a method
that accepts an optional numeric parameter, with a documented default value when it's omitted. I think it's
important make that parameter unambiguously numeric as soon as possible; eg:
``` javascript
/**
* foo(n)
*
* Performs a mathematical operation on n and returns a result.
*
* @param {number} [n] is an optional parameter (defaults to zero if omitted)
* @return {number}
*/
function foo(n) {
n = n || 0;
...
}
```
The expression `n || 0` might seem pointless, because *undefined* and *zero* are equivalent in a "falsy" sense, but
*undefined* is not a number, and there will be fewer problems downstream if you ensure that n is *always* a number.
### Enumerating Array or Object Properties
When using *for*...*in* loops like this:
``` javascript
var a = [100, 200, 300];
for (var i in a) { ... }
```
the type of variable *i* will be **string** rather than **number**; that is, it will contain "0", "1", and "2" rather
than 0, 1, and 2. If you then use *i* to set a matching element in another array, that element will not be stored in
the same (numeric) position as the original array.
One solution is to convert *i* to a **number**:
``` javascript
parseInt(i, 10);
```
However, a more elegant solution is to use the unary "+" operator to coerce the **string** to a **number**:
``` javascript
+i;
```
The same problem arises with objects using numeric properties. And watch out for JavaScript's automatic base
conversion of numeric properties. For example, when you enumerate the properties of object "o":
``` javascript
var o = {
0x20: ' ',
0x41: 'A'
};
```
you will get the strings "32" and "65", not "0x20" and "0x41". You must quote your property names to prevent
any conversion; eg:
``` javascript
var o = {
"0x20": ' ',
"0x41": 'A'
};
```
Numeric properties can always be safely converted using the unary "+" operator, regardless whether they were quoted
or not.
The unary "+" is a great alternative to parseInt(), but be mindful of their differences. One important difference
is that parseInt() will stop when it encounters an invalid digit, returning whatever value was parsed up to that point,
whereas unary "+" conversion will return *NaN* if there are any invalid digits in the string.
### Shift Counts For Bitwise Shifts
It turns out that shifting an integer value by more than 31 bits in either direction may not shift as many bits as
you'd expect. For example:
``` javascript
n = 0x10000000;
n >>>= 33;
```
will shift n by only *one* bit, not 33 bits, and the result will be 0x08000000, not zero. This is because,
just like the shift instructions on 32-bit Intel processors, JavaScript converts the shift count to a *mod 32* value
(in other words, it truncates the shift count to a 5-bit value).
So the above example is equivalent to:
``` javascript
n >>>= 1;
```
If you really need larger shift counts to work in a consistent manner, you can perform multiple shifts, where each
shift count is in the range 0-31. Here's one way to shift a number 33 bits:
``` javascript
n = (n >>> 31) >>> 2;
```
Also, it's not quite correct to say that a shift count of zero has *no* effect on a number:
``` javascript
n = 0x88888888|0; // n is displayed as -2004318072
n >>>= 0; // n is displayed as 2290649224
```
It's true that the bottom 32 bits of the number were not changed, but a side-effect of the unsigned shift operator
is that all the upper sign bits are stripped from the (64-bit) result.
Similarly, as soon as you perform any other bitwise operation on the number, even one that does not modify the low
32 bits, the upper bits will revert to the sign of the lower 32-bit value:
``` javascript
n |= 0; // n is displayed as -2004318072 again
```
*[@jeffpar](http://twitter.com/jeffpar)*
*March 26, 2015*

View file

@ -1,154 +0,0 @@
---
layout: post
title: COMPAQ DeskPro 386 Update
date: 2015-04-16 11:00:00
category: 80386
permalink: /blog/2015/04/16/
machines:
- type: pcx86
id: deskpro386
debugger: true
uncompiled: true
config: /devices/pcx86/machine/compaq/deskpro386/ega/2048kb/debugger/machine.xml
---
PCx86 can now boot the [COMPAQ DeskPro 386/16 ROM BIOS](/devices/pcx86/rom/compaq/deskpro386/).
There's still a problem with the Hard Drive Controller, which I haven't looked into yet,
but booting from a floppy works.
While working through issues with this ROM BIOS, I created some lightly-annotated
[source code](/devices/pcx86/rom/compaq/deskpro386/1988-01-28/1988-01-28.asm) that can be re-assembled
with [NASM](http://www.nasm.us/). The initial process of creating the source code is
explained [here](/devices/pcx86/rom/compaq/deskpro386/#recreating-rom-source-code).
At the top of the source code, I explain a few important details about ROM addresses that
are worth recapping here:
> This 32Kb ROM image is ORG'ed at 0x8000, because most of its code is designed to run
at real-mode addresses F000:8000 through F000:FFFF.
> And even though the 80386 resets with CS:IP set to F000:FFF0, the physical base address
of CS is set to %FFFF0000, which means the ROM must also be mapped at physical addresses
%FFFF8000 through %FFFFFFFF.
> Additionally, DeskPro 386 systems mirror this 32Kb ROM at real-mode address F000:0000
through F000:7FFF. Once again, that region is mirrored at physical addresses %FFFF0000
through %FFFF7FFFF.
> In other words, both 32Kb halves of the last 64Kb of both the first and last megabyte
of the 80386's 4Gb address space are physically mapped to this ROM image.
> Finally, the DeskPro 386 has a "RAM Relocation" feature that allows 128Kb of RAM at
%00FE0000 through %00FFFFFF to be mapped to %000E0000 through %000FFFFF, effectively
replacing the ROM in the first megabyte with write-protected RAM; the top 64Kb of that
RAM must first be initialized with the 64Kb at %000F0000 prior to remapping. It's also
possible to copy external ROMs from %000C0000 through %000EFFFF into the bottom 64Kb of
that RAM, but this is only done for ROMs known to contain relocatable code; eg, a COMPAQ
Video Graphics Controller (VGC) Board.
> Every DeskPro 386 system must have a MINIMUM of 1Mb of RAM, of which either 256Kb,
512Kb, or 640Kb can be physically mapped as conventional memory (at the bottom of the
first megabyte), with the remainder (either 768Kb, 512Kb, or 384Kb) physically mapped
to the top of the 16th megabyte (ending at address %00FFFFFF), the last 128Kb of which
is used by the "RAM Relocation" feature. The remaining memory immediately below that
128Kb (ie, below %00FE0000) can only be accessed by special system software, such as CEMM.
> COMPAQ refers to that remaining memory as "Compaq Built-in Memory".
So there you have it. Once the ROM has relocated itself to RAM at the top of the 16th
megabyte, there are no less than THREE physical address ranges where ROM code and data
structures can be accessed:
1. %000F0000 through %000FFFFF (aka real-mode adresses F000:0000 through F000:FFFF)
2. %00FF0000 through %00FFFFFF (the relocated copy)
3. %FFFF0000 through %FFFFFFFF (the physical alias of %000F0000 through %000FFFFF)
As you would expect, most of the ROM's code and data references are to first megabyte,
since most of the code is designed to run in real-mode. But there are portions that
run in protected-mode, and those portions are much less consistent about which address
range to use -- no doubt, in part, because it makes no difference. The ROM does
not run with paging enabled, so any physical address is as easy to access as any other.
Unless, of course, the A20 line is disabled. In that case, only the first range is
accessible; the other two are not.
April 19, 2015 Update
---
Thanks to some sleuthing by [Michal Necasek](http://os2museum.com/), it turns out that my
assumptions about A20 management on the COMPAQ DeskPro 386 were incorrect.
He noted that, on page 398 of "DOS Internals" by Geoff Chappell, (c) 1994, the author says:
> On a machine that controls the A20 by passing the address line through an AND gate with a
signal from some bit at an I/O port, the A20MAP program should produce a map similar to:
Memory mapping with disabled A20 line:
0MB -> 0MB
1MB -> 0MB
2MB -> 2MB
3MB -> 2MB
4MB -> 4MB
5MB -> 4MB
6MB -> 6MB
7MB -> 6MB
> showing wrap-around for every second megabyte. It is also possible to include other address lines
in the controlling mechanism, which may reduce the incidence of wrap-around, as with a Compaq
DeskPro:
0MB -> 0MB
1MB -> 0MB
2MB -> 2MB
3MB -> 3MB
4MB -> 4MB
---
This means that the DeskPro ROM BIOS can, in fact, access its own code and data at ANY of the above
three physical address ranges at any time, regardless whether A20 is disabled or not.
Which is a good thing, because I came across at least one code sequence in the ROM BIOS that enters
protected-mode with A20 disabled -- an unwise thing to do on most machines:
;
; When we arrive here, the A20 line has been disabled; on most systems, that would
; mean that the ROM's GDT would only be accessible at the "low" ROM address (%0F0730),
; not the "high" address (%FF0730). But fortunately, A20 management on COMPAQ
; DeskPros affects wrap-around only from the 1st to the 2nd megabyte; no other address
; range is affected.
;
; FYI, it seems this code doesn't do anything if bits 6 and 7 of the RAM Settings
; register are set to anything other than 0x40 (ie, it returns to real-mode almost
; immediately after entering protected-mode).
;
lgdt [cs:0x077e] ; 0000F498 2E0F01167E07; load [gdtr_hi] into GDTR
mov eax,cr0 ; 0000F49E 0F2000
or ax,0x1 ; 0000F4A1 0D0100
mov cr0,eax ; 0000F4A4 0F2200
jmp 0x28:xf4ac ; 0000F4A7 EAACF42800
Before fully understanding the DeskPro's unusual A20 management, PCx86 worked around it by
redirecting all A20 changes from the Bus component to the CPU component, giving the CPU first
crack at any changes to A20. If the CPU was in real-mode, it would simply pass the A20 request
on to the Bus. However, if the CPU was in protected-mode, it would maintain the requested
"logical" A20 state but ensure that the "physical" state of A20 was always enabled. In short,
it was no longer possible for the CPU to be in protected-mode AND for the A20 line to be disabled;
when one was enabled, the other was enabled as well.
I'm in the process of replacing that work-around with a much more compatible change, at least
on 32-bit bus configurations, which involves changing the physical address map for the 2nd megabyte
to match that of the 1st megabyte whenever A20 is disabled. I could probably get away with remapping
only the first 64Kb of the 2nd megabyte, but until I'm actually able to run some tests on a real
DeskPro 386, I'm going to assume COMPAQ's A20 implementation affected the entire 2nd megabyte.
Here's my [COMPAQ DeskPro 386/16](/devices/pcx86/machine/compaq/deskpro386/ega/2048kb/debugger/) test
configuration. Set a breakpoint at F000:F498 ("bp f000:f498") in the Debugger panel to see the above
code in action. When the machine is operating in real-mode, you can use the "rp" command to dump all
the registers, including the current base and limit values loaded into the segment registers.
{% include machine.html id="deskpro386" %}
*[@jeffpar](http://twitter.com/jeffpar)*
*April 16-19, 2015*

View file

@ -1,26 +0,0 @@
---
layout: post
title: PC Tech Journal Collection
date: 2015-05-20 11:00:00
category: PC Tech Journal
permalink: /blog/2015/05/20/
---
This is an update to my 2014 [post](/blog/2014/08/01/) on the PCjs online collection of old
[PC Tech Journal](/pubs/pc/magazines/pctj/) magazine issues.
Our collection is much more complete now. We have the first issue, the last issue, and almost all the issues
in between. All we're currently missing are the April 1988 issue and the first three issues of 1989, at least in
terms of regular issues.
We also have the [1987 Editorial Index and Comprehensive Product Guide](/pubs/pc/magazines/pctj/PCTJ-1987-00/);
however, even though it identifies itself as a 1987 issue, the editorial index only covers issues through
October 1986, so "technically" it should be considered a late 1986 issue. It is officially Vol. 4, No. 13, which,
numerically, puts it squarely between the December 1986 and January 1987 issues.
[<img src="http://archive.pcjs.org/pubs/pc/magazines/pctj/PCTJ-1983-07/thumbs/PCTJ-1983-07 1.jpeg" width="200" height="260" alt="PC Tech Journal, July-August 1983"/>](/pubs/pc/magazines/pctj/)
Happy reading!
*[@jeffpar](http://twitter.com/jeffpar)*
*May 20, 2015*

View file

@ -1,142 +0,0 @@
---
layout: post
title: Debugging the IBM VGA ROM
date: 2015-06-01 11:00:00
category: Video
permalink: /blog/2015/06/01/
---
The IBM VGA ("Video Graphics Array") standard was introduced as part of the IBM PS/2 line of computers;
it was not a feature you could purchase or install in older PC, XT or AT-compatible machines. In fact, full VGA
support was not even available in all PS/2 models.
Of the first four PS/2 models -- the 8086-based Model 30, the 80286-based Model 50 and Model 60, and the 80386-based
Model 80 -- VGA support was available only in the three higher-end models. The Model 30 came with MCGA ("Multicolor
Graphics Array") video hardware that supported a subset of VGA modes (eg, 640x480 2-color and 320x200 256-color graphics).
It wasn't until October 1987 that IBM finally introduced an 8-bit ISA card that brought VGA capability to older PCs.
The card was called the **IBM PS/2 Display Adapter**. However, I think the name is a bit confusing, since the card
could only be used in PC, XT, and AT-compatible systems. I'll refer to it here simply as the IBM VGA.
The VGA ROM used here is assumed to have come from an original IBM VGA. It's unknown if IBM ever made any
revisions to the VGA ROM. With the introduction of the PS/2 family and the VGA, IBM decided to no longer publish
the source code for its ROMs, so I've created some assemblable source code from the IBM VGA ROM
[here](/devices/pcx86/video/ibm/vga/).
I've finally started debugging a machine configuration that uses the IBM VGA ROM. Since the VGA and the 80386 are
contemporaries, I'm using an [80386 machine configuration](/devices/pcx86/machine/compaq/deskpro386/vga/2048kb/debugger/).
However, I don't expect the IBM VGA ROM to require any 80386 support or PS/2-specific features.
The first problem I ran into was here:
;
; Initialize the ROM BIOS Video Mode Options byte @40:0087 (default to color and 256Kb of RAM)
;
mov byte [0x487],0x60 ; 0000008D
;
; The x100 subroutine alternately enables port 0x3B? and 0x3D? decoding, verifying that there is
; no response on opposing ports 0x3D? and 0x3B?, respectively; otherwise, it assumes that another
; video card must exist and attempts to select co-existing settings for the VGA. For example, if
; there is an unexpected response on the color ports, the VGA ROM will default to mono operation.
;
call x100 ; 00000092
The Video component installs I/O port handlers for all possible I/O ranges; when a range isn't being used, the
associated I/O operations are redirected to a dummy Card, so that the active Card isn't affected. Here,
however, that was insufficient. If the VGA is the only installed video card, the VGA ROM expects *NO RESPONSE*
on inactive CRTC ports. So I've changed the CRTC I/O handlers to check the Card's fActive flag. This seems
like a safe and logical change, but I still have to check for backward-compatibility issues with older ROMs.
Other problems included:
* Some bugs in Read Mode 1 that caused a memory test failure
* Horizontal and vertical retrace timing issues (the ROM requires a specific number of intervals per second)
* Differences between the EGA and VGA in the SWSENSE bit (bit 4) of Input Status Register 0
The last problem was the most puzzling, because the ROM programs a series of values into the first DAC register,
and expects the SWSENSE bit of Input Status Register 0 to change in very specific ways, depending on the kind
of monitor attached.
I've not found any hardware documentation that explains exactly how this should work. IBM's own Technical Reference
material is extremely vague:
"Bit 4: Switch Sense Bit - This bit allows the system microprocessor to read the switch sense line.
This bit allows the power-on self-test to determine if a monochrome or color display is connected to
the system."
I've hard-coded a solution that assumes a color monitor. Support for using a monochrome monitor with an EGA was
never completed, and this is another related issue that will have to be resolved for the VGA as well.
---
While debugging and fixing assorted IBM VGA issues, I made a table of all the register values for the video
modes commonly used on the IBM VGA. Here's that table:
INT 0x10 Mode Requested: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x0D 0x0E 0x10 0x12 0x13
BIOSMODE: 0x01 0x01 0x03 0x03 0x04 0x04 0x06 0x0D 0x0E 0x10 0x12 0x13
CRTC[0x00]: HTOTAL 0x2D 0x2D 0x5F 0x5F 0x2D 0x2D 0x5F 0x2D 0x5F 0x5F 0x5F 0x5F
CRTC[0x01]: HDISP_END 0x27 0x27 0x4F 0x4F 0x27 0x27 0x4F 0x27 0x4F 0x4F 0x4F 0x4F
CRTC[0x02]: HBLANK_START 0x28 0x28 0x50 0x50 0x28 0x28 0x50 0x28 0x50 0x50 0x50 0x50
CRTC[0x03]: HBLANK_END 0x90 0x90 0x82 0x82 0x90 0x90 0x82 0x90 0x82 0x82 0x82 0x82
CRTC[0x04]: HRETRACE_START 0x2B 0x2B 0x55 0x55 0x2B 0x2B 0x54 0x2B 0x54 0x54 0x54 0x54
CRTC[0x05]: HRETRACE_END 0xA0 0xA0 0x81 0x81 0x80 0x80 0x80 0x80 0x80 0x80 0x80 0x80
CRTC[0x06]: VTOTAL 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0xBF 0x0B 0xBF
CRTC[0x07]: OVERFLOW 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x1F 0x3E 0x1F
CRTC[0x08]: PRESET_ROW 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
CRTC[0x09]: MAX_SCAN 0x4F 0x4F 0x4F 0x4F 0xC1 0xC1 0xC1 0xC0 0xC0 0x40 0x40 0x41
CRTC[0x0A]: CURSOR_START 0x0D 0x0D 0x0D 0x0D 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
CRTC[0x0B]: CURSOR_END 0x0E 0x0E 0x0E 0x0E 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
CRTC[0x0C]: START_ADDR_HI 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
CRTC[0x0D]: START_ADDR_LO 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
CRTC[0x0E]: CURSOR_ADDR_HI 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x00
CRTC[0x0F]: CURSOR_ADDR_LO 0x19 0x19 0x41 0x41 0x19 0x19 0x41 0x19 0x41 0x41 0xE1 0xA2
CRTC[0x10]: VRETRACE_START 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x9C 0x83 0xEA 0x9C
CRTC[0x11]: VRETRACE_END 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x8E 0x85 0x8C 0x8E
CRTC[0x12]: VDISP_END 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x8F 0x5D 0xDF 0x8F
CRTC[0x13]: OFFSET 0x14 0x14 0x28 0x28 0x14 0x14 0x28 0x14 0x28 0x28 0x28 0x28
CRTC[0x14]: UNDERLINE 0x1F 0x1F 0x1F 0x1F 0x00 0x00 0x00 0x00 0x00 0x0F 0x00 0x40
CRTC[0x15]: VBLANK_START 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x96 0x63 0xE7 0x96
CRTC[0x16]: VBLANK_END 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xB9 0xBA 0x04 0xB9
CRTC[0x17]: MODE_CTRL 0xA3 0xA3 0xA3 0xA3 0xA2 0xA2 0xC2 0xE3 0xE3 0xE3 0xE3 0xA3
CRTC[0x18]: LINE_COMPARE 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
GRC[0x00]: SRESET 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
GRC[0x01]: ESRESET 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
GRC[0x02]: COLORCMP 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
GRC[0x03]: DATAROT 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
GRC[0x04]: READMAP 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
GRC[0x05]: MODE 0x10 0x10 0x10 0x10 0x30 0x30 0x00 0x00 0x00 0x00 0x00 0x40
GRC[0x06]: MISC 0x0E 0x0E 0x0E 0x0E 0x0F 0x0F 0x0D 0x05 0x05 0x05 0x05 0x05
GRC[0x07]: COLORDC 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0F 0x0F 0x0F 0x0F 0x0F
GRC[0x08]: BITMASK 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
SEQ[0x00]: RESET 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03 0x03
SEQ[0x01]: CLOCKING 0x08 0x08 0x00 0x00 0x09 0x09 0x01 0x09 0x01 0x01 0x01 0x01
SEQ[0x02]: MAPMASK 0x03 0x03 0x03 0x03 0x03 0x03 0x01 0x0F 0x0F 0x0F 0x0F 0x0F
SEQ[0x03]: CHARMAP 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
SEQ[0x04]: MEMMODE 0x03 0x03 0x03 0x03 0x02 0x02 0x06 0x06 0x06 0x06 0x06 0x0E
ATC[0x00]: PAL00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
ATC[0x01]: PAL01 0x01 0x01 0x01 0x01 0x13 0x13 0x17 0x01 0x01 0x01 0x01 0x01
ATC[0x02]: PAL02 0x02 0x02 0x02 0x02 0x15 0x15 0x17 0x02 0x02 0x02 0x02 0x02
ATC[0x03]: PAL03 0x03 0x03 0x03 0x03 0x17 0x17 0x17 0x03 0x03 0x03 0x03 0x03
ATC[0x04]: PAL04 0x04 0x04 0x04 0x04 0x02 0x02 0x17 0x04 0x04 0x04 0x04 0x04
ATC[0x05]: PAL05 0x05 0x05 0x05 0x05 0x04 0x04 0x17 0x05 0x05 0x05 0x05 0x05
ATC[0x06]: PAL06 0x14 0x14 0x14 0x14 0x06 0x06 0x17 0x06 0x06 0x14 0x14 0x06
ATC[0x07]: PAL07 0x07 0x07 0x07 0x07 0x07 0x07 0x17 0x07 0x07 0x07 0x07 0x07
ATC[0x08]: PAL08 0x38 0x38 0x38 0x38 0x10 0x10 0x17 0x10 0x10 0x38 0x38 0x08
ATC[0x09]: PAL09 0x39 0x39 0x39 0x39 0x11 0x11 0x17 0x11 0x11 0x39 0x39 0x09
ATC[0x0A]: PAL0A 0x3A 0x3A 0x3A 0x3A 0x12 0x12 0x17 0x12 0x12 0x3A 0x3A 0x0A
ATC[0x0B]: PAL0B 0x3B 0x3B 0x3B 0x3B 0x13 0x13 0x17 0x13 0x13 0x3B 0x3B 0x0B
ATC[0x0C]: PAL0C 0x3C 0x3C 0x3C 0x3C 0x14 0x14 0x17 0x14 0x14 0x3C 0x3C 0x0C
ATC[0x0D]: PAL0D 0x3D 0x3D 0x3D 0x3D 0x15 0x15 0x17 0x15 0x15 0x3D 0x3D 0x0D
ATC[0x0E]: PAL0E 0x3E 0x3E 0x3E 0x3E 0x16 0x16 0x17 0x16 0x16 0x3E 0x3E 0x0E
ATC[0x0F]: PAL0F 0x3F 0x3F 0x3F 0x3F 0x17 0x17 0x17 0x17 0x17 0x3F 0x3F 0x0F
ATC[0x10]: MODE 0x0C 0x0C 0x0C 0x0C 0x01 0x01 0x01 0x01 0x01 0x01 0x01 0x41
ATC[0x11]: OVERSCAN 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
ATC[0x12]: PLANES 0x0F 0x0F 0x0F 0x0F 0x03 0x03 0x01 0x0F 0x0F 0x0F 0x0F 0x0F
ATC[0x13]: HPAN 0x08 0x08 0x08 0x08 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
In addition, I've created a [VGA Tests](/tests/pc/vga/) directory to hold VGA test and sample code that PCjs can
now successfully run (for the most part). See that directory for more details.
*[@jeffpar](http://twitter.com/jeffpar)*
*June 1, 2015 (Updated July 9, 2015)*

View file

@ -1,186 +0,0 @@
---
layout: post
title: The Strange Case of the EGA Graphics Scroll Bug
date: 2015-06-05 11:00:00
category: Video
permalink: /blog/2015/06/05/
---
I was playing with different video modes using this [IBM PC AT w/EGA](/devices/pcx86/machine/5170/ega/640kb/rev1/debugger/),
and I discovered an odd problem.
For example, when I ran this code:
A>b:debug
-a
0CE0:0100 mov ax,e
0CE0:0103 int 10
0CE0:0105 int 3
0CE0:0106
-g
the following "text" correctly appeared at the top of the screen, in 640x200 16-color graphics mode 0x0E:
AX=0B01 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0CE0 ES=0CE0 SS=0CE0 CS=0CE0 IP=0105 NV UP EI PL NZ NA PO NC
0CE0:0105 CC INT 3
-
And when I typed "q", then "cls" and finally "dir", the screen filled with DOS directory contents.
But as soon as the screen started to scroll, the screen contents became garbled. Other EGA graphics modes,
like 640x350 16-color mode 0x10, didn't have this problem.
To investigate, I set a breakpoint in the IBM EGA ROM where the scrolling starts, at 0xC000:12EA (see p.130 of the
"IBM Enhanced Graphics Adapter" Technical Reference document):
CRANK_A:
PUSH CX
MOV CL,DL
SUB CH,CH
PUSH SI
PUSH DI
REP MOVSB
POP DI
POP SI
ADD SI,BX
ADD DI,BX
POP CX
LOOP CRANK_A
CX contains 0xC0 (192), which is the number of scan-lines to move up, and BX contains 0x50 (80), the number
of bytes per scan-line.
When the breakpoint was hit, I dumped the video hardware state, using the Debugger's "*d video*" command.
For comparison purposes, I've pasted the corresponding video state for mode 0x10 on the right-hand side.
breakpoint hit: C000:12EA (exec)
stopped (175707689 ops, 800060264 cycles, 133470 ms, 5994308 hz)
AX=020F BX=0050 CX=00C0 DX=1950 SP=0B58 BP=0511 SI=0280 DI=0000
SS=011F DS=A000 ES=A000 PS=0246 V0 D0 I1 T0 S0 Z1 A0 P1 C0
C000:12EA 51 PUSH CX
BIOSMODE: 0x0E BIOSMODE: 0x10
CRTC[0x00]: HORZ_TOTAL 0x70 CRTC[0x00]: HORZ_TOTAL 0x5B
CRTC[0x01]: HORZ_DISP_END 0x4F CRTC[0x01]: HORZ_DISP_END 0x4F
CRTC[0x02]: HORZ_BLANK_START 0x59 CRTC[0x02]: HORZ_BLANK_START 0x53
CRTC[0x03]: HORZ_BLANK_END 0x2D CRTC[0x03]: HORZ_BLANK_END 0x37
CRTC[0x04]: HORZ_RETRACE_START 0x5E CRTC[0x04]: HORZ_RETRACE_START 0x52
CRTC[0x05]: HORZ_RETRACE_END 0x06 CRTC[0x05]: HORZ_RETRACE_END 0x00
CRTC[0x06]: VERT_TOTAL 0x04 CRTC[0x06]: VERT_TOTAL 0x6C
CRTC[0x07]: OVERFLOW 0x11 CRTC[0x07]: OVERFLOW 0x1F
CRTC[0x08]: PRESET_ROW_SCAN 0x00 CRTC[0x08]: PRESET_ROW_SCAN 0x00
CRTC[0x09]: MAX_SCAN_LINE 0x00 CRTC[0x09]: MAX_SCAN_LINE 0x00
CRTC[0x0A]: CURSOR_START 0x00 CRTC[0x0A]: CURSOR_START 0x00
CRTC[0x0B]: CURSOR_END 0x01 CRTC[0x0B]: CURSOR_END 0x01
CRTC[0x0C]: START_ADDR_HI 0x00 CRTC[0x0C]: START_ADDR_HI 0x00
CRTC[0x0D]: START_ADDR_LO 0x00 CRTC[0x0D]: START_ADDR_LO 0x00
CRTC[0x0E]: CURSOR_ADDR_HI 0x07 CRTC[0x0E]: CURSOR_ADDR_HI 0x01
CRTC[0x0F]: CURSOR_ADDR_LO 0x80* CRTC[0x0F]: CURSOR_ADDR_LO 0x41*
CRTC[0x10]: VERT_RETRACE_START 0xE0 CRTC[0x10]: VERT_RETRACE_START 0x5E
CRTC[0x11]: VERT_RETRACE_END 0x23 CRTC[0x11]: VERT_RETRACE_END 0x2B
CRTC[0x12]: VERT_DISP_END 0xC7 CRTC[0x12]: VERT_DISP_END 0x5D
CRTC[0x13]: OFFSET 0x28 CRTC[0x13]: OFFSET 0x28
CRTC[0x14]: UNDERLINE 0x00 CRTC[0x14]: UNDERLINE 0x0F
CRTC[0x15]: VERT_BLANK_START 0xDF CRTC[0x15]: VERT_BLANK_START 0x5F
CRTC[0x16]: VERT_BLANK_END 0xEF CRTC[0x16]: VERT_BLANK_END 0x0A
CRTC[0x17]: MODE_CTRL 0xE3 CRTC[0x17]: MODE_CTRL 0xE3
CRTC[0x18]: LINE_COMPARE 0xFF CRTC[0x18]: LINE_COMPARE 0xFF
STATUS1: 0x01 STATUS1: 0x01
ATCDATA: true ATCDATA: true
ATC[0x00]: PAL00 0x00 ATC[0x00]: PAL00 0x00
ATC[0x01]: PAL01 0x01 ATC[0x01]: PAL01 0x01
ATC[0x02]: PAL02 0x02 ATC[0x02]: PAL02 0x02
ATC[0x03]: PAL03 0x03 ATC[0x03]: PAL03 0x03
ATC[0x04]: PAL04 0x04 ATC[0x04]: PAL04 0x04
ATC[0x05]: PAL05 0x05 ATC[0x05]: PAL05 0x05
ATC[0x06]: PAL06 0x06 ATC[0x06]: PAL06 0x14
ATC[0x07]: PAL07 0x07 ATC[0x07]: PAL07 0x07
ATC[0x08]: PAL08 0x10 ATC[0x08]: PAL08 0x38
ATC[0x09]: PAL09 0x11 ATC[0x09]: PAL09 0x39
ATC[0x0A]: PAL0A 0x12 ATC[0x0A]: PAL0A 0x3A
ATC[0x0B]: PAL0B 0x13 ATC[0x0B]: PAL0B 0x3B
ATC[0x0C]: PAL0C 0x14 ATC[0x0C]: PAL0C 0x3C
ATC[0x0D]: PAL0D 0x15 ATC[0x0D]: PAL0D 0x3D
ATC[0x0E]: PAL0E 0x16 ATC[0x0E]: PAL0E 0x3E
ATC[0x0F]: PAL0F 0x17 ATC[0x0F]: PAL0F 0x3F
ATC[0x10]: MODE 0x01 ATC[0x10]: MODE 0x01
ATC[0x11]: OVERSCAN 0x00 ATC[0x11]: OVERSCAN 0x00
ATC[0x12]: PLANES 0x0F ATC[0x12]: PLANES 0x0F
ATC[0x13]: HORZPAN 0x00 ATC[0x13]: HORZPAN 0x00
GRC[0x00]: SRESET 0x00 GRC[0x00]: SRESET 0x00
GRC[0x01]: ESRESET 0x00 GRC[0x01]: ESRESET 0x00
GRC[0x02]: COLORCMP 0x00 GRC[0x02]: COLORCMP 0x00
GRC[0x03]: DATAROT 0x00 GRC[0x03]: DATAROT 0x00*
GRC[0x04]: READMAP 0x00 GRC[0x04]: READMAP 0x00
GRC[0x05]: MODE 0x11* GRC[0x05]: MODE 0x00
GRC[0x06]: MISC 0x05 GRC[0x06]: MISC 0x05
GRC[0x07]: COLORDC 0x0F GRC[0x07]: COLORDC 0x0F
GRC[0x08]: BITMASK 0xFF GRC[0x08]: BITMASK 0xFF
SEQ[0x00]: RESET 0x03 SEQ[0x00]: RESET 0x03
SEQ[0x01]: CLOCKING 0x01 SEQ[0x01]: CLOCKING 0x01
SEQ[0x02]: MAPMASK 0x0F* SEQ[0x02]: MAPMASK 0x0F*
SEQ[0x03]: CHARMAP 0x00 SEQ[0x03]: CHARMAP 0x00
SEQ[0x04]: MEMMODE 0x06 SEQ[0x04]: MEMMODE 0x06
FEAT: 0x02 FEAT: 0x02
MISC: 0x23 MISC: 0xA7
STATUS0: 0x10 STATUS0: 0x10
LATCHES: 0x00000000 LATCHES: 0x00000000
ACCESS: 0x1411 ACCESS: 0x0400
One of the apparent oddities is that, for mode 0x0E, the GRC Mode Register was programmed with 0x11, whereas
for mode 0x10, it was programmed with 0x00. Why would mode 0x0E want to set the ODDEVEN bit during the scroll,
when it hadn't been set during any other writes to the screen?
At this point, I dumped the instruction history buffer a bit ("*dh 100*"), and noticed this GRC write:
C000:1582 8BC5 MOV AX,BP ;history=33
C000:1584 B603 MOV DH,03 ;history=32
C000:1586 B2CE MOV DL,CE ;history=31
C000:1588 E88AF7 CALL 0D15 ;history=30
C000:0D15 86C4 XCHG AL,AH ;history=29
C000:0D17 EE OUT DX,AL ;history=28
C000:0D18 42 INC DX ;history=27
C000:0D19 86C4 XCHG AL,AH ;history=26
C000:0D1B EE OUT DX,AL ;history=25
C000:0D1C 4A DEC DX ;history=24
So I looked farther back and saw where BP was set:
C000:1522 BA00A0 MOV DX,A000 ;history=97
C000:1525 BD1105 MOV BP,0511 ;history=96
Here's the complete function:
GR_ST_1:
MOV DX,A000
MOV BP,0511
CMP AH,0F
JC 1535
CALL 14F7
JNC 1535
MOV BP,0501
RET
OK, so any (EGA) graphics mode below 0x0F is going to the trigger the use of Write Mode 1 with the ODDEVEN bit set.
And sure enough, the scrolling bug also occurs when using 320x200 16-color mode 0x0D.
It's also worth noting that, whenever the ODDEVEN bit of the GRC Mode Register is set, the SEQUENTIAL bit in the Sequencer
Memory Mode Register is supposed to be clear (and vice versa -- those two bits are supposed to always be oppositely set).
But here, the IBM EGA BIOS hasn't done that. One wonders if that was a mistake....
Another bit of trivia: while dumping the frame buffer in a VGA text mode in a different emulator, I discovered that
when I turned off the ODDEVEN bit in the GRC Mode Register, odd bytes would still appear from plane 1; it wasn't until I
*also* turned off the CHAIN bit in the GRC Miscellaneous Register that the odd bytes would no longer appear. But,
that could have just been an idiosyncrasy of that particular emulator.
As a result of all these observations, and more importantly, to make EGA scrolling work properly in modes 0x0D and 0x0E,
I've changed the Video component to use odd/even memory functions *only* when the SEQUENTIAL bit (bit 2) of the
Sequencer's Memory Mode Register is clear, instead of relying on the ODDEVEN bit (bit 4) of the Graphics Controller's Mode
Register.
To be continued.... because I've barely scratched the surface of all the side-effects of EGA/VGA odd/even addressing,
and I hope to do some testing on real hardware in the near future.
*[@jeffpar](http://twitter.com/jeffpar)*
*June 5, 2015*

View file

@ -1,204 +0,0 @@
---
layout: post
title: Windows 95
date: 2015-07-17 11:00:00
category: Windows 95
permalink: /blog/2015/07/17/
machines:
- type: pcx86
id: deskpro386
debugger: true
state: /disks/pcx86/windows/win95/4.00.950/deskpro386.json
config: /devices/pcx86/machine/compaq/deskpro386/vga/4096kb/debugger/machine.xml
drives: '[{name:"68Mb Hard Disk",type:4,path:"http://archive.pcjs.org/disks/pcx86/fixed/68mb/win95.json"}]'
autoMount: ''
---
This week (July 14, 2015) was the 20th anniversary of Windows 95 RTM ("Release To Manufacturing"). So I decided to
throw a PCjs party and try running Windows 95 Setup inside a PCx86 machine for the first time.
Sadly, it immediately failed:
Please wait while Setup initializes.
Windows requires a computer with an 80386 processor or higher.
The failing code:
0E36:08FD 06 PUSH ES
0E36:08FE 1E PUSH DS
0E36:08FF 9C PUSHF
0E36:0900 33C0 XOR AX,AX
0E36:0902 50 PUSH AX
0E36:0903 9D POPF
0E36:0904 9C PUSHF
0E36:0905 58 POP AX
0E36:0906 A90080 TEST AX,8000
0E36:0909 7517 JNZ 0922
0E36:090B B80070 MOV AX,7000
0E36:090E 50 PUSH AX
0E36:090F 9D POPF
0E36:0910 FB STI
0E36:0911 9C PUSHF
0E36:0912 58 POP AX
0E36:0913 A90070 TEST AX,7000
0E36:0916 7405 JZ 091D
0E36:0918 B88603 MOV AX,0386
0E36:091B EB08 JMP 0925
0E36:091D B88602 MOV AX,0286
0E36:0920 EB03 JMP 0925
0E36:0922 B88600 MOV AX,0086
0E36:0925 9D POPF
0E36:0926 1F POP DS
0E36:0927 07 POP ES
0E36:0928 C3 RET
was easily fixed with a change to [x86cpu.js](/modules/pcx86/lib/x86cpu.js), allowing the IOPL bits to be
modified in real-mode on an 80386. When I had previously tweaked setPS() to accomodate 80286/80386 discrimination
logic in OS/2 1.0, there was no 80386 support in PCx86 at that time, so it was sufficient to *never* allow the
IOPL bits to be altered in real-mode.
The next problem was triggered by Setup's CAB ("Diamond") decompression code, which uses all 32 bits
of the 80386's 32-bit registers. That in itself was not a problem, but by leaving stray bits in the upper halves of
registers like EDX, it exposed a bug in the PCx86 I/O instruction handlers, which neglected to mask EDX with 0xFFFF before
performing port lookups, causing mysterious I/O failures that usually manifested themselves as hard disk I/O errors.
Then PCx86 crashed in the middle of the decompression of the first CAB file (MINI.CAB). It turned out the stack
had been improperly adjusted because a "RETF n" instruction mistakenly believed that a stack switch had occurred.
This was, in fact, a left-over condition from a protected-mode stack-switch. That was easily cleared.
Next, I discovered that I had never finished updating a handful of instructions to full 32-bit operation;
namely, INC, DEC, NEG, NOT, TEST, MOVSB and MOVSW. Those are done now.
Then I ran into a couple of Windows 95 oddities. First, some kernel initialization code deliberately
executed an invalid opcode (0x0F,0xFF) and expected its DPMI exception (0x06) handler to field the exception; that was
fixed by flagging the opcode as genuinely invalid.
> SIDEBAR
> By default, PCx86 marks opcodes as invalid *only* if they have been confirmed invalid. PCx86 considers the vast
majority of unused/undocumented opcodes to be merely "undefined" until I've seen them in the wild. An instruction will
be marked invalid if I either discover that real hardware treats it as an Invalid Opcode (by throwing the exception)
*or* that real software expects it to. For opcode 0x0F,0xFF, it was the latter.
> Don't be confused that Intel also refers to the Invalid Opcode exception (0x06) as the #UD ("Undefined")
opcode exception. Every opcode that triggers that exception is, by definition, defined: it's defined as invalid.
I consider it a misnomer to refer to any invalid instruction as "undefined".
The other recent Windows 95 oddity I ran into was an instruction with multiple address-override (0x67) prefixes; the
first prefix changed the instruction's addressing mode from 16-bit to 32-bit, and the second prefix changed it back to
16-bit. PCx86 should have simply ignored the second prefix.
With all of the above changes in place, PCx86 v1.18.4 is able to run Windows 95 Setup a bit farther, but still far from
completion. If you want to give it a spin yourself, start the machine below (click the "Run" button) and once it has
finished booting, run SETUP from drive B, where the first Windows 95 diskette is already loaded.
If, when it crashes (and it will), you're interested in examining the instructions that were executed prior to the
crash, you can dump the instruction history buffer using the Debugger's "dh" command.
NOTE: The diskette images contain a pre-release version of Windows 95, as I don't currently have the RTM version on
diskette.
---
August 13, 2015 Update
---
PCx86 v1.18.8 has made a little more progress running Windows 95 Setup, but CAB decompression still fails almost
immediately. To monitor DOS calls until the first 36-byte read of PRECOPY1.CAB, try setting the following
breakpoint and then starting the machine, using the PCx86 Debugger *input* field next to the **Enter** button:
m dos off
bp 1ED4:16B4 "set fn=ah;dos;if fn!=3f||cx!=24"
g
Alternatively, you can hard-code those commands into the Debugger component of the machine.xml file; eg:
```xml
<debugger id="debugger" messages="fault|tss|int" commands='m dos off;bp 1ED4:16B4 "set fn=ah;dos;if fn!=3f||cx!=24"'/>
```
Once the 36-byte read is hit, you'll probably want to stop on the next instruction that examine those bytes,
by using a memory read breakpoint:
br ds:dx
and you also might want to change the first breakpoint to stop on *any* file read, and add a second breakpoint
to dump the initial contents of those reads:
bp 1ED4:16B4 "set fn=ah;dos;if fn!=3f"
bp FDC8:422A "if fn==3f;di ds:dx;db ds:dx;h;else"
In the current machine, `1ED4:16B4` is the DOS INT 0x21 entry point and `FDC8:422A` is the corresponding IRET.
The first breakpoint sets an internal variable, `fn`, to the value of the **AH** register on entry, so that the
second breakpoint can check the value on exit.
Regarding the other Debugger commands shown above, the `dos` command describes the current DOS operation
(alternatively, you could use the `m int on; m dos on` commands to turn on DOS interrupt messages). The `di`
command dumps PCx86 BACKTRACK(tm) information, to help you visually confirm which INT 0x21 calls are reading
PRECOPY1.CAB. Finally, the `if` command evaluates the given expression, and if the result is non-zero ("true"),
all subsequent commands are executed, up to to any `else` command; otherwise, only commands after the `else`
command will be executed, and if there is no `else` command, execution will stop.
Debugger expressions may contain the usual variety of arithmetic, bitwise and logical binary operators, and they
are evaluated using traditional operator precedence (ie, the same as C or JavaScript); any other operators, such as
parentheses, assignment operators, and unary or ternary operators, are not supported in expressions.
This test machine below has been updated to load WDEB386.EXE prior to starting B:SETUP.EXE, if you prefer using
WDEB386. Make sure the machine is running (ie, click the **Run** button, or use the PCx86 Debugger "g" command),
and then click on the Debugger *output* control to give it focus and press CTRL-C to trigger WDEB386.
The Debugger *input* field is used exclusively for PCx86 Debugger commands, whereas the *output* textarea combines
all Debugger output *and* WDEB386 COM2 serial port I/O. You can even use the PCx86 Debugger to debug
the WDEB386 debugger; just make sure the appropriate text control has focus before typing a command.
To help reduce confusion, the PCx86 Debugger displays a double-character command prefix, to differentiate its commands
from WDEB386's single-character command prompt -- but it's still easy to get confused.
A quick recap of those command prefixes (which you won't see until AFTER you've typed a PCx86 command):
* `>>` indicates real-mode
* `##` indicates protected-mode
* `--` indicates V86-mode
80386 Debug register (DR0-DR7) support was recently added, so even WDEB386 read/write breakpoints should work now.
---
August 21, 2015 Update
---
PCx86 v1.19.1 has finally solved a number of nagging bugs. The CAB decompression code itself was running
fine; it would crash after loading a 32-bit value into EAX *and* a timer interrupt occurred. A path
through the interrupt handler was trashing the upper bits of EAX. The culprit: any of the MOV instructions
that move an immediate value into one of the "high" 8-bit registers (AH, BH, CH, or DH). Those instructions
were failing to preserve the upper 16 bits of the entire register. Further proof that the most exasperating
bugs sometimes have the most mundane causes.
Also recently fixed: the annoying VGA video glitch that would occur whenever the mouse was moved, improper
updates to the ACCESSED bit in a selector's descriptor table entry, improper error codes when a selector load
generated a fault, and the RETF instruction's failure to properly restart when the return address referred
to a not-present segment.
The next problem appears to be timer-related. When Windows 95 Setup begins its hardware analysis, it
gets "stuck" in code that's reading and writing timer ports (0x40 and 0x43). Turning on timer port messages
with the `"m port on;m timer on"` Debugger commands reveals that the problem maybe an unsupported timer
command:
chipset.outPort(0x0043,PIT1_CTRL,0xD2) @1847:DD02
PIT1_CTRL: Read-Back command not supported (yet)
---
September 12, 2015 Update
---
Lots of bugs have been squashed in the past few weeks -- not enough to finish setting up Windows 95, but it's getting
closer. More details on recent releases can be found [here](https://github.com/jeffpar/pcjs/releases).
The machine below has been reconfigured with a hard disk image containing all the Windows 95 Setup files. The machine
is at the point where Windows 95 SETUP has just rebooted.
{% include machine.html id="deskpro386" %}
*[@jeffpar](http://twitter.com/jeffpar)*
*July 17, 2015 (updated September 12, 2015)*

View file

@ -1,39 +0,0 @@
---
layout: post
title: Windows 95 In Your Web Browser
date: 2015-09-21 11:00:00
category: Windows 95
permalink: /blog/2015/09/21/
machines:
- type: pcx86
id: deskpro386
state: /disks/pcx86/windows/win95/4.00.950/deskpro386.json
config: /devices/pcx86/machine/compaq/deskpro386/vga/4096kb/machine.xml
drives: '[{name:"68Mb Hard Disk",type:4,path:"http://archive.pcjs.org/disks/pcx86/fixed/68mb/win95.json"}]'
autoMount: ''
---
Today, the last serious bug preventing a successful boot of Windows 95 was fixed. I won't bore you with
the details.
OK, I will: three arithmetic instructions (specifically, **AND**, **OR** and **XOR**) include a variation that
converts an immediate signed byte into a signed word. Those variations were failing to truncate the result when
a 16-bit operand size was in effect, and if the destination was a register, the upper 16 bits of that register
could become corrupted.
The [Windows 95 Test Machine](/disks/pcx86/windows/win95/4.00.950/) hard disk has been updated
with a complete set of Windows 95 files from a "Compact" installation, and first boot has finished, so instead
of the initial "Getting ready to run Windows 95 for the first time..." splash screen, you'll see the normal
Windows 95 startup screen.
The machine is still a bit finicky. It easily gets confused about the state of its shift keys if you switch away
from the browser and then back again. And Explorer windows don't open in the correct view; for example, both
**My Computer** and **Recycle Bin** open the same (incorrect) view. In short, there are still some serious bugs
to be resolved, but booting has been achieved.
The adventure continues.
{% include machine.html id="deskpro386" %}
*[@jeffpar](http://twitter.com/jeffpar)*
*September 21, 2015*

View file

@ -1,218 +0,0 @@
---
layout: post
title: Windows 95 and Early 80386 CPUs
date: 2015-10-27 11:00:00
categories: ['Windows 95', '80386']
permalink: /blog/2015/10/27/
---
Every time Windows 95 starts up, its real-mode loader performs the following CPU identification test.
&0654:121E 9C PUSHF
&0654:121F 33C0 XOR AX,AX ; try to clear bit 15 of flags
&0654:1221 50 PUSH AX
&0654:1222 9D POPF
&0654:1223 9C PUSHF
&0654:1224 58 POP AX
&0654:1225 A90080 TEST AX,8000 ; bit 15 of flags set anyway?
&0654:1228 7543 JNZ 126D ; yes (must be an 8086/8086)
&0654:122A B80070 MOV AX,7000 ; try to set bits 12-14 of flags
&0654:122D 50 PUSH AX
&0654:122E 9D POPF
&0654:122F FB STI
&0654:1230 9C PUSHF
&0654:1231 58 POP AX
&0654:1232 9D POPF
&0654:1233 A90070 TEST AX,7000 ; any of bits 12-14 of flags set?
&0654:1236 7436 JZ 126E ; no (must be an 80286)
&0654:1238 51 PUSH CX
&0654:1239 33C9 XOR CX,CX
&0654:123B 8BC4 MOV AX,SP
&0654:123D 83E003 AND AX,0003
&0654:1240 7404 JZ 1246
&0654:1242 8BC8 MOV CX,AX
&0654:1244 2BE0 SUB SP,AX
&0654:1246 669C PUSHFD
&0654:1248 666800000400 PUSH 00040000 ; try to set the AC bit of flags
&0654:124E 669D POPFD
&0654:1250 669C PUSHFD
&0654:1252 6658 POP EAX
&0654:1254 669D POPFD
&0654:1256 66A900000400 TEST EAX,00040000 ; AC bit set?
&0654:125C 7508 JNZ 1266 ; yes (must be an 80486, or newer)
&0654:125E 40 INC AX
&0654:125F 03E1 ADD SP,CX
&0654:1261 59 POP CX
&0654:1262 0BC0 OR AX,AX
&0654:1264 F9 STC ; return ZF clear and CF set to indicate 80386
&0654:1265 C3 RET
&0654:1266 03E1 ADD SP,CX
&0654:1268 59 POP CX
&0654:1269 660BC0 OR EAX,EAX ; return ZF clear and CF clear to indicate 80486
&0654:126C C3 RET
&0654:126D 9D POPF
&0654:126E 33C0 XOR AX,AX ; return ZF set to indicate 80286 or older
&0654:1270 C3 RET
If the above function returns ZF set, then the processor is an 80286 or older, so Windows 95 displays the
following message and exits:
You need an 80386 processor to run Windows.
If the above function returns CF set, then the processor is an 80386, and if CF is clear, the processor is an
80486 or newer.
When CF is set, Windows 95 proceeds to the following 80386 stepping check, which attempts to execute an
XBTS (Extract Bit String) instruction -- an instruction that existed only on B0 and earlier 80386 steppings.
&0654:1299 53 PUSH BX
&0654:129A 51 PUSH CX
&0654:129B 52 PUSH DX
&0654:129C B80635 MOV AX,3506 ; save the current "invalid opcode" handler (IVT entry #6)
&0654:129F CD21 INT 21
&0654:12A1 8CC0 MOV AX,ES
&0654:12A3 66C1E010 SHL EAX,10
&0654:12A7 8BC3 MOV AX,BX
&0654:12A9 6650 PUSH EAX
&0654:12AB 1E PUSH DS
&0654:12AC BAE012 MOV DX,12E0
&0654:12AF 8CCB MOV BX,CS
&0654:12B1 8EDB MOV DS,BX
&0654:12B3 B80625 MOV AX,2506 ; temporarily install a new "invalid opcode" exception handler
&0654:12B6 CD21 INT 21
&0654:12B8 1F POP DS
&0654:12B9 33C0 XOR AX,AX
&0654:12BB 8BD0 MOV DX,AX
&0654:12BD B900FF MOV CX,FF00
&0654:12C0 0FA6CA XBTS CX,DX,AX,CL ; attempt to execute an XBTS instruction
&0654:12C3 6658 POP EAX
&0654:12C5 8BD0 MOV DX,AX
&0654:12C7 66C1E810 SHR EAX,10
&0654:12CB 1E PUSH DS
&0654:12CC 8ED8 MOV DS,AX
&0654:12CE B80625 MOV AX,2506 ; restore the original "invalid opcode" exception handler
&0654:12D1 CD21 INT 21
&0654:12D3 1F POP DS
&0654:12D4 B8B000 MOV AX,00B0
&0654:12D7 0BC9 OR CX,CX ; did XBTS work (ie, did CX change)?
&0654:12D9 7401 JZ 12DC ; yes, so we must have a B0 stepping or earlier
&0654:12DB 40 INC AX ; no, so bump the stepping to B1
&0654:12DC 5A POP DX
&0654:12DD 59 POP CX
&0654:12DE 5B POP BX
&0654:12DF C3 RET ; returns AX == 0xB0 if B0 stepping or earlier, 0xB1 if B1 stepping or later
&0654:12E0 55 PUSH BP ; temporary "invalid opcode" handler
&0654:12E1 8BEC MOV BP,SP
&0654:12E3 83460203 ADD [BP+02],0003 ; advance IP past the 3-byte XBTS instruction
&0654:12E7 5D POP BP
&0654:12E8 CF IRET
If the above function returns 0xB0, then the 80386 is a B0 or earlier stepping, so Windows 95 displays the
following message and aborts:
Windows may not run correctly with the 80386 processor that is installed in this computer.
Upgrade your 80386 processor.
Otherwise, the 80386 is a B1 or later stepping, so Windows 95 next performs a multiplication test (a simplified
version of the multiplication tests discussed in "[Early 80386 CPUs](/blog/2015/02/23/)"):
&0654:12E9 33C9 XOR CX,CX
&0654:12EB 66BB81000000 MOV EBX,00000081
&0654:12F1 66B800A01704 MOV EAX,0417A000
&0654:12F7 66F7E3 MUL EBX
&0654:12FA 6683FA02 CMP EDX,00000002
&0654:12FE 750B JNZ 130B
&0654:1300 663D00A0E70F CMP EAX,0FE7A000
&0654:1306 7503 JNZ 130B
&0654:1308 E2E1 LOOP 12EB
&0654:130A C3 RET
If any of the 65,536 identical multiplications return an incorrect result, Windows 95 displays the following
message:
WARNING: The 80386 processor in this computer may not reliably execute 32-bit
multiplication. Windows may occasionally fail on this computer.
You may want to replace your 80386 processor.
Press any key to continue...Press a key to continue
A multiplication failure implies that the 80386 stepping is B1, because later steppings resolved the problem.
You may have heard that Windows 95 [pulled support for the 80386 B1 stepping](http://blogs.msdn.com/b/oldnewthing/archive/2011/01/12/10114521.aspx),
and that's true, but only insofar as Windows 95 SETUP is concerned. The following code is executed by WINSETUP.BIN,
a 16-bit Windows component that manages the Windows 95 installation process:
#05C7:69E8 1E PUSH DS
#05C7:69E9 07 POP ES
#05C7:69EA 6657 PUSH EDI
#05C7:69EC FD STD
#05C7:69ED 66BF00000000 MOV EDI,00000000
#05C7:69F3 678A07 MOV AL,[EDI]
#05C7:69F6 67AA STOSB
#05C7:69F8 33C0 XOR AX,AX
#05C7:69FA 6681FFFFFF0000 CMP EDI,0000FFFF
#05C7:6A01 7501 JNZ 6A04
#05C7:6A03 40 INC AX
#05C7:6A04 FC CLD
#05C7:6A05 665F POP EDI
#05C7:6A07 C3 RET
Ths above code checks for B1 stepping [Errata #7](/pubs/pc/reference/intel/80386/#b1-errata): "Wrong Register Size for
String Instructions in Mixed 16/32-bit Addressing Systems." It returns AX == 0 if the STOSB instruction updated EDI
correctly (0xFFFFFFFF) or AX == 1 if EDI is incorrect (0x0000FFFF).
If Errata #7 is detected, then Windows 95 SETUP displays the following message and aborts:
Setup Error B1: Setup has detected an 80386 processor that is not compatible with this version of Windows.
Before you can run this version of Windows, you need to upgrade your processor.
Contact your computer manufacturer for more information.
However, if you can get through SETUP, Windows 95 will still run on a B1 stepping. For example, if you installed
Windows 95 using a newer 80386, and then later "downgraded" the CPU to a B1, Windows 95 would still run. If your B1
suffered from the multiplication flaw, you would see the 32-bit multiplication warning on start-up, but you could
still continue to run, and if there was no multiplication problem, you would not see any message at all.
---
PCjs v1.20.0 now supports a "stepping" attribute on the &lt;cpu&gt; element, which you can use to simulate specific
stepping behavior. For example, a *machine.xml* file with the following CPU definition:
```xml
<cpu id="cpu386" model="80386" stepping="b0"/>
```
will cause Windows 95 to abort exactly as described as above. Similarly, selecting a 80386 B1 stepping:
```xml
<cpu id="cpu386" model="80386" stepping="b1"/>
```
will cause Windows 95 to display the 32-bit multiplication warning shown above (PCjs deliberately fails the exact
multiplication test that Windows 95 performs).
If you want to simulate a B1 stepping that does *not* have the 32-bit multiplication flaw, set the stepping to B2:
```xml
<cpu id="cpu386" model="80386" stepping="b2"/>
```
B2 was not an actual 80386 stepping; it is a *pseudo-stepping* that provides a simple way of specifying a B1 80386 that
passes all 32-bit multiplication tests.
As previously discussed, Windows 95 SETUP will refuse to install on any "A" or "B" stepping, but if it's already been
installed, it *will* start up on a B1 stepping.
PCjs stepping support is extremely limited at this point. Here's a summary:
1. 80386 steppings A0-B0 provide *limited* support for the short-lived XBTS and IBTS instructions
2. 80386 steppings A0-B1 enable [Errata #7](/pubs/pc/reference/intel/80386/#b1-errata) for STOSB (as tested by Windows 95; see above)
3. 80386 stepping B1 enables 32-bit multiplication errors (as tested by Windows 95; see above)
4. 80386 stepping B2 includes all supported B1 errata, but without 32-bit multiplication errors
In addition, on 80386 reset, we set the CPU revision number in DX to the appropriate value for the specified stepping.
Support for additional 80286 and 80386 errata may be added over time, as interesting scenarios or test cases are discovered.
*[@jeffpar](http://twitter.com/jeffpar)*
*October 27, 2015*

View file

@ -1,124 +0,0 @@
---
layout: post
title: Rebuilding the PCjs Website
date: 2015-12-10 11:03:00
category: Website
permalink: /blog/2015/12/10/
---
It's been nice using Node.js to power the PCjs website, using Amazon's Elastic Beanstalk service, but that combination
has also been a source of some frustrations.
+ When someone posts an article or a tweet linking to a PCjs page, the website bogs down, and while Amazon's Elastic
Beanstalk service makes it easy to automatically scale up, each new instance automatically multiplies my expenses as well,
which hover around $34/month for a single instance. With assorted S3 and transfer charges, my average monthly bill is
over $55/month. That's a bit much for a site that generates zero revenue.
+ Once or twice a year, when I'm attempting to either update the server or upgrade my Node configuration, the update
or upgrade will fail, and Amazon's web console provides virtually no details about why it failed. I get an error message
like "**ERROR: Failed to deploy application**" and that is it. Literally.
Granted, there are some "simple" things I could do to improve performance, like adding an **nginx** proxy server to
the configuration, but that feels like a band-aid solution, and as a software developer, the time spent fiddling with
web server issues is time I'd much rather spend writing code.
Since PCjs is designed to do all its work in the user's web browser, and since the website can be completely built out
as a set of static web pages, I've decided to stop using Node to power www.pcjs.org. I'm in the process of migrating
the website to [GitHub Pages](https://pages.github.com/), and using [Jekyll](https://help.github.com/articles/using-jekyll-with-pages/)
to convert all my existing Markdown files to HTML.
This new approach is *very* similar to what the PCjs custom Node modules did: every time someone visited a folder
on the website that did not yet contain an "index.html", the PCjs Node server would create one, either by converting the
README.md file in that folder to HTML or generating a default HTML document. The PCjs Markdown-to-HTML converter also
contained some special logic that made it easy to embed PCjs machines on a page.
Thanks to GitHub Pages, all of that now happens ahead of time: whenever I update the PCjs "gh-pages" branch on GitHub,
Jekyll automatically runs through the entire site and rebuilds a complete set of web pages.
The Node web server would automatically embed PCjs machines on web pages by looking for special Markdown links, such as;
[Embedded IBM PC](machine.xml "PCjs:ibm5150")
After migrating to GitHub Pages and Jekyll, that markup must now be written as:
{% raw %}
{% include machine.html id="ibm5150" %}
{% endraw %}
and the following "Front Matter" (YAML) must appear at the top of the Markdown file:
---
...
machines:
- type: pcx86
id: ibm5150
---
Basically, the YAML at the top of the file lists all the machines that the page intends to use, and then
{% raw %}`{% include ... %}`{% endraw %} is inserted in the text at the point where a machine should be embedded.
The `machine.html` include accepts only one parameter: the `id` of a machine listed at the top of the file.
The `machines` element at the top of the file must specify a `type` and `id` at a minimum. `type` should be one of
the following values, depending on whether you want an IBM PC or Challenger 1P:
- pc
- c1p
and `id` can be any identifier you want to use to embed the machine. If you want to include the machine's built-in
debugger, set `debugger` to *true*; the older method of specifying *pc-dbg* or *c1p-dbg* instead of *pc* or *c1p* as the
`type` still works, too.
You may also use `config` to specify a machine XML configuration file if not using the default *machine.xml*;
`template` to specify an alternate XSL template file if not using the default *components.xsl*; `state` to specify
a JSON-encoded machine state file if the machine requires a predefined state; and `uncompiled` may be set to *true*
to force a machine to use uncompiled sources, overriding the value of `site.pcjs.compiled` in **_config.yml**.
For example, the PCjs home page contains two machines, so this appears at the top of the
[Markdown file](https://raw.githubusercontent.com/jeffpar/pcjs/master/index.md):
machines:
- type: pcx86
id: ibm5150
config: /devices/pcx86/machine/5150/mda/64kb/machine.xml
- type: c1p
id: demoC1P
config: /devices/c1p/machine/8kb/large/machine.xml
If necessary, you can also override some of the settings in a machine XML file. Here's an example of overriding the
FDC `autoMount` setting, making it easy to reuse the same machine XML file with different boot disks:
machines:
- type: pcx86
id: deskpro386
debugger: true
autoMount:
A:
path: /disks/pcx86/os2/misc/football/FOOTBALL-76817.json
config: /devices/pcx86/machine/compaq/deskpro386/ega/4096kb/debugger/machine.xml
Other settings that can currently be overridden include:
+ `autoPower`
+ `drives`
+ `messages`
+ `state`
Additional overrides will be added as needed. See the [Windows 95 Demo](/disks/pcx86/windows/win95/4.00.950/)
machine and its associated [Markdown file](https://raw.githubusercontent.com/jeffpar/pcjs/master/disks/pcx86/windows/win95/4.00.950/README.md)
for more override examples, including how to set `autoMount` to *not* mount any diskettes.
I will continue to include a Node web server with the PCjs Project, but it's now intended for development
purposes only (not production servers). I've updated the PCjs MarkOut component to parse any "Front Matter"
at the top of the PCjs Markdown files, and to convert all new Jekyll-style embedded machines and screenshots
to the older Markdown-compatible formats, so pages generated by the Node web server should still function
as before. But there are no guarantees.
WARNING: If you decide to run/alternate between Jekyll's web server (WEBrick) and the built-in Node web
server (server.js), you should run "grunt clean" before starting either one, to remove any old **index.html**
files. Node may inadvertently reuse old "index.html" files, and Jekyll may inadvertently propagate them
to its "_site" folder. It's easy to tell when this happens, because you'll see the wrong color scheme: Node
web server pages were designed to use *dark* colors, whereas Jekyll web server pages currently use *light* colors.
*[@jeffpar](http://twitter.com/jeffpar)*
*December 10, 2015*

View file

@ -1,48 +0,0 @@
---
layout: post
title: Revisiting OS/2
date: 2015-12-27 11:00:00
category: OS/2
permalink: /blog/2015/12/27/
machines:
- type: pcx86
id: ibm5170
debugger: true
config: /devices/pcx86/machine/5170/ega/2048kb/rev3/debugger/machine.xml
drives: '[{name:"20Mb Hard Disk",type:2,path:"/disks/pcx86/fixed/20mb/IBMOS210-EGA.json"}]'
automount: ''
---
Just for fun (because I have a warped sense of fun), I decided to revisit some of the old OS/2 software I wrote
almost 30 years ago. But first, I needed an OS/2 development environment.
So I started with a clean install of [IBM OS/2 1.0](/disks/pcx86/os2/ibm/1.0/) in the 8Mhz IBM PC AT machine
below, by booting from the "IBM OS/2 1.0 (1.44M Install)" diskette in drive A and reformatting the machine's 20Mb
drive C.
Next, I installed the [MS OS/2 SDK 1.02](/disks/pcx86/tools/microsoft/os2/sdk/1.02/). This SDK was released
in December 1987 along with [Microsoft OS/2 1.0](/disks/pcx86/os2/microsoft/1.0/). I don't have any of the
printed documentation that came with the SDK, such as the *Installation Guide*, but I do have the
[Microsoft® Operating System/2 Programmers Toolkit](/pubs/pc/software/os2/microsoft/ptk10/) documentation
from March 1988, thanks to the [OS/2 Museum](http://www.os2museum.com/wp/os2-history/os2-library/os2-1-x-programming/).
Aside from **Microsoft Macro Assembler 5.00A** (MASM) and **Microsoft C Compiler 5.10 (Beta)** (CL), the SDK
included some other useful tools, such as the **SDK Editor** (SDKED), which was essentially an OS/2 port of
Mark Zbikowski's full-screen editor (Z) that was used internally at Microsoft for many years. It was renamed
to the **Microsoft Editor** (M or MEP) with the release of **Microsoft C Compiler 5.10**, and it was later integrated
into **Programmer's Workbench** (PWB), the text-mode Integrated Development Environment (IDE) that came with
**Microsoft C Compiler 6.0**.
With the introduction of graphical IDEs, such as Visual BASIC in 1991, Visual C++ in 1993, and Visual Studio in 1995,
this stand-alone, text-mode editor became obsolete, but in the 1980s, it was a valuable tool. You can learn more
about [SDKED](/disks/pcx86/tools/microsoft/os2/sdk/1.02/#using-sdked) on the
[MS OS/2 SDK 1.02](/disks/pcx86/tools/microsoft/os2/sdk/1.02/) page.
Our [IBM OS/2 1.0](/disks/pcx86/os2/ibm/1.0/) demo machine (shown below) has the
[MS OS/2 SDK 1.02](/disks/pcx86/tools/microsoft/os2/sdk/1.02/) pre-installed, so check out our copy of the
[Microsoft® Operating System/2 Programmers Toolkit](/pubs/pc/software/os2/microsoft/ptk10/) and then write some code!
{% include machine.html id="ibm5170" %}
*[@jeffpar](http://twitter.com/jeffpar)*
*December 27, 2015*

View file

@ -1,39 +0,0 @@
---
layout: post
title: Early OS/2 Artifacts
date: 2016-01-23 14:00:00
category: OS/2
permalink: /blog/2016/01/23/
---
Before OS/2 was named **OS/2** by IBM on April 2, 1987, the operating system was known by many different names at
Microsoft as it evolved, including **DOS5**, **MT-DOS**, **CP-DOS**, and **ADOS**.
In late 1986, Microsoft began working on a couple different branches. One was called **SIZZLE**, where a variety of
performance improvements were tested before being merged back into the main branch.
Another branch was **FOOTBALL** (aka **PIGSKIN**), an early 80386-based prototype intended to test the viability
of the running multiple DOS applications in V86-mode. Sometimes this 80386 version was also called **386DOS**,
to distinguish it from **286DOS**. More details are in this
[FOOTBALL Design Document](/disks/pcx86/os2/misc/football/87058/#football-design-document).
To shed some light on those efforts, I recently added a few [OS/2 Prototype Disks](/disks/pcx86/os2/misc/): a small
collection of early (mostly pre-1.0) OS/2 boot disks that provide a glimpse of what some of those early OS/2 builds
looked like.
Getting these early versions of OS/2 to run in **PCjs** has been a bit of a challenge. There have been some successes
but also some lingering issues. Debugging continues.
Part of the problem is that these pre-1.0 builds still contain a few bugs. Also, the original
[OS/2 FOOTBALL Boot Disk](/disks/pcx86/os2/misc/football/87058/) from February 1987 was developed and
tested exclusively on Compaq DeskPro 386 machines from late 1986, so it has some uncommon 80386 dependencies:
* The [80386 LOADALL](/pubs/pc/reference/intel/80386/loadall/) instruction
* 32-bit segment register writes must modify only 16 bits of memory
**FOOTBALL** also had some specific video hardware requirements: CGA or EGA. Note that the VGA, which is what most
emulators use by default these days, did not exist in 1986. The VGA was introduced in April 1987, when IBM
unveiled their new PS/2 hardware line -- and announced OS/2.
*[@jeffpar](http://twitter.com/jeffpar)*
*January 23, 2016*

View file

@ -1,57 +0,0 @@
---
layout: post
title: "Super Bowl Winner: PCjs"
date: 2016-02-08 14:00:00
permalink: /blog/2016/02/08/
---
The new release of PCjs (v1.20.8) is a fairly minor update, but it's an important one for **FOOTBALL** fans, resolving
two annoying problems with the [OS/2 FOOTBALL Boot Disk](/disks/pcx86/os2/misc/football/87058/): mysterious hard-error popups
and blank screens.
A hard-error popup would occur when FOOTBALL tried to initialize a non-existent PRN device. To resolve that, PCjs
now provides basic parallel port emulation, in the form of a [ParallelPort](/docs/pcx86/parallel/) component that you
include in a machine XML file with the &lt;parallel&gt; element, in much the same way you include the
[SerialPort](/docs/pcx86/serial/) component with the &lt;serial&gt; element. This [Compaq DeskPro 386]
(/devices/pcx86/machine/compaq/deskpro386/ega/4096kb/debugger/) machine used to run FOOTBALL has now been updated to
include one parallel port.
The other problem was that switching between sessions with the **SysReq** key would often result in a blank screen;
the new session was active, but you couldn't see it. This was a side-effect of how FOOTBALL reprograms the video
controller when switching screens *and* the linear page mappings it creates to access video memory, which in turn
exposed a PCjs memory-management bug. The upshot is that whenever the Video component moves the address of the video
buffer (which is *physical* memory), it must tell the CPU to flush any linear-to-physical mappings that may still refer
to the old physical memory.
With these changes, the [OS/2 FOOTBALL Boot Disk](/disks/pcx86/os2/misc/football/87058/) appears to be quite usable now.
Feel free to give it a few kicks!
---
I've also tidied up a few things in the [Devices](/devices/) folder. ROM images used to be stored under
`/devices/pcx86/basic/` and `/devices/pcx86/bios/`, but BASIC and BIOS ROM images aren't actually devices; they are the
*contents* of ROM devices. So, with that in mind, I've made the following rearrangements:
* `/devices/pcx86/bios/5150/*` => `/devices/pcx86/rom/5150/*`
* `/devices/pcx86/bios/5160/*` => `/devices/pcx86/rom/5160/*`
* `/devices/pcx86/bios/5170/*` => `/devices/pcx86/rom/5170/*`
* `/devices/pcx86/bios/compaq/*` => `/devices/pcx86/rom/compaq/*`
* `/devices/pcx86/basic/ibm-basic-1.00.json` => `/devices/pcx86/rom/5150/basic/BASIC100.json`
* `/devices/pcx86/basic/ibm-basic-1.10.json` => `/devices/pcx86/rom/5160/basic/BASIC110.json`
This structure mirrors what was done with Machine and Video devices, where the devices are organized
first by manufacturer (IBM or COMPAQ) and then by type (MDA, CGA, EGA, etc).
Here, the first ROM subdivision is either a manufacturer or a machine model. A model implies a manufacturer;
for example, models 5150 through 5170 refer to IBM PC models.
---
The project currently includes only two BASIC ROM versions, C1.00 and C1.10, which were initially released with the
first model 5150 and 5160 machines, respectively. For more details, see [IBM PC ROMs](/devices/pcx86/rom/).
I believe there was also a BASIC ROM version 1.20 released for IBM PCjr, but since PCjs does not yet emulate the PCjr,
it has not been added to the project.
*[@jeffpar](http://twitter.com/jeffpar)*
*February 8, 2016*

View file

@ -1,130 +0,0 @@
---
layout: post
title: "Saving Disks and Machines"
date: 2016-02-17 14:00:00
permalink: /blog/2016/02/17/
---
PCx86 (v1.20.9) now offers new, *much* easier ways to save disks and machines, thanks to the new
[Save Disk](/blog/2016/02/17/#saving-disks) and [Save Machine](/blog/2016/02/17/#saving-machines) features.
With one click, PCx86 can now generate a single download containing everything you need to embed any of our
IBM PC demos on your own web page.
Saving Disks
---
Floppy disk images can now be saved to your desktop computer by simply clicking the **Save** button next
to the floppy disk controls. Select the drive first, and then whatever diskette is shown as being "loaded"
in that drive will be saved in your local machine's Downloads folder when you click **Save**.
If you made any changes to that disk after it was loaded, those changes will be included, so if you want a pristine
copy of the disk, click the **Load** button first. PCx86 will ask you to confirm that you really want to reload the
disk and discard any changes.
Note that the disk *should* be downloaded as an **.img** file, which is nothing more than a sector-by-sector binary
dump of the disk. For example, a 360Kb double-sided double-density (DSDD) disk contains 9 512-byte sectors in each
of the 40 tracks on each of its 2 sides, so when you **Save** a 360Kb disk, the downloaded file should be exactly
368,640 bytes large.
The Mac OS X operating system can automatically mount most **.img** files (from disks created by DOS 2.0 or later).
Older DOS 1.x disks, along with most non-DOS disks, do not have a BIOS Parameter Block (BPB) in the boot sector, so
most modern operating systems won't recognize the disk format. Other operating systems, like Windows, may require
third-party software in order to mount an **.img** file, and some third-party software may prefer a different extension,
such as **.ima** or **.bin**.
It's also recommended that you make your **.img** files *read-only*, so that if you do mount them on your desktop
computer, neither you nor the operating system will inadvertently modify the contents of the disk. On OS X, this is
easily done with the **chmod** utility.
For example, if you saved the disk named "PC-DOS 2.00 (Disk 1)", it should have been downloaded as "PCDOS200-DISK1.img"
in your Downloads folder, so the OS X Terminal command `chmod -w PCDOS200-DISK1.img` will make it read-only, and
`chmod +w PCDOS200-DISK1.img` will make it writable again.
**NOTE**: Some browsers, notably Safari, do not support named downloads, so any disks you download will end up
with default names like "Unknown" or "download". PCx86 will still try to let you know what the original filename was,
so that you can rename it appropriately.
Saving Machines
---
Saving the entire state of any existing IBM PC machine is also much simpler now, using the new **Save Machine** link.
You can choose to save a machine in its initial state, or make changes to any of the machine's disks and then save it.
All your changes should be preserved.
Under the bottom-left corner of any IBM PC on the PCjs [website](/), you should now see a
[**Save Machine**] link. When you click that link, PCx86 will generate a large chunk of JavaScript containing
everything that machine needs to run, including:
* The machine XML configuration file (eg, "machine.xml")
* The machine XSL transformation file (eg, "components.xsl")
* The machine CSS stylesheet file (eg, "components.css")
* The machine state file (eg, "state.json")
* The PCx86 machine emulation script (eg, "pcx86.js")
* Copies of all the disk images mounted by the machine
Let's say you want to save the IBM PC on the PCx86 [home page](/). When you click **Save Machine**, two things should
happen:
* A file will be downloaded (eg, "pcx86.js")
* A dialog box will appear with some markup to copy-and-paste
The dialog box should provide the following information:
Check your Downloads folder for "pcx86.js", copy it to your web server as "pcx86.js",
and then add the following to your web page:
<div id="ibm5150"></div>
...
<script type="text/javascript" src="pcx86.js"></script>
<script type="text/javascript">embedPC("ibm5150","machine.xml","components.xsl");</script>
The machine should appear where the <div> is located.
Copy the downloaded file to your own web server as **pcx86.js**, then create or edit a web page and insert the above text.
If **pcx86.js** and your web page are in different folders, then you'll also need to update *src* to include the exact
location of the script.
Some notes:
* PCx86 may attempt to name the downloaded file **pcx86.json** instead of **pcx86.js**, because a file with a ".js"
extension could cause your web browser to block the download.
* For browsers that don't support named downloads, PCx86 will attempt to open a new window/tab instead. Make sure
you copy the *entire* contents of that window into a file named to **pcx86.js** (or **pcx86-dbg.js** if the machine is
using the built-in PCx86 debugger).
* Your browser may also impose size limitations on the download. If nothing happens, the machine data may be too
large for your browser; try a different browser (eg, Firefox or Safari) or a different machine.
* If you have modified any floppy disks mounted by the machine *or* any of the machine's hard disks, those
modifications should be saved along with the machine.
* Any floppy disks mounted during the lifetime of the machine will be added to the machine's state. So, for example,
if you want your copy of the machine to include all the Windows 1.01 SDK disks, make sure you have loaded each disk
once before clicking **Save Machine**.
* Any floppy disks *not* mounted during the lifetime of the machine will be *removed* from the machine's list of
available disks; we don't want machines running on other websites to be consuming our bandwidth.
* The machine's current state, including memory and screen contents, are saved as part of the machine state.
So, even if the original machine always powers on from scratch, the *copied* machine will always resume at the point
it was saved. This behavior, however, can be disabled by passing a *parms* object as the 4th parameter to the
*embedPC()* call, overriding the 'state' property:
```xml
<script type="text/javascript">embedPC("ibm5150","machine.xml","components.xsl","{state:null}");</script>
```
While the [PCx86 Documentation](/docs/pcx86/) explains how to create a *new* machine, by writing your own machine
XML file and manually copying all the other pieces, the new **Save Machine** feature is the best way to save
any *existing* IBM PC and embed it on any other website.
**WARNING**: While this feature is still "hot of the press," there will probably be some kinks to work out. Every
browser seems to have its own idiosyncrasies in terms of what can be downloaded and/or how large it can be. It's
also quite possible that certain machine features or modifications may not be properly preserved.
If you're having trouble with a particular browser or a particular machine, be sure to
[let me know](mailto:Jeff@pcjs.org), and then try another browser and/or machine.
*[@jeffpar](http://twitter.com/jeffpar)*
*February 17, 2016*

View file

@ -1,95 +0,0 @@
---
layout: post
title: Remembering COMPAQ
date: 2016-02-24 14:00:00
permalink: /blog/2016/02/24/
---
IBM is obviously the company everyone thinks of first when we talk about the IBM PC -- after all, IBM is right
there in the name. They designed the thing. So IBM, and in particular the folks who worked in Boca Raton at IBM's
Entry Systems Division in the early 1980's, deserve all the credit for defining what eventually became known as the
Industry Standard Architecture (ISA) PC platform.
However, the dawn of the PC era also saw the rise and fall of many other personal computer companies.
One of the more exceptional companies from that era was COMPAQ. It would be a mistake to dismiss COMPAQ as just
another company that set out to "copy" or "clone" IBM's design, because from the very beginning, COMPAQ's ambitions
went beyond mere imitation. They were really in the *compatibility* business.
Innovation in the PC industry would not have been as rapid and diverse if IBM had been the only supplier of
compatible machines and accessories. If IBM had been able to control the market, they probably would have continued
some of the monopolistic practices they had perfected in the mainframe era. Market control might have been a win for
IBM, but it almost certainly would *not* have been a win for customers.
In 1982, the founders of COMPAQ took a big gamble, by reportedly investing around a million dollars to create
a system that mimicked IBM's without infringing on it. I don't know if COMPAQ pioneered the concept of "clean room"
design, where the engineers designing and coding have no direct contact with the hardware and software they're cloning,
but they were certainly successful.
But that wasn't COMPAQ's most important contribution. That was just the price they had to "pay to play." Armed with
compatible hardware and software, COMPAQ proceeded to innovate in ways that IBM did not.
Look at COMPAQ's first product: the COMPAQ Portable. For starters, it was *portable*. And unlike
IBM machines, it didn't force you to choose between a higher-quality text-only (MDA) display or a lower-quality
graphics-capable (CGA) display. COMPAQ improved on IBM's mutually-exclusive display choices by creating a monochrome
display that could display both high-quality text *and* graphics.
IBM released their own portable unit a year later, but it looked suspiciously COMPAQ-like, and it lacked COMPAQ's
innovative display.
That tradition of innovation at COMPAQ continued for many years. Sometimes they even seemed *more* concerned about
compatibility than IBM did. For example, faster machines sometimes broke speed-sensitive floppy-based copy protection
schemes, which COMPAQ tried to avoid by automatically *slowing* the machine down whenever a floppy drive was spinning.
To my knowledge, IBM never bothered with such a feature -- maybe IBM was simply being pragmatic, or perhaps they were
just a little arrogant, operating on the theory that if the universe revolves around IBM, then the planets will
simply realign *themselves* (translation: software vendors will release new versions if they want to remain IBM-compatible).
Sadly, COMPAQ was ultimately absorbed by another behemoth -- Hewlett Packard -- and then simply disappeared. Today,
all we have are the memories.
### Speaking of Memories
In particular, Read-Only Memories: we have precious few of those, too. I finally obtained
a [ROM Dump](/devices/pcx86/rom/compaq/portable/) from COMPAQ's first machine, the COMPAQ Portable, but I had to buy
a system board on ebay to get it. Considering all the effort (and money) that COMPAQ invested in writing that code,
it's a bit depressing that these things haven't been properly preserved and memorialized.
Looking ahead to the day when PCjs will be able to simulate the original COMPAQ Portable, I thought it would be a good
idea to create my own roughly chronological list of [COMPAQ Machines](/devices/pcx86/machine/compaq/) from the 1980s,
since they are all machines I would like to see PCjs eventually support:
- COMPAQ Portable
- COMPAQ [Portable] Plus
- COMPAQ DeskPro
- COMPAQ DeskPro 286
- COMPAQ Portable 286
- COMPAQ Portable II
- [COMPAQ DeskPro 386](/devices/pcx86/machine/compaq/deskpro386/)
- COMPAQ Portable III
- COMPAQ DeskPro 386/20
- COMPAQ Portable 386
- COMPAQ DeskPro 386/25
- COMPAQ DeskPro 386s
- COMPAQ DeskPro 386/20e
- COMPAQ SLT Series (SLT/286)
- COMPAQ DeskPro 386/33
- COMPAQ LTE Series (LTE and LTE/286)
It's hard to stop at this point, because COMPAQ produced other innovative 80386-based systems, like those in the LTE
and LTE Lite series. But I think I need to stick to my original plan and draw a line at the end of the 1980s.
### Regarding The COMPAQ Name
As best I can tell, COMPAQ preferred to print its company name in all-caps, so that's my practice as well.
However, it seems that sometime between the release of [COMPAQ MS-DOS 3.10](/disks/pcx86/dos/compaq/3.10/) and
[COMPAQ MS-DOS 3.31](/disks/pcx86/dos/compaq/3.31/), there may have been a shift in policy. Both products still
called themselves `The COMPAQ Personal Computer MS-DOS`, but in 3.31, the copyright string changed to
`Compaq Computer Corp.`
Their all-caps practice also extended to product names (eg, `COMPAQ DESKPRO`), at least in their marketing literature.
Contemporary news stories, however, tended to lower-case the product name (eg, `COMPAQ Deskpro`). I've decided to
split the difference and use mixed-case where it seems appropriate (eg, `COMPAQ DeskPro`).
*[@jeffpar](http://twitter.com/jeffpar)*
*February 24, 2016*

View file

@ -1,48 +0,0 @@
---
layout: post
title: Touching Windows
date: 2016-03-06 09:00:00
permalink: /blog/2016/03/06/
---
Yesterday, I fired up [Windows 3.1](/disks/pcx86/windows/3.10/) and played a complete game of
[Windows Solitaire](https://en.wikipedia.org/wiki/Microsoft_Solitaire) on my iPad. It was a bit, um, touchy,
but it worked.
{% comment %}[<img src="/blog/images/ipad-solitaire-small.jpg" alt="Windows Solitaire on iPad"/>](/blog/images/ipad-solitaire.jpg){% endcomment %}
{% include screenshot.html src="/blog/images/ipad-solitaire-small.jpg" title="iPad running Solitaire on Windows 3.10" link="/blog/images/ipad-solitaire.jpg" %}
The basic touch events are:
- `touchstart`
- `touchmove`
- `touchend`
which roughly correspond to `mousedown`, `mousemove`, and `mouseup` events, except that your finger isn't
exactly like a mouse -- it doesn't have *buttons* for one thing -- so there are several usability problems
that must be solved.
One of the problems is how to deal with the *default behaviors* of touch events. For example, tapping on a
PCjs screen is normally how you trigger the iPad's soft keyboard, and dragging your finger across the screen
normally scrolls the page up and down. However, you don't want *either* of those behaviors when you're
trying to move the machine's mouse pointer around or click on things.
PCjs attempts to resolve the tapping problem by disabling default behaviors most of the time, except when
two taps occur more than 1/2 second apart. This allows a quick *double-tap* to be treated as a double-click,
while a pair of slower taps allows the soft keyboard to activate.
Another problem is how to differentiate between *moving the mouse* (ie, without any buttons pressed) and
*dragging the mouse* (ie, with the left button pressed). Newer devices offer the option of relying on "3D Touch"
(aka Force Touch), and using pressure to determine the user's intent, but most devices don't have that feature,
and I'm not sure I'd want to rely on that anyway.
The PCjs solution: if you *tap and hold* for more than 200ms, and then start moving, your movements become the
equivalent of a *mouse drag* operation.
This is my first stab at touch-to-mouse conversion, and it's not perfect, but it's already fairly usable,
as my Solitaire experiment demonstrates. And there are still some open questions, such as how to intuitively
support operations like *right drag*, since the current *mouse drag* operation is inherently *left drag*;
one solution would be to rely on multi-touch and use two fingers to trigger right-button operations.
*[@jeffpar](http://twitter.com/jeffpar)*
*March 6, 2016*

View file

@ -1,34 +0,0 @@
---
layout: post
title: Demos of Windows/386 and Windows 3.x
date: 2016-03-12 14:00:00
permalink: /blog/2016/03/12/
---
I recently added some more demos to the PCjs Project, to showcase its ability to run old 80286-based and
80386-based software, such as [Windows/386](/disks/pcx86/windows/2.0x/), [Windows 3.0](/disks/pcx86/windows/3.00/),
[Windows 3.1](/disks/pcx86/windows/3.10/), and [Windows 95](/disks/pcx86/windows/win95/4.00.950/).
{% include screenshot.html src="/disks/pcx86/windows/2.0x/thumbnail.jpg" width="200" height="120" title="COMPAQ DeskPro 386, Windows/386 2.01" link="/disks/pcx86/windows/2.0x/" %}
{% include screenshot.html src="/disks/pcx86/windows/3.00/thumbnail.jpg" width="200" height="120" title="IBM PC AT w/EGA, Windows 3.00" link="/disks/pcx86/windows/3.00/" %}
{% include screenshot.html src="/disks/pcx86/windows/3.10/thumbnail.jpg" width="200" height="120" title="IBM PC AT w/VGA, Windows 3.10" link="/disks/pcx86/windows/3.10/" %}
{% include screenshot.html src="/disks/pcx86/windows/win95/4.00.950/thumbnail.jpg" width="200" height="120" title="COMPAQ DeskPro 386, Windows 95" link="/disks/pcx86/windows/win95/4.00.950/" %}
As the [OS/2 Museum](http://www.os2museum.com/wp/windows386-2-01/) points out, [Windows/386 2.01](/disks/pcx86/windows/2.0x/)
was the first Microsoft product to specifically target the 80386. However, not only was it *not* a 32-bit operating
system, it didn't even run Windows applications in protected-mode. Windows apps ran in V86-mode, and even then, *only*
if you started Windows by running `WIN386.EXE`.
There may have been some incidental protection advantages to running Windows in V86-mode instead of real-mode,
but the primary advantages were the ability to simulate expanded memory (EMS) using the 80386's paging capabilities,
and the ability to run multiple DOS applications simultaneously.
Alternatively, you could start Windows/386 with `WIN86.COM`, which reverted to the older Windows 1.x memory model,
where all Windows applications ran in real-mode and only one DOS application could be run at a time.
Strangely, unlike all previous and subsequent versions of Windows, there was no `WIN` command in Windows/386.
At least there was no `LOSE` command.
*[@jeffpar](http://twitter.com/jeffpar)*
*March 12, 2016*

View file

@ -1,56 +0,0 @@
---
layout: post
title: Introducing the Intel 8080 CPU
date: 2016-04-30 14:00:00
permalink: /blog/2016/04/30/
---
Or rather, introducing [PC8080](/modules/pc8080/), a new 8080-based machine emulator recently added to the
PCjs Project.
Our first [8080 Test Machine](/devices/pc8080/machine/exerciser/) loads a copy of the
[8080 Exerciser](https://web.archive.org/web/20151006085348/http://www.idb.me.uk/sunhillow/8080.html)
(specifically, [8080EX1](/devices/pc8080/rom/exerciser/8080EX1.MAC)) and intercepts the exerciser's CP/M console
calls so that you can see its progress in the Control Panel window. It's a "headless" test machine
(no keyboard or display), so that's all you get.
The good news: PC8080 passes all the 8080 Exerciser tests. And it doesn't do it by using all sorts of weird
"flags tables" that most other 8080 emulators seem to fall back on.
Like all the other CPU emulations in the PCjs Project, PC8080 never "calculates" the flags unless/until they are
actually required, which considerably speeds up arithmetic operations.
Of particular note are the 8080's subtract, compare, and decrement operations, which actually perform addition,
not subtraction, by using two's complement arithmetic in "stages": the first stage (inverting the source operand)
occurs *before* the addition, and the second stage (incrementing the inverted operand) occurs *after* the addition.
And it appears to be the result of the *first* stage, not the second, that determines the state of the Auxiliary
Carry flag (AF).
The behavior of the Auxiliary Carry flag (AF) and the associated DAA instruction are probably the most significant
(and least understood) *arithmetic* differences between the 8080 and all later x86-based CPUs. Well, there's also
the fact that the 8080 doesn't provide an Overflow flag (OF). Internally however, PC8080 retains the ability to
calculate overflow (since PC8080 was a fork of PCjs), which should be useful when we add Z80 support to PC8080.
On a related note, [Ken Shirriff](http://www.righto.com/) has some fascinating blog posts on the 8085 that also
provide clues as to how the 8080 likely operates:
* [Inside the ALU of the 8085 microprocessor (January 2013)](http://www.righto.com/2013/01/inside-alu-of-8085-microprocessor.html)
* [Silicon reverse engineering: The 8085's undocumented flags (February 2013)](http://www.righto.com/2013/02/looking-at-silicon-to-understanding.html)
* [The 8085's register file reverse engineered (March 2013)](http://www.righto.com/2013/03/register-file-8085.html)
* [Reverse-engineering the 8085's ALU and its hidden registers (July 2013)](http://www.righto.com/2013/07/reverse-engineering-8085s-alu-and-its.html)
* [Reverse-engineering the flag circuits in the 8085 processor (July 2013)](http://www.righto.com/2013/07/reverse-engineering-flag-circuits-in.html)
* [Reverse-engineering the 8085's decimal adjust circuitry (August 2013)](http://www.righto.com/2013/08/reverse-engineering-8085s-decimal.html)
The idea is to make [PC8080](/modules/pc8080/) sufficiently configurable so that it will work with a variety of
8080-based systems, including those with memory-mapped video displays (like
[Space Invaders](/devices/pc8080/machine/invaders/)), as well as simpler terminal-based systems, like the CP/M-based
systems of old.
In fact, as soon as [Space Invaders](/devices/pc8080/machine/invaders/) is working, my next planned adaptation
is a DEC VT100 terminal emulator (itself an 8080-based machine) which can then be "wired up" to other PCjs machine
simulations. This will not be yet-another VT100-compatible emulation -- which, like 8080 emulators, has been done to
death -- but rather a simulation of the original VT100 hardware, building on [Adam Mayer's](https://github.com/phooky)
work [reverse-engineering the VT100](https://github.com/phooky/VT100-Hax).
*[@jeffpar](http://twitter.com/jeffpar)*
*April 30, 2016*

View file

@ -1,95 +0,0 @@
---
layout: post
title: The Sharpening
date: 2016-05-04 08:00:00
permalink: /blog/2016/05/04/
---
This was the week of The Sharpening.
A while back, I updated most of the machines to use higher-resolution "screens". For example, a typical
[EGA video configuration](/devices/pcx86/video/ibm/ega/1984-09-13/128kb-autolockfs.xml) now specifies a *screenWidth*
of 1280 and *screenHeight* of 700, dimensions which are exactly twice the standard EGA resolution.
That change had no effect on the machine's operation, but it did improve the machine's appearance, because
most people are using much higher resolution monitors today, so by using a higher-resolution "screen" (canvas),
less interpolation is happening when a machine's screen image is scaled up to fill your browser window.
The amount of scaling *also* depends on whether the machine allows itself to be stretched to fill the browser window.
For example, this [machine](/devices/pcx86/machine/5160/ega/640kb/array/machine.xml) (used by the
[EGA Machine Array Demo](/devices/pcx86/machine/5160/ega/640kb/array/)) is limited to an overall *width* of 680 pixels,
no matter how large you make your browser window:
```xml
<machine id="ibm5160" class="pc" border="1" width="680px" float="left" background="#FAEBD7">
```
but most machines don't specify a (maximum) overall width, so their screen canvas is allowed to stretch far beyond
the initial *screenWidth* and *screenHeight*, thanks to some additional CSS settings.
Larger screens mean less interpolation, which is a good thing if you want the screens to look less "fuzzy."
However, interpolation also happens at a deeper level, because internally, PCjs uses two canvases to move pixels
from the machine's frame buffer to your browser: the screen canvas, which I've already discussed, and another
canvas called the *buffer* canvas, where changes to the machine's frame buffer are, um, buffered.
The *buffer* canvas has the same dimensions as the machine's frame buffer, whereas the *screen* canvas
has generally higher dimensions (as explained above), which your browser may then be stretching to even higher
dimensions, depending on your monitor resolution and browser size.
The differential between the *buffer* canvas and *screen* canvas is where additional interpolation (fuzziness)
creeps in. That is where The Sharpening now occurs.
All the browsers I've tested so far (Chrome, Firefox, and Safari) support a
[Canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API)
[Context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D) property named
[imageSmoothingEnabled](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/imageSmoothingEnabled),
which eliminates much of the fuzziness that would occur when copying pixels from the lower-resolution *buffer* canvas
to the higher-resolution *screen* canvas.
So I've added a new [Video](/docs/pcx86/video/) property named *smoothing* that can be set to "true" or "false",
and I've set it to "false" for most machines in the project. If *smoothing* is not set, your browser continues to
use its default interpolation method.
{% include screenshot.html src="/blog/images/si1978-fuzzier.png" width="339" height="388" title="Space Invaders (Fuzzier)" link="/devices/pc8080/machine/invaders/?smoothing=true" %}
{% include screenshot.html src="/blog/images/si1978-sharper.png" width="339" height="388" title="Space Invaders (Sharper)" link="/devices/pc8080/machine/invaders/?smoothing=false" %}
For some people, this might be a matter of taste, because less fuzziness necessarily means more pixelation (ie, you
can see individual pixels more clearly), which becomes more noticeable when switching a machine **Full Screen**.
So I've also added a URL *smoothing* parameter you can use to override a machine's default setting; eg:
http://www.pcjs.org/devices/pc8080/machine/invaders/?smoothing=true
See for yourself, by clicking on each of the [Space Invaders](/devices/pc8080/machine/invaders/) images above and
then clicking the **Full Screen** button; both images link to the same machine, but left one enables image smoothing,
while the right one does not.
Aspect Ratio
---
The *smoothing* property joins another recent [Video](/docs/pcx86/video/) property, *aspect*, that was added in a
[release](https://github.com/jeffpar/pcjs/releases/tag/v1.21.5) last month.
To recap, aspect ratio is display width divided by display height, but the choice of aspect ratio is complicated by
the fact that none of the early IBM video card/monitor combinations (with the exception of the VGA) displayed square
pixels, and (with the exception of the MDA) they could display text and graphics at a variety of resolutions.
So, for those users who either 1) don't like the aspect ratios that PCjs has chosen, or 2) just want to squeeze or
stretch a machine's screen a bit more, there is now an *aspect* parameter you can append to the URL of any page
containing one or more PCjs machines.
For example:
http://www.pcjs.org/disks/pcx86/dos/ibm/1.00/?aspect=2.0
will modify the height of the machine's screen to conform to the requested aspect ratio of 2.0. The screen should still
be responsive to any browser resizing while still retaining that aspect ratio.
---
That's all for now. Work continues on the new [PC8080](/modules/pc8080/) emulator and the
[Space Invaders](/devices/pc8080/machine/invaders/) test machine. More on that later, when it's finished.
Until then, May the 4th be with you!
*[@jeffpar](http://twitter.com/jeffpar)*
*May 4, 2016*

View file

@ -1,205 +0,0 @@
/**
* Reset some basic elements
*/
body, h1, h2, h3, h4, h5, h6,
p, blockquote, pre,
dl, dd, ol, ul, figure {
margin: 0;
padding: 0;
}
/**
* Basic styling
*/
body {
font-family: $base-font-family;
font-size: $base-font-size;
line-height: $base-line-height;
font-weight: 300;
color: $text-color;
background-color: $background-color;
-webkit-text-size-adjust: 100%;
}
/**
* Set `margin-bottom` to maintain vertical rhythm
*/
h1, h2, h3, h4, h5, h6,
p, blockquote, pre,
ul, ol, dl, figure,
%vertical-rhythm {
margin-bottom: $spacing-unit / 2;
}
/**
* Images
*/
img {
max-width: 100%;
vertical-align: middle;
}
/**
* Figures
*/
figure > img {
display: block;
}
figcaption {
font-size: $small-font-size;
}
/**
* Lists
*/
ul, ol {
margin-left: $spacing-unit;
}
li {
> ul,
> ol {
margin-bottom: 0;
}
}
/**
* Headings
*/
h1, h2, h3, h4, h5, h6 {
font-weight: 300;
}
/**
* Links
*/
a {
color: $brand-color;
text-decoration: none;
&:visited {
color: darken($brand-color, 15%);
}
&:hover {
color: $text-color;
text-decoration: underline;
}
}
/**
* Blockquotes
*/
blockquote {
color: $grey-color;
border-left: 4px solid $grey-color-light;
padding-left: $spacing-unit / 2;
/* font-size: 18px; */
/* letter-spacing: -1px; */
/* font-style: italic; */
> :last-child {
margin-bottom: 0;
}
}
/**
* Code formatting
*/
pre,
code {
font-size: 15px;
border: 1px solid $grey-color-light;
border-radius: 3px;
background-color: #eef;
}
code {
padding: 1px 5px;
}
pre {
padding: 8px 12px;
overflow-x: scroll;
white-space: pre-wrap;
> code {
border: 0;
padding-right: 0;
padding-left: 0;
}
}
/**
* Wrapper
*/
.wrapper {
/* max-width: -webkit-calc(800px - (#{$spacing-unit} * 2)); */
/* max-width: calc(800px - (#{$spacing-unit} * 2)); */
margin-right: auto;
margin-left: auto;
padding-right: $spacing-unit;
padding-left: $spacing-unit;
@extend %clearfix;
@include media-query($on-laptop) {
/* max-width: -webkit-calc(800px - (#{$spacing-unit})); */
/* max-width: calc(800px - (#{$spacing-unit})); */
padding-right: $spacing-unit / 2;
padding-left: $spacing-unit / 2;
}
}
/**
* Clearfix
*/
%clearfix {
&:after {
content: "";
display: table;
clear: both;
}
}
/**
* Icons
*/
.icon {
> svg {
display: inline-block;
width: 16px;
height: 16px;
vertical-align: middle;
path {
fill: $grey-color;
}
}
}

View file

@ -1,258 +0,0 @@
/**
* Site header
*/
.site-header {
border-top: 5px solid $grey-color-dark;
border-bottom: 1px solid $grey-color-light;
min-height: 56px;
// Positioning context for the mobile navigation icon
position: relative;
}
.site-title {
font-size: 26px;
line-height: 56px;
letter-spacing: -1px;
margin-bottom: 0;
float: left;
&,
&:visited {
color: $grey-color-dark;
}
}
.site-nav {
float: right;
line-height: 56px;
.menu-icon {
display: none;
}
.page-link {
color: $text-color;
line-height: $base-line-height;
// Gaps between nav items, but not on the first one
&:not(:first-child) {
margin-left: 20px;
}
}
@include media-query($on-palm) {
position: absolute;
top: 9px;
right: 30px;
background-color: $background-color;
border: 1px solid $grey-color-light;
border-radius: 5px;
text-align: right;
.menu-icon {
display: block;
float: right;
width: 36px;
height: 26px;
line-height: 0;
padding-top: 10px;
text-align: center;
> svg {
width: 18px;
height: 15px;
path {
fill: $grey-color-dark;
}
}
}
.trigger {
clear: both;
display: none;
}
&:hover .trigger {
display: block;
padding-bottom: 5px;
}
.page-link {
display: block;
padding: 5px 10px;
}
}
}
/**
* Site footer
*/
.site-footer {
border-top: 1px solid $grey-color-light;
padding: $spacing-unit 0;
}
.footer-heading {
font-size: 18px;
margin-bottom: $spacing-unit / 2;
}
.contact-list,
.social-media-list {
list-style: none;
margin-left: 0;
}
.footer-col-wrapper {
font-size: 15px;
color: $grey-color;
margin-left: -$spacing-unit / 2;
@extend %clearfix;
}
.footer-col {
float: left;
margin-bottom: $spacing-unit / 2;
padding-left: $spacing-unit / 2;
}
.footer-col-1 {
width: -webkit-calc(40% - (#{$spacing-unit} / 2));
width: calc(40% - (#{$spacing-unit} / 2));
}
.footer-col-2 {
width: -webkit-calc(20% - (#{$spacing-unit} / 2));
width: calc(20% - (#{$spacing-unit} / 2));
}
.footer-col-3 {
width: -webkit-calc(100% - (#{$spacing-unit} / 2));
width: calc(100% - (#{$spacing-unit} / 2));
}
@include media-query($on-laptop) {
.footer-col-1,
.footer-col-2 {
width: -webkit-calc(50% - (#{$spacing-unit} / 2));
width: calc(50% - (#{$spacing-unit} / 2));
}
.footer-col-3 {
width: -webkit-calc(100% - (#{$spacing-unit} / 2));
width: calc(100% - (#{$spacing-unit} / 2));
}
}
@include media-query($on-palm) {
.footer-col {
float: none;
width: -webkit-calc(100% - (#{$spacing-unit} / 2));
width: calc(100% - (#{$spacing-unit} / 2));
}
}
/**
* Page content
*/
.page-content {
padding: $spacing-unit 0;
}
.page-heading {
font-size: 20px;
}
.post-list {
margin-left: 0;
list-style: none;
> li {
margin-bottom: $spacing-unit;
}
}
.post-meta {
font-size: $small-font-size;
color: $grey-color;
}
.post-link {
display: block;
font-size: 24px;
}
/**
* Posts
*/
.post-header {
margin-bottom: $spacing-unit;
}
.post-title {
font-size: 42px;
letter-spacing: -1px;
line-height: 1;
@include media-query($on-laptop) {
font-size: 36px;
}
}
.post-content {
margin-bottom: $spacing-unit;
h2 {
font-size: 32px;
@include media-query($on-laptop) {
font-size: 28px;
}
}
h3 {
font-size: 26px;
@include media-query($on-laptop) {
font-size: 22px;
}
}
h4 {
font-size: 20px;
@include media-query($on-laptop) {
font-size: 18px;
}
}
}
/**
* PCjs-specific styles
*/
.machine {
margin-bottom: 16px;
}
.screenshot-frame {
display: inline-block;
margin: 8px;
text-align: center;
}
.screenshot-image {
padding: 5px;
border: 1px solid black;
border-radius: 5px;
background-color: #FAEBD7;
}
.screenshot-label {
font-size: x-small;
}

View file

@ -1,68 +0,0 @@
/**
* Syntax highlighting styles
*/
.highlight {
// background: #fff;
@extend %vertical-rhythm;
.c { color: #998; font-style: italic } // Comment
.err { color: #a61717; background-color: #e3d2d2 } // Error
.k { font-weight: bold } // Keyword
.o { font-weight: bold } // Operator
.cm { color: #998; font-style: italic } // Comment.Multiline
.cp { color: #999; font-weight: bold } // Comment.Preproc
.c1 { color: #998; font-style: italic } // Comment.Single
.cs { color: #999; font-weight: bold; font-style: italic } // Comment.Special
.gd { color: #000; background-color: #fdd } // Generic.Deleted
.gd .x { color: #000; background-color: #faa } // Generic.Deleted.Specific
.ge { font-style: italic } // Generic.Emph
.gr { color: #a00 } // Generic.Error
.gh { color: #999 } // Generic.Heading
.gi { color: #000; background-color: #dfd } // Generic.Inserted
.gi .x { color: #000; background-color: #afa } // Generic.Inserted.Specific
.go { color: #888 } // Generic.Output
.gp { color: #555 } // Generic.Prompt
.gs { font-weight: bold } // Generic.Strong
.gu { color: #aaa } // Generic.Subheading
.gt { color: #a00 } // Generic.Traceback
.kc { font-weight: bold } // Keyword.Constant
.kd { font-weight: bold } // Keyword.Declaration
.kp { font-weight: bold } // Keyword.Pseudo
.kr { font-weight: bold } // Keyword.Reserved
.kt { color: #458; font-weight: bold } // Keyword.Type
.m { color: #099 } // Literal.Number
.s { color: #d14 } // Literal.String
.na { color: #008080 } // Name.Attribute
.nb { color: #0086B3 } // Name.Builtin
.nc { color: #458; font-weight: bold } // Name.Class
.no { color: #008080 } // Name.Constant
.ni { color: #800080 } // Name.Entity
.ne { color: #900; font-weight: bold } // Name.Exception
.nf { color: #900; font-weight: bold } // Name.Function
.nn { color: #555 } // Name.Namespace
.nt { color: #000080 } // Name.Tag
.nv { color: #008080 } // Name.Variable
.ow { font-weight: bold } // Operator.Word
.w { color: #bbb } // Text.Whitespace
.mf { color: #099 } // Literal.Number.Float
.mh { color: #099 } // Literal.Number.Hex
.mi { color: #099 } // Literal.Number.Integer
.mo { color: #099 } // Literal.Number.Oct
.sb { color: #d14 } // Literal.String.Backtick
.sc { color: #d14 } // Literal.String.Char
.sd { color: #d14 } // Literal.String.Doc
.s2 { color: #d14 } // Literal.String.Double
.se { color: #d14 } // Literal.String.Escape
.sh { color: #d14 } // Literal.String.Heredoc
.si { color: #d14 } // Literal.String.Interpol
.sx { color: #d14 } // Literal.String.Other
.sr { color: #009926 } // Literal.String.Regex
.s1 { color: #d14 } // Literal.String.Single
.ss { color: #990073 } // Literal.String.Symbol
.bp { color: #999 } // Name.Builtin.Pseudo
.vc { color: #008080 } // Name.Variable.Class
.vg { color: #008080 } // Name.Variable.Global
.vi { color: #008080 } // Name.Variable.Instance
.il { color: #099 } // Literal.Number.Integer.Long
.lineno { color: #ccc; display:inline-block; padding: 0 5px; border-right:1px solid #ccc; }
}

View file

@ -1,14 +0,0 @@
---
layout: blog
title: Blog
menu_title: Blog
menu_order: 2
permalink: /blog/
---
Keep tabs on the [PCjs Project](https://github.com/jeffpar/pcjs) on GitHub:
[Releases](https://github.com/jeffpar/pcjs/releases), [Issues](https://github.com/jeffpar/pcjs/issues),
and [Commits](https://github.com/jeffpar/pcjs/commits/master).
For random musings about PCjs, web technologies, old hardware and software, and more, read on.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 252 KiB

View file

@ -1,94 +0,0 @@
---
layout: page
permalink: /
machines:
- type: pcx86
id: ibm5150
name: "IBM PC (Model 5150) with Monochrome Display"
config: /devices/pcx86/machine/5150/mda/64kb/machine.xml
- type: c1p
id: demoC1P
config: /devices/c1p/machine/8kb/large/machine.xml
---
Welcome to PCjs, home of [PCx86](/docs/pcx86/), the original IBM PC simulation written entirely JavaScript. It is
one of several JavaScript Machines in the [PCjs Project](https://github.com/jeffpar/pcjs), an open-source project that
includes:
* [PCx86](/docs/pcx86/), an x86-based IBM PC and PC-compatible emulator
* [PC8080](/modules/pc8080/), a 8080-based machine emulator (e.g., [Space Invaders](/devices/pc8080/machine/invaders/))
* [C1Pjs](/docs/c1pjs/), a 6502-based emulation of the Ohio Scientific [Challenger 1P](/devices/c1p/)
All PCjs computer simulations are written entirely in [JavaScript](/modules/). No Flash, Java or other plugins are
required. Supported browsers include modern versions of Chrome, Safari, Firefox, Internet Explorer (v9.0 and up), Edge,
and assorted mobile browsers.
{% include machine.html id="ibm5150" %}
The [JavaScript Machine](/devices/pcx86/machine/5150/mda/64kb/) above uses [PCx86](/docs/pcx86/) configured with an Intel
8088 running at 4.77Mhz, with 64Kb of RAM and an IBM Monochrome Display Adapter. For more control, there are also
[Control Panel](/devices/pcx86/machine/5150/mda/64kb/debugger/) and [Soft Keyboard](/devices/pcx86/machine/5150/mda/64kb/softkbd/)
configurations, featuring the built-in PCx86 Debugger. For even greater control, build your own PC. The
[PCx86 Documentation](/docs/pcx86/) will help you get started.
PCx86 has steadily evolved to support more classic x86-based machines, including the IBM PC XT, the 80286-based IBM PC AT,
and the 80386-based COMPAQ DeskPro 386. PCx86 fully supports the original machine ROMs, video cards, etc, and all
machines run at their original speeds.
The goals of the [PCjs Project](/docs/about/) are to create fast, full-featured simulations of classic
computer hardware, help people understand how these early machines worked, make it easy to experiment with different
machine configurations, and provide a platform for running and analyzing old computer software.
Demos
---
Some pre-configured machines are shown below, ready to run BASIC, DOS, Windows, OS/2, and other assorted software.
{% include screenshot.html src="/apps/pcx86/1981/visicalc/thumbnail.jpg" width="200" height="120" title="IBM PC running VisiCalc" link="/apps/pcx86/1981/visicalc/" %}
{% include screenshot.html src="/devices/pcx86/machine/5150/cga/64kb/donkey/thumbnail.jpg" width="200" height="120" title="IBM PC running DONKEY.BAS" link="/devices/pcx86/machine/5150/cga/64kb/donkey/" %}
{% include screenshot.html src="/disks/pcx86/os2/ibm/1.0/thumbnail.jpg" width="200" height="120" title="IBM PC AT w/EGA, OS/2 1.0" link="/disks/pcx86/os2/ibm/1.0/" %}
{% include screenshot.html src="/devices/pcx86/machine/5160/cga/256kb/win101/thumbnail.jpg" width="200" height="120" title="IBM PC XT w/CGA, Windows 1.01" link="/devices/pcx86/machine/5160/cga/256kb/win101/" %}
{% include screenshot.html src="/disks/pcx86/windows/1.01/thumbnail.jpg" width="200" height="120" title="IBM PC XT w/EGA, Windows 1.01" link="/disks/pcx86/windows/1.01/" %}
{% include screenshot.html src="/disks/pcx86/windows/2.0x/thumbnail.jpg" width="200" height="120" title="COMPAQ DeskPro 386, Windows/386 2.01" link="/disks/pcx86/windows/2.0x/" %}
{% include screenshot.html src="/disks/pcx86/windows/3.00/thumbnail.jpg" width="200" height="120" title="IBM PC AT w/EGA, Windows 3.00" link="/disks/pcx86/windows/3.00/" %}
{% include screenshot.html src="/disks/pcx86/windows/3.10/thumbnail.jpg" width="200" height="120" title="IBM PC AT w/VGA, Windows 3.10" link="/disks/pcx86/windows/3.10/" %}
{% include screenshot.html src="/disks/pcx86/windows/win95/4.00.950/thumbnail.jpg" width="200" height="120" title="COMPAQ DeskPro 386, Windows 95" link="/disks/pcx86/windows/win95/4.00.950/" %}
{% include screenshot.html src="/disks/pcx86/cpm/1.1b/thumbnail.jpg" width="200" height="120" title="IBM PC w/MDA, CP/M-86" link="/disks/pcx86/cpm/1.1b/" %}
{% include screenshot.html src="/disks/pcx86/games/microsoft/adventure/thumbnail.jpg" width="200" height="120" title="IBM PC w/MDA, Microsoft Adventure" link="/disks/pcx86/games/microsoft/adventure/" %}
{% include screenshot.html src="/disks/pcx86/games/infocom/zork1/thumbnail.jpg" width="200" height="120" title="IBM PC w/CGA, Zork I" link="/disks/pcx86/games/infocom/zork1/" %}
There are many more [PCx86 Demos](/devices/pcx86/machine/#ready-to-run-app-demos), including an
[IBM PC with Dual Displays](/devices/pcx86/machine/5150/dual/64kb/) demonstrating early multi-monitor support,
and multiple IBM PC XT machines running side-by-side with [CGA Displays](/devices/pcx86/machine/5160/cga/256kb/array/)
and [EGA Displays](/devices/pcx86/machine/5160/ega/640kb/array/).
C1Pjs
---
Below is the [OSI Challenger C1P](/docs/c1pjs/), another simulation in the PCjs Project.
It simulates Ohio Scientific's 6502-based microcomputer, released in 1978. More details about this simulation
and the original machine are available in the [C1Pjs Documentation](/docs/c1pjs/).
{% include machine.html id="demoC1P" %}
License
---
The [PCjs Project](https://github.com/jeffpar/pcjs) is now an open-source project on [GitHub](http://github.com/).
All published portions are free for redistribution and/or modification under the terms of the
[GNU General Public License](/LICENSE) as published by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
You are required to include the following copyright notice, with a link to [{{ site.pcjs.domain }}]({{ site.url }}/):
> [PCjs]({{ site.url }}/) © 2012-2016 by [Jeff Parsons](mailto:Jeff@pcjs.org) ([@jeffpar](http://twitter.com/jeffpar))
in every source code file of every copy or modified version of this work, and to display that notice on every web page
or computer that runs any version of this software.
See [LICENSE](/LICENSE) for details.
More Information
---
Learn more about the [PCjs Project](/docs/about/) and [PCx86](/docs/about/pcx86/). To
create your own PCx86 machines, see the [PCx86 Documentation](/docs/pcx86/) for details.
If you have questions or run into any problems, feel free to [tweet](http://twitter.com/jeffpar) or
[email](mailto:Jeff@pcjs.org).

View file

@ -1,2 +0,0 @@
User-agent: *
Disallow: /web/