CINXE.COM
Ken Shirriff's blog: microcode
<!DOCTYPE html> <html class='v2' dir='ltr' xmlns='http://www.w3.org/1999/xhtml' xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr'> <head> <link href='https://www.blogger.com/static/v1/widgets/3566091532-css_bundle_v2.css' rel='stylesheet' type='text/css'/> <meta content='width=1100' name='viewport'/> <meta content='text/html; charset=UTF-8' http-equiv='Content-Type'/> <meta content='blogger' name='generator'/> <link href='http://www.righto.com/favicon.ico' rel='icon' type='image/x-icon'/> <link href='http://www.righto.com/search/label/microcode' rel='canonical'/> <link rel="alternate" type="application/atom+xml" title="Ken Shirriff's blog - Atom" href="http://www.righto.com/feeds/posts/default" /> <link rel="alternate" type="application/rss+xml" title="Ken Shirriff's blog - RSS" href="http://www.righto.com/feeds/posts/default?alt=rss" /> <link rel="service.post" type="application/atom+xml" title="Ken Shirriff's blog - Atom" href="https://www.blogger.com/feeds/6264947694886887540/posts/default" /> <link rel="me" href="https://www.blogger.com/profile/08097301407311055124" /> <!--Can't find substitution for tag [blog.ieCssRetrofitLinks]--> <meta content='http://www.righto.com/search/label/microcode' property='og:url'/> <meta content='Ken Shirriff's blog' property='og:title'/> <meta content='Computer history, restoring vintage computers, IC reverse engineering, and whatever' property='og:description'/> <title>Ken Shirriff's blog: microcode</title> <style type='text/css'>@font-face{font-family:'Play';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/play/v19/6aez4K2oVqwIvtg2H68T.woff2)format('woff2');unicode-range:U+0460-052F,U+1C80-1C8A,U+20B4,U+2DE0-2DFF,U+A640-A69F,U+FE2E-FE2F;}@font-face{font-family:'Play';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/play/v19/6aez4K2oVqwIvtE2H68T.woff2)format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116;}@font-face{font-family:'Play';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/play/v19/6aez4K2oVqwIvtY2H68T.woff2)format('woff2');unicode-range:U+0370-0377,U+037A-037F,U+0384-038A,U+038C,U+038E-03A1,U+03A3-03FF;}@font-face{font-family:'Play';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/play/v19/6aez4K2oVqwIvto2H68T.woff2)format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+0300-0301,U+0303-0304,U+0308-0309,U+0323,U+0329,U+1EA0-1EF9,U+20AB;}@font-face{font-family:'Play';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/play/v19/6aez4K2oVqwIvts2H68T.woff2)format('woff2');unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF;}@font-face{font-family:'Play';font-style:normal;font-weight:400;font-display:swap;src:url(//fonts.gstatic.com/s/play/v19/6aez4K2oVqwIvtU2Hw.woff2)format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}</style> <style id='page-skin-1' type='text/css'><!-- /* ----------------------------------------------- Blogger Template Style Name: Simple Designer: Blogger URL: www.blogger.com ----------------------------------------------- */ /* Variable definitions ==================== <Variable name="keycolor" description="Main Color" type="color" default="#66bbdd"/> <Group description="Page Text" selector="body"> <Variable name="body.font" description="Font" type="font" default="normal normal 12px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="body.text.color" description="Text Color" type="color" default="#222222"/> </Group> <Group description="Backgrounds" selector=".body-fauxcolumns-outer"> <Variable name="body.background.color" description="Outer Background" type="color" default="#66bbdd"/> <Variable name="content.background.color" description="Main Background" type="color" default="#ffffff"/> <Variable name="header.background.color" description="Header Background" type="color" default="transparent"/> </Group> <Group description="Links" selector=".main-outer"> <Variable name="link.color" description="Link Color" type="color" default="#2288bb"/> <Variable name="link.visited.color" description="Visited Color" type="color" default="#888888"/> <Variable name="link.hover.color" description="Hover Color" type="color" default="#33aaff"/> </Group> <Group description="Blog Title" selector=".header h1"> <Variable name="header.font" description="Font" type="font" default="normal normal 60px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="header.text.color" description="Title Color" type="color" default="#3399bb" /> </Group> <Group description="Blog Description" selector=".header .description"> <Variable name="description.text.color" description="Description Color" type="color" default="#777777" /> </Group> <Group description="Tabs Text" selector=".tabs-inner .widget li a"> <Variable name="tabs.font" description="Font" type="font" default="normal normal 14px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="tabs.text.color" description="Text Color" type="color" default="#999999"/> <Variable name="tabs.selected.text.color" description="Selected Color" type="color" default="#000000"/> </Group> <Group description="Tabs Background" selector=".tabs-outer .PageList"> <Variable name="tabs.background.color" description="Background Color" type="color" default="#f5f5f5"/> <Variable name="tabs.selected.background.color" description="Selected Color" type="color" default="#eeeeee"/> </Group> <Group description="Post Title" selector="h3.post-title, .comments h4"> <Variable name="post.title.font" description="Font" type="font" default="normal normal 22px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> </Group> <Group description="Date Header" selector=".date-header"> <Variable name="date.header.color" description="Text Color" type="color" default="#222222"/> <Variable name="date.header.background.color" description="Background Color" type="color" default="transparent"/> <Variable name="date.header.font" description="Text Font" type="font" default="normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="date.header.padding" description="Date Header Padding" type="string" default="inherit"/> <Variable name="date.header.letterspacing" description="Date Header Letter Spacing" type="string" default="inherit"/> <Variable name="date.header.margin" description="Date Header Margin" type="string" default="inherit"/> </Group> <Group description="Post Footer" selector=".post-footer"> <Variable name="post.footer.text.color" description="Text Color" type="color" default="#666666"/> <Variable name="post.footer.background.color" description="Background Color" type="color" default="#f9f9f9"/> <Variable name="post.footer.border.color" description="Shadow Color" type="color" default="#eeeeee"/> </Group> <Group description="Gadgets" selector="h2"> <Variable name="widget.title.font" description="Title Font" type="font" default="normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif"/> <Variable name="widget.title.text.color" description="Title Color" type="color" default="#000000"/> <Variable name="widget.alternate.text.color" description="Alternate Color" type="color" default="#999999"/> </Group> <Group description="Images" selector=".main-inner"> <Variable name="image.background.color" description="Background Color" type="color" default="#ffffff"/> <Variable name="image.border.color" description="Border Color" type="color" default="#eeeeee"/> <Variable name="image.text.color" description="Caption Text Color" type="color" default="#222222"/> </Group> <Group description="Accents" selector=".content-inner"> <Variable name="body.rule.color" description="Separator Line Color" type="color" default="#eeeeee"/> <Variable name="tabs.border.color" description="Tabs Border Color" type="color" default="#eeeeee"/> </Group> <Variable name="body.background" description="Body Background" type="background" color="#f6fbf6" default="$(color) none repeat scroll top left"/> <Variable name="body.background.override" description="Body Background Override" type="string" default=""/> <Variable name="body.background.gradient.cap" description="Body Gradient Cap" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/gradients_light.png)"/> <Variable name="body.background.gradient.tile" description="Body Gradient Tile" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/body_gradient_tile_light.png)"/> <Variable name="content.background.color.selector" description="Content Background Color Selector" type="string" default=".content-inner"/> <Variable name="content.padding" description="Content Padding" type="length" default="10px" min="0" max="100px"/> <Variable name="content.padding.horizontal" description="Content Horizontal Padding" type="length" default="10px" min="0" max="100px"/> <Variable name="content.shadow.spread" description="Content Shadow Spread" type="length" default="40px" min="0" max="100px"/> <Variable name="content.shadow.spread.webkit" description="Content Shadow Spread (WebKit)" type="length" default="5px" min="0" max="100px"/> <Variable name="content.shadow.spread.ie" description="Content Shadow Spread (IE)" type="length" default="10px" min="0" max="100px"/> <Variable name="main.border.width" description="Main Border Width" type="length" default="0" min="0" max="10px"/> <Variable name="header.background.gradient" description="Header Gradient" type="url" default="none"/> <Variable name="header.shadow.offset.left" description="Header Shadow Offset Left" type="length" default="-1px" min="-50px" max="50px"/> <Variable name="header.shadow.offset.top" description="Header Shadow Offset Top" type="length" default="-1px" min="-50px" max="50px"/> <Variable name="header.shadow.spread" description="Header Shadow Spread" type="length" default="1px" min="0" max="100px"/> <Variable name="header.padding" description="Header Padding" type="length" default="30px" min="0" max="100px"/> <Variable name="header.border.size" description="Header Border Size" type="length" default="1px" min="0" max="10px"/> <Variable name="header.bottom.border.size" description="Header Bottom Border Size" type="length" default="0" min="0" max="10px"/> <Variable name="header.border.horizontalsize" description="Header Horizontal Border Size" type="length" default="0" min="0" max="10px"/> <Variable name="description.text.size" description="Description Text Size" type="string" default="140%"/> <Variable name="tabs.margin.top" description="Tabs Margin Top" type="length" default="0" min="0" max="100px"/> <Variable name="tabs.margin.side" description="Tabs Side Margin" type="length" default="30px" min="0" max="100px"/> <Variable name="tabs.background.gradient" description="Tabs Background Gradient" type="url" default="url(https://resources.blogblog.com/blogblog/data/1kt/simple/gradients_light.png)"/> <Variable name="tabs.border.width" description="Tabs Border Width" type="length" default="1px" min="0" max="10px"/> <Variable name="tabs.bevel.border.width" description="Tabs Bevel Border Width" type="length" default="1px" min="0" max="10px"/> <Variable name="post.margin.bottom" description="Post Bottom Margin" type="length" default="25px" min="0" max="100px"/> <Variable name="image.border.small.size" description="Image Border Small Size" type="length" default="2px" min="0" max="10px"/> <Variable name="image.border.large.size" description="Image Border Large Size" type="length" default="5px" min="0" max="10px"/> <Variable name="page.width.selector" description="Page Width Selector" type="string" default=".region-inner"/> <Variable name="page.width" description="Page Width" type="string" default="auto"/> <Variable name="main.section.margin" description="Main Section Margin" type="length" default="15px" min="0" max="100px"/> <Variable name="main.padding" description="Main Padding" type="length" default="15px" min="0" max="100px"/> <Variable name="main.padding.top" description="Main Padding Top" type="length" default="30px" min="0" max="100px"/> <Variable name="main.padding.bottom" description="Main Padding Bottom" type="length" default="30px" min="0" max="100px"/> <Variable name="paging.background" color="#ffffff" description="Background of blog paging area" type="background" default="transparent none no-repeat scroll top center"/> <Variable name="footer.bevel" description="Bevel border length of footer" type="length" default="0" min="0" max="10px"/> <Variable name="mobile.background.overlay" description="Mobile Background Overlay" type="string" default="transparent none repeat scroll top left"/> <Variable name="mobile.background.size" description="Mobile Background Size" type="string" default="auto"/> <Variable name="mobile.button.color" description="Mobile Button Color" type="color" default="#ffffff" /> <Variable name="startSide" description="Side where text starts in blog language" type="automatic" default="left"/> <Variable name="endSide" description="Side where text ends in blog language" type="automatic" default="right"/> */ /* Content ----------------------------------------------- */ body { font: normal normal 14px Arial, Tahoma, Helvetica, FreeSans, sans-serif; color: #222222; background: #f6fbf6 none repeat scroll top left; padding: 0 40px 40px 40px; } html body .region-inner { min-width: 0; max-width: 100%; width: auto; } h2 { font-size: 22px; } a:link { text-decoration:none; color: #121fb3; } a:visited { text-decoration:none; color: #121fb3; } a:hover { text-decoration:underline; color: #1a00ff; } .body-fauxcolumn-outer .fauxcolumn-inner { background: transparent url(//www.blogblog.com/1kt/simple/body_gradient_tile_light.png) repeat scroll top left; _background-image: none; } .body-fauxcolumn-outer .cap-top { position: absolute; z-index: 1; height: 400px; width: 100%; } .body-fauxcolumn-outer .cap-top .cap-left { width: 100%; background: transparent url(//www.blogblog.com/1kt/simple/gradients_light.png) repeat-x scroll top left; _background-image: none; } .content-outer { -moz-box-shadow: 0 0 40px rgba(0, 0, 0, .15); -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .15); -goog-ms-box-shadow: 0 0 10px #333333; box-shadow: 0 0 40px rgba(0, 0, 0, .15); margin-bottom: 1px; } .content-inner { padding: 10px 10px; } .content-inner { background-color: #ffffff; } /* Header ----------------------------------------------- */ .header-outer { background: #f6fbf7 url(//www.blogblog.com/1kt/simple/gradients_light.png) repeat-x scroll 0 -400px; _background-image: none; } .Header h1 { font: normal normal 42px Play; color: #666666; text-shadow: 1px 2px 3px rgba(0, 0, 0, .2); } .Header h1 a { color: #666666; } .Header .description { font-size: 140%; color: #666666; } .header-inner .Header .titlewrapper { padding: 22px 30px; } .header-inner .Header .descriptionwrapper { padding: 0 30px; } /* Tabs ----------------------------------------------- */ .tabs-inner .section:first-child { border-top: 0 solid #eeeeee; } .tabs-inner .section:first-child ul { margin-top: -0; border-top: 0 solid #eeeeee; border-left: 0 solid #eeeeee; border-right: 0 solid #eeeeee; } .tabs-inner .widget ul { background: #f5f5f5 url(//www.blogblog.com/1kt/simple/gradients_light.png) repeat-x scroll 0 -800px; _background-image: none; border-bottom: 1px solid #eeeeee; margin-top: 0; margin-left: -30px; margin-right: -30px; } .tabs-inner .widget li a { display: inline-block; padding: .6em 1em; font: normal normal 14px Arial, Tahoma, Helvetica, FreeSans, sans-serif; color: #999999; border-left: 1px solid #ffffff; border-right: 1px solid #eeeeee; } .tabs-inner .widget li:first-child a { border-left: none; } .tabs-inner .widget li.selected a, .tabs-inner .widget li a:hover { color: #000000; background-color: #eeeeee; text-decoration: none; } /* Columns ----------------------------------------------- */ .main-outer { border-top: 0 solid #eeeeee; } .fauxcolumn-left-outer .fauxcolumn-inner { border-right: 1px solid #eeeeee; } .fauxcolumn-right-outer .fauxcolumn-inner { border-left: 1px solid #eeeeee; } /* Headings ----------------------------------------------- */ div.widget > h2, div.widget h2.title { margin: 0 0 1em 0; font: normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif; color: #000000; } /* Widgets ----------------------------------------------- */ .widget .zippy { color: #999999; text-shadow: 2px 2px 1px rgba(0, 0, 0, .1); } .widget .popular-posts ul { list-style: none; } /* Posts ----------------------------------------------- */ h2.date-header { font: normal bold 11px Arial, Tahoma, Helvetica, FreeSans, sans-serif; } .date-header span { background-color: transparent; color: transparent; padding: inherit; letter-spacing: inherit; margin: inherit; } .main-inner { padding-top: 30px; padding-bottom: 30px; } .main-inner .column-center-inner { padding: 0 15px; } .main-inner .column-center-inner .section { margin: 0 15px; } .post { margin: 0 0 25px 0; } h3.post-title, .comments h4 { font: normal normal 22px Arial, Tahoma, Helvetica, FreeSans, sans-serif; margin: .75em 0 0; } .post-body { font-size: 110%; line-height: 1.4; position: relative; } .post-body img, .post-body .tr-caption-container, .Profile img, .Image img, .BlogList .item-thumbnail img { padding: 2px; background: #ffffff; border: 1px solid #ffffff; -moz-box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); -webkit-box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); box-shadow: 1px 1px 5px rgba(0, 0, 0, .1); } .post-body img, .post-body .tr-caption-container { padding: 5px; } .post-body .tr-caption-container { color: #222222; } .post-body .tr-caption-container img { padding: 0; background: transparent; border: none; -moz-box-shadow: 0 0 0 rgba(0, 0, 0, .1); -webkit-box-shadow: 0 0 0 rgba(0, 0, 0, .1); box-shadow: 0 0 0 rgba(0, 0, 0, .1); } .post-header { margin: 0 0 1.5em; line-height: 1.6; font-size: 90%; } .post-footer { margin: 20px -2px 0; padding: 5px 10px; color: #666666; background-color: #f9f9f9; border-bottom: 1px solid #eeeeee; line-height: 1.6; font-size: 90%; } #comments .comment-author { padding-top: 1.5em; border-top: 1px solid #eeeeee; background-position: 0 1.5em; } #comments .comment-author:first-child { padding-top: 0; border-top: none; } .avatar-image-container { margin: .2em 0 0; } #comments .avatar-image-container img { border: 1px solid #ffffff; } /* Comments ----------------------------------------------- */ .comments .comments-content .icon.blog-author { background-repeat: no-repeat; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9sLFwMeCjjhcOMAAAD+SURBVDjLtZSvTgNBEIe/WRRnm3U8RC1neQdsm1zSBIU9VVF1FkUguQQsD9ITmD7ECZIJSE4OZo9stoVjC/zc7ky+zH9hXwVwDpTAWWLrgS3QAe8AZgaAJI5zYAmc8r0G4AHYHQKVwII8PZrZFsBFkeRCABYiMh9BRUhnSkPTNCtVXYXURi1FpBDgArj8QU1eVXUzfnjv7yP7kwu1mYrkWlU33vs1QNu2qU8pwN0UpKoqokjWwCztrMuBhEhmh8bD5UDqur75asbcX0BGUB9/HAMB+r32hznJgXy2v0sGLBcyAJ1EK3LFcbo1s91JeLwAbwGYu7TP/3ZGfnXYPgAVNngtqatUNgAAAABJRU5ErkJggg==); } .comments .comments-content .loadmore a { border-top: 1px solid #999999; border-bottom: 1px solid #999999; } .comments .comment-thread.inline-thread { background-color: #f9f9f9; } .comments .continue { border-top: 2px solid #999999; } /* Accents ---------------------------------------------- */ .section-columns td.columns-cell { border-left: 1px solid #eeeeee; } .blog-pager { background: transparent none no-repeat scroll top center; } .blog-pager-older-link, .home-link, .blog-pager-newer-link { background-color: #ffffff; padding: 5px; } .footer-outer { border-top: 0 dashed #bbbbbb; } /* Mobile ----------------------------------------------- */ body.mobile { background-size: auto; } .mobile .body-fauxcolumn-outer { background: transparent none repeat scroll top left; } .mobile .body-fauxcolumn-outer .cap-top { background-size: 100% auto; } .mobile .content-outer { -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, .15); box-shadow: 0 0 3px rgba(0, 0, 0, .15); } .mobile .tabs-inner .widget ul { margin-left: 0; margin-right: 0; } .mobile .post { margin: 0; } .mobile .main-inner .column-center-inner .section { margin: 0; } .mobile .date-header span { padding: 0.1em 10px; margin: 0 -10px; } .mobile h3.post-title { margin: 0; } .mobile .blog-pager { background: transparent none no-repeat scroll top center; } .mobile .footer-outer { border-top: none; } .mobile .main-inner, .mobile .footer-inner { background-color: #ffffff; } .mobile-index-contents { color: #222222; } .mobile-link-button { background-color: #121fb3; } .mobile-link-button a:link, .mobile-link-button a:visited { color: #ffffff; } .mobile .tabs-inner .section:first-child { border-top: none; } .mobile .tabs-inner .PageList .widget-content { background-color: #eeeeee; color: #000000; border-top: 1px solid #eeeeee; border-bottom: 1px solid #eeeeee; } .mobile .tabs-inner .PageList .widget-content .pagelist-arrow { border-left: 1px solid #eeeeee; } .content-outer { max-width: 1400px !important; } /* fix header */ #header-inner { width: 100% !important; background-position: right !important; } .titlewrapper { padding: 11px 30px 0 !important; } .descriptionwrapper { margin-bottom: 0 !important; } .description { font-size: 120% !important; } /* suppress things */ .date-header { display: none; } #Attribution1 { display: none; } .post-author, .post-timestamp, .reaction-buttons { display: none; } /* h2: sidebar titles */ /* h3: post title */ .post-title , .entry-title { font-size: 180% !important; margin-top: 0 !important; } .entry-title a:link, .entry-title a:visited, .entry-title a:active{ color: #a03; } #main h2 { color:#333; margin-bottom:.4em; margin-top: 13px; font-size:140%; } #main h3 { color:#333; margin-bottom:.4em; margin-top: 13px; font-size:110%; } #main h4 { color:#333; margin-bottom:.5em; } #sidebar-right-1 a:link, #sidebar-right-1 a:visited, #sidebar-right-1 a:active { color: #666; } #sidebar-right-1 h2 { font-size: 100%; color: #666; } /* disable image box */ element.style { } table.chargers img { height: 18px; } table.chargers img { height: 18px; } .post-body img, .post-body .tr-caption-container { padding: 5px; } .post-body img, .post-body .tr-caption-container, .Profile img, .Image img, .BlogList .item-thumbnail img { padding: 0; background: #ffffff; border: none; -moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none; } /* Special items */ a:link img.hilite, a:visited img.hilite { color: #fff; } a:hover img.hilite, a:hover img.hilite2 { color: #f66; } a:active img.hilite { color: #33c; } .hilite {cursor:zoom-in} pre {color:#000000;border:1px solid #000000;} pre.repl { background-color:#e0e0f0; font-size:120%;} pre.arc { background-color:#e0e0f0; font-size:120%;} pre.code { background-color:#e0f0e0; font-size:120%; white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;text-wrap:unrestricted;} code { font-size: 100%;} blockquote { font-size: 110%; background: transparent url("//static.righto.com/images/blockquote.gif") no-repeat 0 0; margin: 20px 0px; padding: 0px 40px;} div.cite {font-size: .8em;.; font-style: italic; color: #888; margin-bottom: 9px;} a.ref { color: gray;vertical-align: super; text-decoration: none; font-size:60%;margin-left: 2px;} a img.hilite { border: 1px solid; color: #888; z-index: 2; } a img.hilite2, a:active img.hilite2 { border: 1px solid; color: #f6fbf6; } table.chargers { border-width: 1px; border-spacing: 2px; border-style: outset; border-color: gray; border-collapse: collapse; background-color: white; } table.chargers th.maker { padding-right: 5px; text-align: right; } table.chargers th { border-width: 1px; padding: 3px; border-style: inset; border-color: gray; background-color: white; text-align: center; } table.chargers img { height: 18px; } table.chargers td { text-align: center; border-width: 1px; padding: 2px 8px; border-style: inset; border-color: gray; background-color: white; } --></style> <style id='template-skin-1' type='text/css'><!-- body { min-width: 750px; } .content-outer, .content-fauxcolumn-outer, .region-inner { min-width: 750px; max-width: 750px; _width: 750px; } .main-inner .columns { padding-left: 0px; padding-right: 240px; } .main-inner .fauxcolumn-center-outer { left: 0px; right: 240px; /* IE6 does not respect left and right together */ _width: expression(this.parentNode.offsetWidth - parseInt("0px") - parseInt("240px") + 'px'); } .main-inner .fauxcolumn-left-outer { width: 0px; } .main-inner .fauxcolumn-right-outer { width: 240px; } .main-inner .column-left-outer { width: 0px; right: 100%; margin-left: -0px; } .main-inner .column-right-outer { width: 240px; margin-right: -240px; } #layout { min-width: 0; } #layout .content-outer { min-width: 0; width: 800px; } #layout .region-inner { min-width: 0; width: auto; } --></style> <meta content='width=device-width, initial-scale=1.0, maximum-scale=12.0, minimum-scale=.25, user-scalable=yes' name='viewport'/> <meta content='mw8ww70r3jW0GzXY6j1d' name='follow_it-verification-code'/> <link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=6264947694886887540&zx=c26a9fbf-fe77-4d5f-920c-7ba0d42cc985' media='none' onload='if(media!='all')media='all'' rel='stylesheet'/><noscript><link href='https://www.blogger.com/dyn-css/authorization.css?targetBlogID=6264947694886887540&zx=c26a9fbf-fe77-4d5f-920c-7ba0d42cc985' rel='stylesheet'/></noscript> <meta name='google-adsense-platform-account' content='ca-host-pub-1556223355139109'/> <meta name='google-adsense-platform-domain' content='blogspot.com'/> </head> <body class='loading'> <div class='navbar no-items section' id='navbar'> </div> <div itemscope='itemscope' itemtype='http://schema.org/Blog' style='display: none;'> <meta content='Ken Shirriff's blog' itemprop='name'/> </div> <div class='body-fauxcolumns'> <div class='fauxcolumn-outer body-fauxcolumn-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <div class='content'> <div class='content-fauxcolumns'> <div class='fauxcolumn-outer content-fauxcolumn-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <div class='content-outer'> <div class='content-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left content-fauxborder-left'> <div class='fauxborder-right content-fauxborder-right'></div> <div class='content-inner'> <header> <div class='header-outer'> <div class='header-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left header-fauxborder-left'> <div class='fauxborder-right header-fauxborder-right'></div> <div class='region-inner header-inner'> <div class='header section' id='header'><div class='widget Header' data-version='1' id='Header1'> <div id='header-inner' style='background-image: url("https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-4KXwYe0lQ4HFzhAye9vvRlij2ZYvMbfPCnqEE__1o85Fjo3XgefxJQhWRdwR3EzNWNMWT3yMaj2QZaT9GazqQx3C6oWa3-hBNlRHG7f-Oib-lv1Wq_C2_A0rt8xZgs87iNqzRVKK7H0A/s800/background.jpg"); background-position: left; width: 550px; min-height: 105px; _height: 105px; background-repeat: no-repeat; '> <div class='titlewrapper' style='background: transparent'> <h1 class='title' style='background: transparent; border-width: 0px'> <a href='http://www.righto.com/'> Ken Shirriff's blog </a> </h1> </div> <div class='descriptionwrapper'> <p class='description'><span>Computer history, restoring vintage computers, IC reverse engineering, and whatever</span></p> </div> </div> </div></div> </div> </div> <div class='header-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </header> <div class='tabs-outer'> <div class='tabs-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left tabs-fauxborder-left'> <div class='fauxborder-right tabs-fauxborder-right'></div> <div class='region-inner tabs-inner'> <div class='tabs no-items section' id='crosscol'></div> <div class='tabs no-items section' id='crosscol-overflow'></div> </div> </div> <div class='tabs-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='main-outer'> <div class='main-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left main-fauxborder-left'> <div class='fauxborder-right main-fauxborder-right'></div> <div class='region-inner main-inner'> <div class='columns fauxcolumns'> <div class='fauxcolumn-outer fauxcolumn-center-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='fauxcolumn-outer fauxcolumn-left-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <div class='fauxcolumn-outer fauxcolumn-right-outer'> <div class='cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left'> <div class='fauxborder-right'></div> <div class='fauxcolumn-inner'> </div> </div> <div class='cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <!-- corrects IE6 width calculation --> <div class='columns-inner'> <div class='column-center-outer'> <div class='column-center-inner'> <div class='main section' id='main'><div class='widget Blog' data-version='1' id='Blog1'> <div class='blog-posts hfeed'> <div class='status-msg-wrap'> <div class='status-msg-body'> Showing posts with label <b>microcode</b>. <a href="http://www.righto.com/">Show all posts</a> </div> <div class='status-msg-border'> <div class='status-msg-bg'> <div class='status-msg-hidden'>Showing posts with label <b>microcode</b>. <a href="http://www.righto.com/">Show all posts</a></div> </div> </div> </div> <div style='clear: both;'></div> <div class="date-outer"> <div class="date-posts"> <div class='post-outer'> <div class='post hentry' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://static.righto.com/images/8086-ad-undoc/die-labeled-w600.jpg' itemprop='image_url'/> <meta content='6264947694886887540' itemprop='blogId'/> <meta content='5516205124640022120' itemprop='postId'/> <a name='5516205124640022120'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='http://www.righto.com/2023/07/undocumented-8086-instructions.html'>Undocumented 8086 instructions, explained by the microcode</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-5516205124640022120' itemprop='description articleBody'> <style> >Undocumented 8086 instructions, explained by the microcode</h1> <style> .hilite {cursor:zoom-in} a:link img.hilite, a:visited img.hilite {color: #fff;} a:hover img.hilite {color: #f66;} </style> <style> pre.microcode {font-family: courier, fixed; padding: 10px; background-color: #f5f5f5; display:inline-block;border:none;} pre.microcode span {color: green; font-style:italic; font-family: sans-serif; font-size: 90%;} </style> <p>What happens if you give the Intel 8086 processor an instruction that doesn't exist? A modern microprocessor (80186 and later) will generate an exception, indicating that an illegal instruction was executed. However, early microprocessors didn't include the circuitry to detect illegal instructions, since the chips didn't have transistors to spare. Instead these processors would do <em>something</em>, but the results weren't specified.<span id="fnref:6502"><a class="ref" href="#fn:6502">1</a></span></p> <p>The 8086 has a number of undocumented instructions. Most of them are simply duplicates of regular instructions, but a few have unexpected behavior, such as revealing the values of internal, hidden registers. In the 8086, most instructions are implemented in microcode, so examining the 8086's microcode can explain why these instructions behave the way they do.</p> <p>The photo below shows the 8086 die under a microscope, with the important functional blocks labeled. The metal layer is visible, while the underlying silicon and polysilicon wiring is mostly hidden. The microcode ROM and the microcode address decoder are in the lower right. The Group Decode ROM (upper center) is also important, as it performs the first step of instruction decoding.</p> <p><a href="https://static.righto.com/images/8086-ad-undoc/die-labeled.jpg"><img alt="The 8086 die under a microscope, with main functional blocks labeled. Click on this image (or any other) for a larger version." class="hilite" height="589" src="https://static.righto.com/images/8086-ad-undoc/die-labeled-w600.jpg" title="The 8086 die under a microscope, with main functional blocks labeled. Click on this image (or any other) for a larger version." width="600" /></a><div class="cite">The 8086 die under a microscope, with main functional blocks labeled. Click on this image (or any other) for a larger version.</div></p> <h2>Microcode and 8086 instruction decoding</h2> <p>You might think that machine instructions are the basic steps that a computer performs. However, instructions usually require multiple steps inside the processor. One way of expressing these multiple steps is through microcode, a technique dating back to 1951. To execute a machine instruction, the computer internally executes several simpler micro-instructions, specified by the microcode. In other words, microcode forms another layer between the machine instructions and the hardware. The main advantage of microcode is that it turns the processor's control logic into a programming task instead of a difficult logic design task.</p> <p>The 8086's <a href="https://www.righto.com/2022/11/how-8086-processors-microcode-engine.html">microcode ROM</a> holds 512 micro-instructions, each 21 bits wide. Each micro-instruction performs two actions in parallel. First is a move between a source and a destination, typically registers. Second is an operation that can range from an arithmetic (ALU) operation to a memory access. The diagram below shows the structure of a 21-bit micro-instruction, divided into six types.</p> <p><a href="https://static.righto.com/images/8086-ad-undoc/microcode-format.jpg"><img alt="The encoding of a micro-instruction into 21 bits. Based on NEC v. Intel: Will Hardware Be Drawn into the Black Hole of Copyright?" class="hilite" height="203" src="https://static.righto.com/images/8086-ad-undoc/microcode-format-w700.jpg" title="The encoding of a micro-instruction into 21 bits. Based on NEC v. Intel: Will Hardware Be Drawn into the Black Hole of Copyright?" width="700" /></a><div class="cite">The encoding of a micro-instruction into 21 bits. Based on <a href="https://digitalcommons.law.scu.edu/cgi/viewcontent.cgi?referer=&httpsredir=1&article=1031&context=chtlj">NEC v. Intel: Will Hardware Be Drawn into the Black Hole of Copyright?</a></div></p> <p>When executing a machine instruction, the 8086 performs a decoding step. Although the 8086 is a 16-bit processor, its instructions are based on bytes. In most cases, the first byte specifies the opcode, which may be followed by additional instruction bytes. In other cases, the byte is a "prefix" byte, which changes the behavior of the following instruction. The first byte is analyzed by something called the <a href="https://www.righto.com/2023/05/8086-processor-group-decode-rom.html">Group Decode ROM</a>. This circuit categorizes the first byte of the instruction into about <code>35</code> categories that control how the instruction is decoded and executed. One category is "1-byte logic"; this indicates a one-byte instruction or prefix that is simple and implemented by logic circuitry in the 8086. For instructions in this category, microcode is not involved while the remaining instructions are implemented in microcode. Many of these instructions are in the "two-byte ROM" category indicating that the instruction has a second byte that also needs to be decoded by microcode. This second byte, called the ModR/M byte, specifies that memory addressing mode or registers that the instruction uses.</p> <p>The next step is the microcode's address decoder circuit, which determines where to start executing microcode based on the opcode. Conceptually, you can think of the microcode as stored in a ROM, indexed by the instruction opcode and a few sequence bits. However, since many instructions can use the same microcode, it would be inefficient to store duplicate copies of these routines. Instead, the microcode address decoder permits multiple instructions to reference the same entries in the ROM. This decoding circuitry is similar to a PLA (Programmable Logic Array) so it matches bit patterns to determine a particular starting point. This turns out to be important for undocumented instructions since undocumented instructions often match the pattern for a "real" instruction, making the undocumented instruction an alias.</p> <p>The 8086 has several internal registers that are invisible to the programmer but are used by the microcode. Memory accesses use the Indirect (<code>IND</code>) and Operand (<code>OPR</code>) registers; the <code>IND</code> register holds the address in the segment, while the <code>OPR</code> register holds the data value that is read or written. Although these registers are normally not accessible by the programmer, some undocumented instructions provide access to these registers, as will be described later.</p> <p>The Arithmetic/Logic Unit (ALU) performs arithmetic, logical, and shift operations in the 8086. The ALU uses three internal registers: <code>tmpA</code>, <code>tmpB</code>, and <code>tmpC</code>. An ALU operation requires two micro-instructions. The first micro-instruction specifies the operation (such as <code>ADD</code>) and the temporary register that holds one argument (e.g. <code>tmpA</code>); the second argument is always in <code>tmpB</code>. A following micro-instruction can access the ALU result through the pseudo-register <code>危</code> (sigma).</p> <h3>The ModR/M byte</h3> <p>A fundamental part of the 8086 instruction format is the ModR/M byte, a byte that specifies addressing for many instructions. The 8086 has a variety of addressing modes, so the ModR/M byte is somewhat complicated. Normally it specifies one memory address and one register. The memory address is specified through one of eight addressing modes (below) along with an optional 8- or 16-bit displacement in the instruction. Instead of a memory address, the ModR/M byte can also specify a second register. For a few opcodes, the ModR/M byte selects what instruction to execute rather than a register.</p> <p><a href="https://static.righto.com/images/8086-ad-undoc/modrm.png"><img alt="The 8086's addressing modes. From The register assignments, from MCS-86 Assembly Language Reference Guide." class="hilite" height="220" src="https://static.righto.com/images/8086-ad-undoc/modrm-w250.png" title="The 8086's addressing modes. From The register assignments, from MCS-86 Assembly Language Reference Guide." width="250" /></a><div class="cite">The 8086's addressing modes. From <a href="http://bitsavers.org/components/intel/8086/9800749-1_MCS-86_Assembly_Language_Reference_Guide_Oct78.pdf">The register assignments, from MCS-86 Assembly Language Reference Guide</a>.</div></p> <p>The implementation of the ModR/M byte plays an important role in the behavior of undocumented instructions. Support for this byte is implemented in both microcode and hardware. The various memory address modes above are implemented by microcode subroutines, which compute the appropriate memory address and perform a read if necessary. The subroutine leaves the memory address in the <code>IND</code> register, and if a read is performed, the value is in the <code>OPR</code> register.</p> <p>The hardware hides the ModR/M byte's selection of memory versus register, by making the value available through the pseudo-register <code>M</code>, while the second register is available through <code>N</code>. Thus, the microcode for an instruction doesn't need to know if the value was in memory or a register, or which register was selected. The Group Decode ROM examines the first byte of the instruction to determine if a ModR/M byte is present, and if a read is required. If the ModR/M byte specifies memory, the Translation ROM determines which micro-subroutines to call before handling the instruction itself. For more on the ModR/M byte, see my post on <a href="https://www.righto.com/2023/02/8086-modrm-addressing.html">Reverse-engineering the ModR/M addressing microcode</a>.</p> <h2>Holes in the opcode table</h2> <p>The first byte of the instruction is a value from <code>00</code> to <code>FF</code> in hex. Almost all of these opcode values correspond to documented 8086 instructions, but there are a few exceptions, "holes" in the opcode table. The table below shows the 256 first-byte opcodes for the 8086, from hex <code>00</code> to <code>FF</code>. Valid opcodes for the 8086 are in white; the colored opcodes are undefined and interesting to examine. Orange, yellow, and green opcodes were given meaning in the 80186, 80286, and 80386 respectively. The purple opcode is unusual: it was implemented in the 8086 and later processors but not documented.<span id="fnref:prefixes"><a class="ref" href="#fn:prefixes">2</a></span> In this section, I'll examine the microcode for these opcode holes.</p> <p><a href="https://static.righto.com/images/8086-ad-undoc/opcodes.png"><img alt="This table shows the 256 opcodes for the 8086, where the white ones are valid instructions. Click for a larger version." class="hilite" height="453" src="https://static.righto.com/images/8086-ad-undoc/opcodes-w450.png" title="This table shows the 256 opcodes for the 8086, where the white ones are valid instructions. Click for a larger version." width="450" /></a><div class="cite">This table shows the 256 opcodes for the 8086, where the white ones are valid instructions. Click for a larger version.</div></p> <h3><code>D6</code>: <code>SALC</code></h3> <p>The opcode <code>D6</code> (purple above) performs a well-known but undocumented operation that is typically called <code>SALC</code>, for Set AL to Carry. This instruction sets the <code>AL</code> register to 0 if the carry flag is 0, and sets the <code>AL</code> register to <code>FF</code> if the carry flag is 1. The curious thing about this undocumented instruction is that it exists in all x86 CPUs, but Intel didn't mention it until 2017. Intel probably put this instruction into the processor deliberately as a <a href="https://en.wikipedia.org/wiki/Fictitious_entry#Copyright_traps">copyright trap</a>. The idea is that if a company created a copy of the 8086 processor and the processor included the <code>SALC</code> instruction, this would prove that the company had copied Intel's microcode and thus had potentially violated Intel's copyright on the microcode. This came to light when NEC created improved versions of the 8086, the NEC V20 and V30 microprocessors, and was sued by Intel. Intel analyzed NEC's microcode but was disappointed to find that NEC's chip did not include the hidden instruction, showing that NEC hadn't copied the microcode.<span id="fnref:magic-instruction"><a class="ref" href="#fn:magic-instruction">3</a></span> Although a Federal judge <a href="https://www.nytimes.com/1989/02/08/business/intel-loses-copyright-case-to-nec.html">ruled</a> in 1989 that NEC hadn't infringed Intel's copyright, the 5-year trial ruined NEC's market momentum.</p> <p>The <code>SALC</code> instruction is implemented with three micro-instructions, shown below.<span id="fnref:microcode"><a class="ref" href="#fn:microcode">4</a></span> The first micro-instruction jumps if the carry (<code>CY</code>) is set. If not, the next instruction moves 0 to the AL register. <code>RNI</code> (Run Next Instruction) ends the microcode execution causing the next machine instruction to run. If the carry was set, all-ones (i.e. <code>FF</code> hex) is moved to the <code>AL</code> register and RNI ends the microcode sequence.</p> <pre class="microcode"> JMPS CY 2 <span><b>SALC</b>: jump on carry</span> ZERO → AL RNI <span>Move 0 to AL, run next instruction</span> ONES → AL RNI <span><b>2:</b>Move FF to AL, run next instruction</span> </pre> <h3><code>0F</code>: <code>POP CS</code></h3> <p>The <code>0F</code> opcode is the first hole in the opcode table. The 8086 has instructions to push and pop the four segment registers, except opcode <code>0F</code> is undefined where <code>POP CS</code> should be. This opcode performs <code>POP CS</code> successfully, so the question is why is it undefined? The reason is that <code>POP CS</code> is essentially useless and doesn't do what you'd expect, so Intel figured it was best not to document it.</p> <p>To understand why <code>POP CS</code> is useless, I need to step back and explain the 8086's segment registers. The 8086 has a <code>20</code>-bit address space, but 16-bit registers. To make this work, the 8086 has the concept of segments: memory is accessed in 64K chunks called segments, which are positioned in the 1-megabyte address space. Specifically, there are four segments: Code Segment, Stack Segment, Data Segment, and Extra Segment, with four segment registers that define the start of the segment: <code>CS</code>, <code>SS</code>, <code>DS</code>, and <code>ES</code>.</p> <p>An inconvenient part of segment addressing is that if you want to access more than 64K, you need to change the segment register. So you might push the data segment register, change it temporarily so you can access a new part of memory, and then pop the old data segment register value off the stack. This would use the <code>PUSH DS</code> and <code>POP DS</code> instructions. But why not <code>POP CS</code>?</p> <p>The 8086 executes code from the code segment, with the instruction pointer (<code>IP</code>) tracking the location in the code segment. The main problem with <code>POP CS</code> is that it changes the code segment, but not the instruction pointer, so now you are executing code at the old offset in a new segment. Unless you line up your code extremely carefully, the result is that you're jumping to an unexpected place in memory. (Normally, you want to change <code>CS</code> and the instruction pointer at the same time, using a <code>CALL</code> or <code>JMP</code> instruction.)</p> <p>The second problem with <code>POP CS</code> is prefetching. For efficiency, the 8086 prefetches instructions before they are needed, storing them in an 6-byte prefetch queue. When you perform a jump, for instance, the microcode flushes the prefetch queue so execution will continue with the new instructions, rather than the old instructions. However, the instructions that pop a segment register don't flush the prefetch buffer. Thus, <code>POP CS</code> not only jumps to an unexpected location in memory, but it will execute an unpredictable number of instructions from the old code path.</p> <p>The <code>POP segment register</code> microcode below packs a lot into three micro-instructions. The first micro-instruction pops a value from the stack. Specifically, it moves the stack pointer (<code>SP</code>) to the Indirect (<code>IND</code>) register. The Indirect register is an internal register, invisible to the programmer, that holds the address offset for memory accesses. The first micro-instruction also performs a memory read (<code>R</code>) from the stack segment (<code>SS</code>) and then increments <code>IND</code> by 2 (<code>P2</code>, plus 2). The second micro-instruction moves <code>IND</code> to the stack pointer, updating the stack pointer with the new value. It also tells the microcode engine that this micro-instruction is the next-to-last (<code>NXT</code>) and the next machine instruction can be started. The final micro-instruction moves the value read from memory to the appropriate segment register and runs the next instruction. Specifically, reads and writes put data in the internal <code>OPR</code> (Operand) register. The hardware uses the register <code>N</code> to indicate the register specified by the instruction. That is, the value will be stored in the <code>CS</code>, <code>DS</code>, <code>ES</code>, or <code>SS</code> register, depending on the bit pattern in the instruction. Thus, the same microcode works for all four segment registers. This is why <code>POP CS</code> works even though <code>POP CS</code> wasn't explicitly implemented in the microcode; it uses the common code.</p> <pre class="microcode"> SP → IND R SS,P2 <span><b>POP sr</b>: read from stack, compute IND plus 2</span> IND → SP NXT <span>Put updated value in SP, start next instruction.</span> OPR → N RNI <span>Put stack value in specified segment register</span> </pre> <p>But why does <code>POP CS</code> run this microcode in the first place? The microcode to execute is selected based on the instruction, but multiple instructions can execute the same microcode. You can think of the address decoder as pattern-matching on the instruction's bit patterns, where some of the bits can be ignored. In this case, the <code>POP sr</code> microcode above is run by any instruction with the bit pattern 000??111, where a question mark can be either a 0 or a 1. You can verify that this pattern matches <code>POP ES</code> (<code>07</code>), <code>POP SS</code> (<code>17</code>), and <code>POP DS</code> (<code>1F</code>). However, it also matches <code>0F</code>, which is why the <code>0F</code> opcode runs the above microcode and performs <code>POP CS</code>. In other words, to make <code>0F</code> do something other than <code>POP CS</code> would require additional circuitry, so it was easier to leave the action implemented but undocumented.</p> <h3><code>60</code>-<code>6F</code>: conditional jumps</h3> <p>One whole row of the opcode table is unused: values <code>60</code> to <code>6F</code>. These opcodes simply act the same as <code>70</code> to <code>7F</code>, the conditional jump instructions.</p> <p>The conditional jumps use the following microcode. It fetches the jump offset from the instruction prefetch queue (<code>Q</code>) and puts the value into the ALU's <code>tmpBL</code> register, the low byte of the <code>tmpB</code> register. It tests the condition in the instruction (<code>XC</code>) and jumps to the <code>RELJMP</code> micro-subroutine if satisfied. The <code>RELJMP</code> code (not shown) updates the program counter to perform the jump.</p> <pre class="microcode"> Q → tmpBL <span><b>Jcond cb:</b> Get offset from prefetch queue</span> JMP XC RELJMP <span>Test condition, if true jump to RELJMP routine</span> RNI <span>No jump: run next instruction</span> </pre> <p>This code is executed for any instruction matching the bit pattern <code>011?????</code>, i.e. anything from <code>60</code> to <code>7F</code>. The condition is specified by the four low bits of the instruction. The result is that any instruction <code>60</code>-<code>6F</code> is an alias for the corresponding conditional jump <code>70</code>-<code>7F</code>.</p> <h3><code>C0</code>, <code>C8</code>: <code>RET/RETF imm</code></h3> <p>These undocumented opcodes act like a return instruction, specifically <code>RET imm16</code> (<a href="https://www.os2museum.com/wp/undocumented-8086-opcodes-part-i/">source</a>). Specifically, the instruction <code>C0</code> is the same as <code>C2</code>, near return, while <code>C8</code> is the same as <code>CA</code>, far return.</p> <p>The microcode below is executed for the instruction bits <code>1100?0?0</code>, so it is executed for <code>C0</code>, <code>C2</code>, <code>C8</code>, and <code>CA</code>. It gets two bytes from the instruction prefetch queue (<code>Q</code>) and puts them in the <code>tmpA</code> register. Next, it calls <code>FARRET</code>, which performs either a near return (popping <code>PC</code> from the stack) or a far return (popping <code>PC</code> and <code>CS</code> from the stack). Finally, it adds the original argument to the <code>SP</code>, equivalent to popping that many bytes.</p> <pre class="microcode"> Q → tmpAL ADD tmpA <span><b>RET/RETF iw:</b> Get word from prefetch, set up ADD</span> Q → tmpAH CALL FARRET <span>Call Far Return micro-subroutine</span> IND → tmpB <span>Move SP (in IND) to tmpB for ADD</span> 危 → SP RNI <span>Put sum in Stack Pointer, end</span> </pre> <p>One tricky part is that the <code>FARRET</code> micro-subroutine examines bit 3 of the instruction to determine whether it does a near return or a far return. This is why documented instruction <code>C2</code> is a near return and <code>CA</code> is a far return. Since <code>C0</code> and <code>C8</code> run the same microcode, they will perform the same actions, a near return and a far return respectively.</p> <h3><code>C1</code>: <code>RET</code></h3> <p>The undocumented <code>C1</code> opcode is identical to the documented <code>C3</code>, near return instruction. The microcode below is executed for instruction bits <code>110000?1</code>, i.e. <code>C1</code> and <code>C3</code>. The first micro-instruction reads from the Stack Pointer, incrementing <code>IND</code> by 2. Prefetching is suspended and the prefetch queue is flushed, since execution will continue at a new location. The Program Counter is updated with the value from the stack, read into the <code>OPR</code> register. Finally, the updated address is put in the Stack Pointer and execution ends.</p> <pre class="microcode"> SP → IND R SS,P2 <span><b>RET: </b> Read from stack, increment by 2</span> SUSP <span>Suspend prefetching</span> OPR → PC FLUSH <span>Update PC from stack, flush prefetch queue</span> IND → SP RNI <span>Update SP, run next instruction</span> </pre> <h3><code>C9</code>: <code>RET</code></h3> <p>The undocumented <code>C9</code> opcode is identical to the documented <code>CB</code>, far return instruction. This microcode is executed for instruction bits <code>110010?1</code>, i.e. <code>C9</code> and <code>CB</code>, so <code>C9</code> is identical to <code>CB</code>. The microcode below simply calls the <code>FARRET</code> micro-subroutine to pop the Program Counter and CS register. Then the new value is stored into the Stack Pointer. One subtlety is that <code>FARRET</code> looks at bit 3 of the instruction to switch between a near return and a far return, as described earlier. Since <code>C9</code> and <code>CB</code> both have bit 3 set, they both perform a far return.</p> <pre class="microcode"> CALL FARRET <span><b>RETF:</b> call FARRET routine</span> IND → SP RNI <span>Update stack pointer, run next instruction</span> </pre> <h3><code>F1</code>: <code>LOCK</code> prefix</h3> <p>The final hole in the opcode table is <code>F1</code>. This opcode is different because it is implemented in logic rather than microcode. The Group Decode ROM indicates that <code>F1</code> is a prefix, one-byte logic, and LOCK. The Group Decode outputs are the same as <code>F0</code>, so <code>F1</code> also acts as a <code>LOCK</code> prefix.</p> <h2>Holes in two-byte opcodes</h2> <p>For most of the 8086 instructions, the first byte specifies the instruction. However, the 8086 has a few instructions where the second byte specifies the instruction: the <code>reg</code> field of the ModR/M byte provides an opcode extension that selects the instruction.<span id="fnref:extension"><a class="ref" href="#fn:extension">5</a></span> These fall into four categories which Intel labeled "Immed", "Shift", "Group 1", and "Group 2", corresponding to opcodes <code>80</code>-<code>83</code>, <code>D0</code>-<code>D3</code>, <code>F6</code>-<code>F7</code>, and <code>FE</code>-<code>FF</code>. The table below shows how the second byte selects the instruction. Note that "Shift", "Group 1", and "Group 2" all have gaps, resulting in undocumented values.</p> <p><a href="https://static.righto.com/images/8086-ad-undoc/groups.jpg"><img alt="Meaning of the reg field in two-byte opcodes. From MCS-86 Assembly Language Reference Guide." class="hilite" height="129" src="https://static.righto.com/images/8086-ad-undoc/groups-w600.jpg" title="Meaning of the reg field in two-byte opcodes. From MCS-86 Assembly Language Reference Guide." width="600" /></a><div class="cite">Meaning of the reg field in two-byte opcodes. From <a href="http://bitsavers.org/components/intel/8086/9800749-1_MCS-86_Assembly_Language_Reference_Guide_Oct78.pdf">MCS-86 Assembly Language Reference Guide</a>.</div></p> <p>These sets of instructions are implemented in two completely different ways. The "Immed" and "Shift" instructions run microcode in the standard way, selected by the first byte. For a typical arithmetic/logic instruction such as <code>ADD</code>, bits 5-3 of the first instruction byte are latched into the <code>X</code> register to indicate which ALU operation to perform. The microcode specifies a generic ALU operation, while the <code>X</code> register controls whether the operation is an <code>ADD</code>, <code>SUB</code>, <code>XOR</code>, or so forth. However, the Group Decode ROM indicates that for the special "Immed" and "Shift" instructions, the <code>X</code> register latches the bits from the <em>second</em> byte. Thus, when the microcode executes a generic ALU operation, it ends up with the one specified in the second byte.<span id="fnref:alu"><a class="ref" href="#fn:alu">6</a></span></p> <p>The "Group 1" and "Group 2" instructions (<code>F0</code>-<code>F1</code>, <code>FE</code>-<code>FF</code>), however, run different microcode for each instruction. Bits 5-3 of the second byte replace bits 2-0 of the instruction before executing the microcode. Thus, <code>F0</code> and <code>F1</code> act as if they are opcodes in the range <code>F0</code>-<code>F7</code>, while <code>FE</code> and <code>FF</code> act as if they are opcodes in the range <code>F8</code>-<code>FF</code>. Thus, each instruction specified by the second byte can have its own microcode, unlike the "Immed" and "Shift" instructions. The trick that makes this work is that all the "real" opcodes in the range <code>F0</code>-<code>FF</code> are implemented in logic, not microcode, so there are no collisions.</p> <h3>The hole in "Shift": <code>SETMO</code>, <code>D0</code>..<code>D3/6</code></h3> <p>There is a "hole" in the list of shift operations when the second byte has the bits <code>110</code> (6). (This is typically expressed as <code>D0/6</code> and so forth; the value after the slash is the opcode-selection bits in the ModR/M byte.) Internally, this value selects the ALU's <code>SETMO</code> (Set Minus One) operation, which simply returns <code>FF</code> or <code>FFFF</code>, for a byte or word operation respectively.<span id="fnref:setmo"><a class="ref" href="#fn:setmo">7</a></span></p> <p>The microcode below is executed for 1101000? bit patterns patterns (D0 and D1). The first instruction gets the value from the <code>M</code> register and sets up the ALU to do whatever operation was specified in the instruction (indicated by <code>XI</code>). Thus, the same microcode is used for all the "Shift" instructions, including <code>SETMO</code>. The result is written back to <code>M</code>. If no writeback to memory is required (<code>NWB</code>), then <code>RNI</code> runs the next instruction, ending the microcode sequence. However, if the result is going to memory, then the last line writes the value to memory.</p> <pre class="microcode"> M → tmpB XI tmpB, NXT <span><b>rot rm, 1</b>: get argument, set up ALU</span> 危 → M NWB,RNI F <span>Store result, maybe run next instruction</span> W DS,P0 RNI <span>Write result to memory</span> </pre> <p>The D2 and D3 instructions (1101001?) perform a variable number of shifts, specified by the <code>CL</code> register, so they use different microcode (below). This microcode loops the number of times specified by <code>CL</code>, but the control flow is a bit tricky to avoid shifting if the intial counter value is 0. The code sets up the ALU to pass the counter (in <code>tmpA</code>) unmodified the first time (<code>PASS</code>) and jumps to <b>4</b>, which updates the counter and sets up the ALU for the shift operation (<code>XI</code>). If the counter is not zero, it jumps back to <b>3</b>, which performs the previously-specified shift and sets up the ALU to decrement the counter (<code>DEC</code>). This time, the code at <b>4</b> decrements the counter. The loop continues until the counter reaches zero. The microcode stores the result as in the previous microcode.</p> <pre class="microcode"> ZERO → tmpA <span><b>rot rm,CL</b>: 0 to tmpA</span> CX → tmpAL PASS tmpA <span>Get count to tmpAL, set up ALU to pass through</span> M → tmpB JMPS 4 <span>Get value, jump to loop (4)</span> 危 → tmpB DEC tmpA F <span><b>3</b>: Update result, set up decrement of count</span> 危 → tmpA XI tmpB <span><b>4</b>: update count in tmpA, set up ALU</span> JMPS NZ 3 <span>Loop if count not zero</span> tmpB → M NWB,RNI <span>Store result, maybe run next instruction</span> W DS,P0 RNI <span>Write result to memory</span> </pre> <h3>The hole in "group 1": <code>TEST</code>, <code>F6/1</code> and <code>F7/1</code></h3> <p>The <code>F6</code> and <code>F7</code> opcodes are in "group 1", with the specific instruction specified by bits 5-3 of the second byte. The second-byte table showed a hole for the <code>001</code> bit sequence. As explained earlier, these bits replace the low-order bits of the instruction, so <code>F6</code> with 001 is processed as if it were the opcode <code>F1</code>. The microcode below matches against instruction bits <code>1111000?</code>, so <code>F6/1</code> and <code>F7/1</code> have the same effect as <code>F6/0</code> and <code>F7/1</code> respectively, that is, the byte and word <code>TEST</code> instructions.</p> <p>The microcode below gets one or two bytes from the prefetch queue (<code>Q</code>); the <code>L8</code> condition tests if the operation is an 8-bit (i.e. byte) operation and skips the second micro-instruction. The third micro-instruction ANDs the argument and the fetched value. The condition flags (<code>F</code>) are set based on the result, but the result itself is discarded. Thus, the <code>TEST</code> instruction tests a value against a mask, seeing if any bits are set.</p> <pre class="microcode"> Q → tmpBL JMPS L8 2 <span><b>TEST rm,i:</b> Get byte, jump if operation length = 8</span> Q → tmpBH <span>Get second byte from the prefetch queue</span> M → tmpA AND tmpA, NXT <span><b>2:</b> Get argument, AND with fetched value</span> 危 → no dest RNI F <span>Discard result but set flags.</span> </pre> <p>I explained the processing of these "Group 3" instructions in more detail in my <a href="https://www.righto.com/2022/11/how-8086-processors-microcode-engine.html">microcode article</a>.</p> <h3>The hole in "group 2": <code>PUSH</code>, <code>FE/7</code> and <code>FF/7</code></h3> <p>The <code>FE</code> and <code>FF</code> opcodes are in "group 2", which has a hole for the <code>111</code> bit sequence in the second byte. After replacement, this will be processed as the <code>FF</code> opcode, which matches the pattern <code>1111111?</code>. In other words, the instruction will be processed the same as the <code>110</code> bit pattern, which is <code>PUSH</code>. The microcode gets the Stack Pointer, sets up the ALU to decrement it by 2. The new value is written to <code>SP</code> and <code>IND</code>. Finally, the register value is written to stack memory.</p> <!-- For some reason, the [8086 undocumented instructions](https://en.wikipedia.org/wiki/X86_instruction_listings#Undocumented_x86_instructions) page on Wikipedia doesn't list FE-FF. --> <pre class="microcode"> SP → tmpA DEC2 tmpA <span><b>PUSH rm</b>: set up decrement SP by 2</span> 危 → IND <span>Decremented SP to IND</span> 危 → SP <span>Decremented SP to SP</span> M → OPR W SS,P0 RNI <span>Write the value to memory, done</span> </pre> <h3><code>82</code> and <code>83</code> "Immed" group</h3> <p>Opcodes <code>80</code>-<code>83</code> are the "Immed" group, performing one of eight arithmetic operations, specified in the ModR/M byte. The four opcodes differ in the size of the values: opcode <code>80</code> applies an 8-bit immediate value to an 8-bit register, <code>81</code> applies a 16-bit value to a 16-bit register, <code>82</code> applies an 8-bit value to an 8-bit register, and <code>83</code> applies an 8-bit value to a 16-bit register. The opcode 82 has the strange situation that <a href="https://en.wikipedia.org/wiki/X86_instruction_listings#Undocumented_instructions_that_are_widely_available_across_many_x86_CPUs_include">some sources</a> say it is undocumented, but it shows up in some Intel documentation as a valid bit combination (e.g. below). Note that <code>80</code> and <code>82</code> have the 8-bit to 8-bit action, so the <code>82</code> opcode is redundant.</p> <p><a href="https://static.righto.com/images/8086-ad-undoc/adc.png"><img alt="ADC is one of the instructions with opcode 80-83. From the 8086 datasheet, page 27." class="hilite" height="34" src="https://static.righto.com/images/8086-ad-undoc/adc-w600.png" title="ADC is one of the instructions with opcode 80-83. From the 8086 datasheet, page 27." width="600" /></a><div class="cite">ADC is one of the instructions with opcode 80-83. From the <a href="https://www.electro-tech-online.com/datasheets/8086_intel.pdf">8086 datasheet</a>, page 27.</div></p> <p>The microcode below is used for all four opcodes. If the ModR/M byte specifies memory, the appropriate micro-subroutine is called to compute the effective address in <code>IND</code>, and fetch the byte or word into <code>OPR</code>. The first two instructions below get the two immediate data bytes from the prefetch queue; for an 8-bit operation, the second byte is skipped. Next, the second argument <code>M</code> is loaded into tmpA and the desired ALU operation (<code>XI</code>) is configured. The result <code>危</code> is stored into the specified register <code>M</code> and the operation may terminate with <code>RNI</code>. But if the ModR/M byte specified memory, the following write micro-operation saves the value to memory.</p> <pre class="microcode"> Q → tmpBL JMPS L8 2 <span><b>alu rm,i</b>: get byte, test if 8-bit op</span> Q → tmpBH <span>Maybe get second byte</span> M → tmpA XI tmpA, NXT <span><b>2</b>: </span> 危 → M NWB,RNI F <span>Save result, update flags, done if no memory writeback</span> W DS,P0 RNI <span>Write result to memory if needed</span> </pre> <p>The tricky part of this is the <code>L8</code> condition, which tests if the operation is 8-bit. You might think that bit 0 acts as the byte/word bit in a nice, orthogonal way, but the 8086 has a bunch of special cases. Bit 0 of the instruction typically selects between a byte and a word operation, but there are a bunch of special cases. The Group Decode ROM creates a signal indicating if bit 0 should be used as the byte/word bit. But it generates a second signal indicating that an instruction should be forced to operate on bytes, for instructions such as <code>DAA</code> and <code>XLAT</code>. Another Group Decode ROM signal indicates that bit 3 of the instruction should select byte or word; this is used for the <code>MOV</code> instructions with opcodes Bx. Yet another Group Decode ROM signal indicates that inverted bit 1 of the instruction should select byte or word; this is used for a few opcodes, including <code>80</code>-<code>87</code>.</p> <p>The important thing here is that for the opcodes under discussion (<code>80</code>-<code>83</code>), the <code>L8</code> micro-condition uses <em>both</em> bits 0 and 1 to determine if the instruction is 8 bits or not. The result is that only opcode <code>81</code> is considered 16-bit by the <code>L8</code> test, so it is the only one that uses two immediate bytes from the instruction. However, the register operations use only bit 0 to select a byte or word transfer. The result is that opcode <code>83</code> has the unusual behavior of using an 8-bit immediate operand with a 16-bit register. In this case, the 8-bit value is sign-extended to form a 16-bit value. That is, the top bit of the 8-bit value fills the entire upper half of the 16-bit value, converting an 8-bit signed value to a 16-bit signed value (e.g. -1 is <code>FF</code>, which becomes <code>FFFF</code>). This makes sense for arithmetic operations, but not much sense for logical operations.</p> <p>Intel documentation is inconsistent about which opcodes are listed for which instructions. Intel opcode maps generally define opcodes <code>80</code>-<code>83</code>. However, lists of specific instructions show opcodes <code>80</code>, <code>81</code>, and <code>83</code> for arithmetic operations but only <code>80</code> and <code>81</code> for logical operations.<span id="fnref:immed"><a class="ref" href="#fn:immed">8</a></span> That is, Intel omits the redundant <code>82</code> opcode as well as omitting logic operations that perform sign-extension (<code>83</code>).</p> <h3>More <code>FE</code> holes</h3> <p>For the "group 2" instructions, the <code>FE</code> opcode performs a byte operation while <code>FF</code> performs a word operation. Many of these operations don't make sense for bytes: <code>CALL</code>, <code>JMP</code>, and <code>PUSH</code>. (The only instructions supported for <code>FE</code> are <code>INC</code> and <code>DEC</code>.) But what happens if you use the unsupported instructions? The remainder of this section examines those cases and shows that the results are not useful.</p> <h4><code>CALL</code>: <code>FE/2</code></h4> <p>This instruction performs an indirect subroutine call within a segment, reading the target address from the memory location specified by the ModR/M byte.</p> <p>The microcode below is a bit convoluted because the code falls through into the shared <code>NEARCALL</code> routine, so there is some unnecessary register movement. Before this microcode executes, the appropriate ModR/M micro-subroutine will read the target address from memory. The code below copies the destination address from <code>M</code> to <code>tmpB</code> and stores it into the PC later in the code to transfer execution. The code suspends prefetching, corrects the PC to cancel the offset from prefetching, and flushes the prefetch queue. Finally, it decrements the SP by two and writes the old PC to the stack.</p> <pre class="microcode"> M → tmpB SUSP <span><b>CALL rm</b>: read value, suspend prefetch</span> SP → IND CORR <span>Get SP, correct PC</span> PC → OPR DEC2 tmpC <span>Get PC to write, set up decrement</span> tmpB → PC FLUSH <span><b>NEARCALL</b>: Update PC, flush prefetch</span> IND → tmpC <span>Get SP to decrement</span> 危 → IND <span>Decremented SP to IND</span> 危 → SP W SS,P0 RNI <span>Update SP, write old PC to stack</span> </pre> <p>This code will mess up in two ways when executed as a byte instruction. First, when the destination address is read from memory, only a byte will be read, so the destination address will be corrupted. (I think that the behavior here depends on the bus hardware. The 8086 will ask for a byte from memory but will read the word that is placed on the bus. Thus, if memory returns a word, this part may operate correctly. The 8088's behavior will be different because of its 8-bit bus.) The second issue is writing the old PC to the stack because only a byte of the PC will be written. Thus, when the code returns from the subroutine call, the return address will be corrupt.</p> <h4><code>CALL</code>: <code>FE/3</code></h4> <p>This instruction performs an indirect subroutine call between segments, reading the target address from the memory location specified by the ModR/M byte.</p> <pre class="microcode"> IND → tmpC INC2 tmpC <span><b>CALL FAR rm</b>: set up IND+2</span> 危 → IND R DS,P0 <span>Read new CS, update IND</span> OPR → tmpA DEC2 tmpC <span>New CS to tmpA, set up SP-2</span> SP → tmpC SUSP <span><b>FARCALL</b>: Suspend prefetch</span> 危 → IND CORR <span><b>FARCALL2</b>: Update IND, correct PC</span> CS → OPR W SS,M2 <span>Push old CS, decrement IND by 2</span> tmpA → CS PASS tmpC <span>Update CS, set up for NEARCALL</span> PC → OPR JMP NEARCALL <span>Continue with NEARCALL</span> </pre> <p>As in the previous <code>CALL</code>, this microcode will fail in multiple ways when executed in byte mode. The new CS and PC addresses will be read from memory as bytes, which may or may not work. Only a byte of the old CS and PC will be pushed to the stack.</p> <h4><code>JMP</code>: <code>FE/4</code></h4> <p>This instruction performs an indirect jump within a segment, reading the target address from the memory location specified by the ModR/M byte. The microcode is short, since the ModR/M micro-subroutine does most of the work. I believe this will have the same problem as the previous <code>CALL</code> instructions, that it will attempt to read a byte from memory instead of a word.</p> <pre class="microcode"> SUSP <span><b>JMP rm</b>: Suspend prefetch</span> M → PC FLUSH RNI <span>Update PC with new address, flush prefetch, done</span> </pre> <h4><code>JMP</code>: <code>FE/5</code></h4> <p>This instruction performs an indirect jump between segments, reading the new PC and CS values from the memory location specified by the ModR/M byte. The ModR/M micro-subroutine reads the new PC address. This microcode increments <code>IND</code> and suspends prefetching. It updates the PC, reads the new CS value from memory, and updates the CS. As before, the reads from memory will read bytes instead of words, so this code will not meaningfully work in byte mode.</p> <pre class="microcode"> IND → tmpC INC2 tmpC <span><b>JMP FAR rm</b>: set up IND+2</span> 危 → IND SUSP <span>Update IND, suspend prefetch</span> tmpB → PC R DS,P0 <span>Update PC, read new CS from memory</span> OPR → CS FLUSH RNI <span>Update CS, flush prefetch, done</span> </pre> <h4><code>PUSH</code>: <code>FE/6</code></h4> <p>This instruction pushes the register or memory value specified by the ModR/M byte. It decrements the SP by 2 and then writes the value to the stack. It will write one byte to the stack but decrements the SP by 2, so one byte of old stack data will be on the stack along with the data byte.</p> <pre class="microcode"> SP → tmpA DEC2 tmpA <span><b>PUSH rm</b>: Set up SP decrement </span> 危 → IND <span>Decremented value to IND</span> 危 → SP <span>Decremented value to SP</span> M → OPR W SS,P0 RNI <span>Write the data to the stack</span> </pre> <h2>Undocumented instruction values</h2> <p>The next category of undocumented instructions is where the first byte indicates a valid instruction, but there is something wrong with the second byte.</p> <h3><code>AAM</code>: ASCII Adjust after Multiply</h3> <p>The <code>AAM</code> instruction is a fairly obscure one, designed to support binary-coded decimal arithmetic (BCD). After multiplying two BCD digits, you end up with a binary value between 0 and <code>81</code> (0×0 to 9×9). If you want a BCD result, the <code>AAM</code> instruction converts this binary value to BCD, for instance splitting <code>81</code> into the decimal digits 8 and 1, where the upper digit is <code>81</code> divided by <code>10</code>, and the lower digit is <code>81</code> modulo <code>10</code>.</p> <p>The interesting thing about <code>AAM</code> is that the 2-byte instruction is <code>D4</code> <code>0A</code>. You might notice that hex <code>0A</code> is <code>10</code>, and this is not a coincidence. There wasn't an easy way to get the value <code>10</code> in the microcode, so instead they made the instruction provide that value in the second byte. The undocumented (but well-known) part is that if you provide a value other than <code>10</code>, the instruction will convert the binary input into digits in that base. For example, if you provide 8 as the second byte, the instruction returns the value divided by 8 and the value modulo 8.</p> <p>The microcode for <code>AAM</code>, below, sets up the registers. calls the <code>CORD</code> (Core Division) micro-subroutine to perform the division, and then puts the results into <code>AH</code> and <code>AL</code>. In more detail, the <code>CORD</code> routine divides <code>tmpA/tmpC</code> by <code>tmpB</code>, putting the <em>complement</em> of the quotient in <code>tmpC</code> and leaving the remainder in <code>tmpA</code>. (If you want to know how CORD works internally, see my <a href="https://www.righto.com/2023/04/reverse-engineering-8086-divide-microcode.html">division post</a>.) The important step is that the <code>AAM</code> microcode gets the divisor from the prefetch queue (<code>Q</code>). After calling <code>CORD</code>, it sets up the ALU to perform a 1's complement of <code>tmpC</code> and puts the result (<code>危</code>) into <code>AH</code>. It sets up the ALU to pass <code>tmpA</code> through unchanged, puts the result (<code>危</code>) into <code>AL</code>, and updates the flags accordingly (<code>F</code>).</p> <pre class="microcode"> Q → tmpB <span><b>AAM:</b> Move byte from prefetch to tmpB</span> ZERO → tmpA <span>Move 0 to tmpA</span> AL → tmpC CALL CORD <span>Move AL to tmpC, call CORD.</span> COM1 tmpC <span>Set ALU to complement</span> 危 → AH PASS tmpA, NXT <span>Complement AL to AH</span> 危 → AL RNI F <span>Pass tmpA through ALU to set flags</span> </pre> <p>The interesting thing is why this code has undocumented behavior. The 8086's microcode only has support for the constants 0 and all-1's (<code>FF</code> or <code>FFFF</code>), but the microcode needs to divide by <code>10</code>. One solution would be to implement an additional micro-instruction and more circuitry to provide the constant <code>10</code>, but every transistor was precious back then. Instead, the designers took the approach of simply putting the number <code>10</code> as the second byte of the instruction and loading the constant from there. Since the <code>AAM</code> instruction is not used very much, making the instruction two bytes long wasn't much of a drawback. But if you put a different number in the second byte, that's the divisor the microcode will use. (Of course you could add circuitry to verify that the number is <code>10</code>, but then the implementation is no longer simple.)</p> <p>Intel could have documented the full behavior, but that creates several problems. First, Intel would be stuck supporting the full behavior into the future. Second, there are corner cases to deal with, such as divide-by-zero. Third, testing the chip would become harder because all these cases would need to be tested. Fourth, the documentation would become long and confusing. It's not surprising that Intel left the full behavior undocumented.</p> <h3><code>AAD</code>: ASCII Adjust before Division</h3> <p>The <code>AAD</code> instruction is analogous to <code>AAM</code> but used for BCD division. In this case, you want to divide a two-digit BCD number by something, where the BCD digits are in <code>AH</code> and <code>AL</code>. The <code>AAD</code> instruction converts the two-digit BCD number to binary by computing <code>AH</code>×<code>10+AL</code>, before you perform the division.</p> <p>The microcode for <code>AAD</code> is shown below. The microcode sets up the registers, calls the multiplication micro-subroutine <code>CORX</code> (Core Times), and then puts the results in <code>AH</code> and <code>AL</code>. In more detail, the multiplier comes from the instruction prefetch queue <code>Q</code>. The <code>CORX</code> routine multiples <code>tmpC</code> by <code>tmpB</code>, putting the result in <code>tmpA/tmpC</code>. Then the microcode adds the low BCD digit (<code>AL</code>) to the product (<code>tmpB + tmpC</code>), putting the sum (<code>危</code>) into <code>AL</code>, clearing <code>AH</code> and setting the status flags <code>F</code> appropriately.</p> <p>One interesting thing is that the second-last micro-instruction jumps to <code>AAEND</code>, which is the last micro-instruction of the <code>AAM</code> microcode above. By reusing the micro-instruction from <code>AAM</code>, the microcode is one micro-instruction shorter, but the jump adds one cycle to the execution time. (The CORX routine is used for integer multiplication; I discuss the internals in <a href="https://www.righto.com/2023/03/8086-multiplication-microcode.html">this post</a>.)</p> <pre class="microcode"> Q → tmpC <span><b>AAD:</b> Get byte from prefetch queue.</span> AH → tmpB CALL CORX <span>Call CORX</span> AL → tmpB ADD tmpC <span>Set ALU for ADD</span> ZERO → AH JMP AAEND <span>Zero AH, jump to AAEND</span> i ... 危 → AL RNI F <span><b>AAEND:</b> Sum to AL, done.</span> </pre> <p>As with <code>AAM</code>, the constant <code>10</code> is provided in the second byte of the instruction. The microcode accepts any value here, but values other than <code>10</code> are undocumented.</p> <h3><code>8C</code>, <code>8E</code>: MOV sr</h3> <p>The opcodes <code>8C</code> and <code>8E</code> perform a <code>MOV</code> register to or from the specified segment register, using the register specification field in the ModR/M byte. There are four segment registers and three selection bits, so an invalid segment register can be specified. However, the hardware that decodes the register number ignores instruction bit 5 for a segment register. Thus, specifying a segment register 4 to 7 is the same as specifying a segment register 0 to 3. For more details, see my article on <a href="https://www.righto.com/2023/03/8086-register-codes.html">8086 register codes</a>.</p> <h2>Unexpected <code>REP</code> prefix</h2> <h3><code>REP IMUL</code> / <code>IDIV</code></h3> <p>The <code>REP</code> prefix is used with string operations to cause the operation to be repeated across a block of memory. However, if you use this prefix with an <code>IMUL</code> or <code>IDIV</code> instruction, it has the unexpected behavior of negating the product or the quotient (<a href="https://www.reenigne.org/blog/8086-microcode-disassembled/">source</a>).</p> <p>The reason for this behavior is that the string operations use an internal flag called <code>F1</code> to indicate that a <code>REP</code> prefix has been applied. The multiply and divide code reuses this flag to track the sign of the input values, toggling <code>F1</code> for each negative value. If <code>F1</code> is set, the value at the end is negated. (This handles "two negatives make a positive.") The consequence is that the <code>REP</code> prefix puts the flag in the 1 state when the multiply/divide starts, so the computed sign will be wrong at the end and the result is the negative of the expected result. The microcode is fairly complex, so I won't show it here; I explain it in detail in <a href="https://www.righto.com/2023/03/8086-multiplication-microcode.html">this blog post</a>.</p> <h3><code>REP RET</code></h3> <p><a href="https://en.wikipedia.org/wiki/X86_instruction_listings#Undocumented_x86_instructions">Wikipedia</a> lists <code>REP RET</code> (i.e. <code>RET</code> with a <code>REP</code> prefix) as a way to implement a two-byte return instruction. This is kind of trivial; the <code>RET</code> microcode (like almost every instruction) doesn't use the <code>F1</code> internal flag, so the <code>REP</code> prefix has no effect.</p> <h3><code>REPNZ MOVS/STOS</code></h3> <p><a href="https://en.wikipedia.org/wiki/X86_instruction_listings#Undocumented_x86_instructions">Wikipedia</a> mentions that the use of the <code>REPNZ</code> prefix (as opposed to <code>REPZ</code>) is undefined with string operations other than <code>CMPS/SCAS</code>. An internal flag called <code>F1Z</code> distinguishes between the <code>REPZ</code> and <code>REPNZ</code> prefixes. This flag is only used by <code>CMPS/SCAS</code>. Since the other string instructions ignore this flag, they will ignore the difference between <code>REPZ</code> and <code>REPNZ</code>. I wrote about string operations in more detail in <a href="https://www.righto.com/2023/04/8086-microcode-string-operations.html">this post</a>.</p> <h2>Using a register instead of memory.</h2> <p>Some instructions are documented as requiring a memory operand. However, the ModR/M byte can specify a register. The behavior in these cases can be highly unusual, providing access to hidden registers. Examining the microcode shows how this happens.</p> <h3><code>LEA reg</code></h3> <p>Many instructions have a ModR/M byte that indicates the memory address that the instruction should use, perhaps through a complicated addressing mode. The <code>LEA</code> (Load Effective Address) instruction is different: it doesn't access the memory location but returns the address itself. The undocumented part is that the ModR/M byte can specify a register instead of a memory location. In that case, what does the <code>LEA</code> instruction do? Obviously it can't return the address of a register, but it needs to return something.</p> <p>The behavior of <code>LEA</code> is explained by how the 8086 handles the ModR/M byte. Before running the microcode corresponding to the instruction, the microcode engine calls a short micro-subroutine for the particular addressing mode. This micro-subroutine puts the desired memory address (the effective address) into the <code>tmpA</code> register. The effective address is copied to the <code>IND</code> (Indirect) register and the value is loaded from memory if needed. On the other hand, if the ModR/M byte specified a register instead of memory, no micro-subroutine is called. (I explain ModR/M handling in more detail in <a href="https://www.righto.com/2023/02/8086-modrm-addressing.html">this article</a>.)</p> <p>The microcode for <code>LEA</code> itself is just one line. It stores the effective address in the <code>IND</code> register into the specified destination register, indicated by <code>N</code>. This assumes that the appropriate ModR/M micro-subroutine was called before this code, putting the effective address into <code>IND</code>.</p> <pre class="microcode"> IND → N RNI <span><b>LEA</b>: store IND register in destination, done</span> </pre> <p>But if a register was specified instead of a memory location, no ModR/M micro-subroutine gets called. Instead, the <code>LEA</code> instruction will return whatever value was left in <code>IND</code> from before, typically the previous memory location that was accessed. Thus, <code>LEA</code> can be used to read the value of the <code>IND</code> register, which is normally hidden from the programmer.</p> <h3><code>LDS reg</code>, <code>LES reg</code></h3> <p>The <code>LDS</code> and <code>LES</code> instructions load a far pointer from memory into the specified segment register and general-purpose register. The microcode below assumes that the appropriate ModR/M micro-subroutine has set up <code>IND</code> and read the first value into <code>OPR</code>. The microcode updates the destination register, increments <code>IND</code> by 2, reads the next value, and updates <code>DS</code>. (The microcode for <code>LES</code> is a copy of this, but updates <code>ES</code>.)</p> <pre class="microcode"> OPR → N <span><b>LDS</b>: Copy OPR to dest register</span> IND → tmpC INC2 tmpC <span>Set up incrementing IND by 2</span> 危 → IND R DS,P0 <span>Update IND, read next location</span> OPR → DS RNI <span>Update DS</span> </pre> <p>If the <code>LDS</code> instruction specifies a register instead of memory, a micro-subroutine will not be called, so <code>IND</code> and <code>OPR</code> will have values from a previous instruction. <code>OPR</code> will be stored in the destination register, while the <code>DS</code> value will be read from the address <code>IND+2</code>. Thus, these instructions provide a mechanism to access the hidden <code>OPR</code> register.</p> <h3><code>JMP FAR rm</code></h3> <p>The <code>JMP FAR rm</code> instruction normally jumps to the far address stored in memory at the location indicated by the ModR/M byte. (That is, the ModR/M byte indicates where the new PC and CS values are stored.) But, as with <code>LEA</code>, the behavior is undocumented if the ModR/M byte specifies a register, since a register doesn't hold a four-byte value.</p> <p>The microcode explains what happens. As with <code>LEA</code>, the code expects a micro-subroutine to put the address into the <code>IND</code> register. In this case, the micro-subroutine also loads the value at that address (i.e. the destination <code>PC</code>) into tmpB. The microcode increments <code>IND</code> by 2 to point to the <code>CS</code> word in memory and reads that into <code>CS</code>. Meanwhile, it updates the <code>PC</code> with <code>tmpB</code>. It suspends prefetching and flushes the queue, so instruction fetching will restart at the new address.</p> <pre class="microcode"> IND → tmpC INC2 tmpC <span><b>JMP FAR rm</b>: set up to add 2 to IND</span> 危 → IND SUSP <span>Update IND, suspend prefetching</span> tmpB → PC R DS,P0 <span>Update PC with tmpB. Read new CS from specified address</span> OPR → CS FLUSH RNI <span>Update CS, flush queue, done</span> </pre> <p>If you specify a register instead of memory, the micro-subroutine won't get called. Instead, the program counter will be loaded with whatever value was in <code>tmpB</code> and the <code>CS</code> segment register will be loaded from the memory location two bytes after the location that <code>IND</code> was referencing. Thus, this undocumented use of the instruction gives access to the otherwise-hidden <code>tmpB</code> register.</p> <h2>The end of undocumented instructions</h2> <p>Microprocessor manufacturers soon realized that undocumented instructions were a problem, since programmers find them and often use them. This creates an issue for future processors, or even revisions of the current processor: if you eliminate an undocumented instruction, previously-working code that used the instruction will break, and it will seem like the new processor is faulty.</p> <p>The solution was for processors to detect undocumented instructions and prevent them from executing. By the early 1980s, processors had enough transistors (thanks to Moore's law) that they could include the circuitry to block unsupported instructions. In particular, the 80186/80188 and the 80286 generated a trap of type 6 when an unused opcode was executed, blocking use of the instruction.<span id="fnref:186"><a class="ref" href="#fn:186">9</a></span> This trap is also known as #UD (Undefined instruction trap).<span id="fnref:fault"><a class="ref" href="#fn:fault">10</a></span></p> <h2>Conclusions</h2> <p>The 8086, like many early microprocessors, has undocumented instructions but no traps to stop them from executing.<span id="fnref:references"><a class="ref" href="#fn:references">11</a></span> For the 8086, these fall into several categories. Many undocumented instructions simply mirror existing instructions. Some instructions are implemented but not documented for one reason or another, such as <code>SALC</code> and <code>POP CS</code>. Other instructions can be used outside their normal range, such as <code>AAM</code> and <code>AAD</code>. Some instructions are intended to work only with a memory address, so specifying a register can have strange effects such as revealing the values of the hidden <code>IND</code> and <code>OPR</code> registers.</p> <p>Keep in mind that my analysis is based on transistor-level simulation and examining the microcode; I haven't verified the behavior on a physical 8086 processor. Please let me know if you see any errors in my analysis or undocumented instructions that I have overlooked. Also note that the behavior could change between different versions of the 8086; in particular, some versions by different manufacturers (such as the NEC V20 and V30) are known to be different.</p> <p>I plan to write more about the 8086, so follow me on Twitter <a href="https://twitter.com/kenshirriff">@kenshirriff</a> or <a href="http://www.righto.com/feeds/posts/default">RSS</a> for updates. I've also started experimenting with Mastodon recently as <a href="https://oldbytes.space/@kenshirriff">@<span class="__cf_email__" data-cfemail="7b101e150813120909121d1d3b14171f19020f1e0855080b1a181e">[email protected]</span></a> and Bluesky as <a href="https://staging.bsky.app/profile/righto.com">@righto.com</a> so you can follow me there too.</p> <h2>Notes and references</h2> <div class="footnote"> <ol> <li id="fn:6502"> <p>The 6502 processor, for instance, has illegal instructions with various effects, including causing the processor to hang. The article <a href="https://www.pagetable.com/?p=39">How MOS 6502 illegal opcodes really work</a> describes in detail how the instruction decoding results in various illegal opcodes. Some of these opcodes put the internal bus into a floating state, so the behavior is electrically unpredictable. <a class="footnote-backref" href="#fnref:6502" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:prefixes"> <p>The 8086 used up almost all the single-byte opcodes, which made it difficult to extend the instruction set. Most of the new instructions for the 386 or later are multi-byte opcodes, either using <code>0F</code> as a prefix or reusing the earlier REP prefix (<code>F3</code>). Thus, the x86 instruction set is less efficient than it could be, since many single-byte opcodes were "wasted" on hardly-used instructions such as BCD arithmetic, forcing newer instructions to be multi-byte. <a class="footnote-backref" href="#fnref:prefixes" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:magic-instruction"> <p>For details on the "magic instruction" hidden in the 8086 microcode, see <a href="https://digitalcommons.law.scu.edu/cgi/viewcontent.cgi?referer=&httpsredir=1&article=1031&context=chtlj">NEC v. Intel: Will Hardware Be Drawn into the Black Hole of Copyright Editors</a> page 49. I haven't found anything stating that <code>SALC</code> was the hidden instruction, but this is the only undocumented instruction that makes sense as something deliberately put into the microcode. The court case is complicated since NEC had a licensing agreement with Intel, so I'm skipping lots of details. See <a href="http://jolt.law.harvard.edu/articles/pdf/v03/03HarvJLTech209.pdf">NEC v. Intel: Breaking new ground in the law of copyright</a> for more. <a class="footnote-backref" href="#fnref:magic-instruction" title="Jump back to footnote 3 in the text">↩</a></p> </li> <li id="fn:microcode"> <p>The microcode listings are based on Andrew Jenner's <a href="https://www.reenigne.org/blog/8086-microcode-disassembled/">disassembly</a>. I have made some modifications to (hopefully) make it easier to understand. <a class="footnote-backref" href="#fnref:microcode" title="Jump back to footnote 4 in the text">↩</a></p> </li> <li id="fn:extension"> <p>Specifying the instruction through the ModR/M reg field may seem a bit random, but there's a reason for this. A typical instruction such as <code>ADD</code> has two arguments specified by the ModR/M byte. But other instructions such as shift instructions or <code>NOT</code> only take one argument. For these instructions, the ModR/M <code>reg</code> field would be wasted if it specified a register. Thus, using the <code>reg</code> field to specify instructions that only use one argument makes the instruction set more efficient. <a class="footnote-backref" href="#fnref:extension" title="Jump back to footnote 5 in the text">↩</a></p> </li> <li id="fn:alu"> <p>Note that "normal" ALU operations are specified by bits 5-3 of the instruction; in order these are <code>ADD</code>, <code>OR</code>, <code>ADC</code>, <code>SBB</code>, <code>AND</code>, <code>SUB</code>, <code>XOR</code>, and <code>CMP</code>. These are exactly the same ALU operations that the "Immed" group performs, specified by bits 5-3 of the second byte. This illustrates how the same operation selection mechanism (the <code>X</code> register) is used in both cases. Bit 6 of the instruction switches between the set of arithmetic/logic instructions and the set of shift/rotate instructions. <a class="footnote-backref" href="#fnref:alu" title="Jump back to footnote 6 in the text">↩</a></p> </li> <li id="fn:setmo"> <p>As far as I can tell, SETMO isn't used by the microcode. Thus, I think that SETMO wasn't deliberately implemented in the ALU, but is a consequence of how the ALU's control logic is implemented. That is, all the even entries are left shifts and the odd entries are right shifts, so operation 6 activates the left-shift circuitry. But it doesn't match a specific left shift operation, so the ALU doesn't get configured for a "real" left shift. In other words, the behavior of this instruction is due to how the ALU handles a case that it wasn't specifically designed to handle.</p> <p>This function is implemented in the ALU somewhat similar to a shift left. However, instead of passing each input bit to the left, the bit from the right is passed to the left. That is, the input to bit 0 is shifted left to all of the bits of the result. By setting this bit to 1, all bits of the result are set, yielding the minus 1 result. <a class="footnote-backref" href="#fnref:setmo" title="Jump back to footnote 7 in the text">↩</a></p> </li> <li id="fn:immed"> <p>This footnote provides some references for the "Immed" opcodes. The <a href="https://www.electro-tech-online.com/datasheets/8086_intel.pdf">8086 datasheet</a> has an opcode map showing opcodes <code>80</code> through <code>83</code> as valid. However, in the listings of individual instructions it only shows <code>80</code> and <code>81</code> for logical instructions (i.e. bit 1 must be 0), while it shows <code>80</code>-<code>83</code> for arithmetic instructions. The modern <a href="https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html">Intel 64 and IA-32 Architectures Software Developer's Manual</a> is also contradictory. Looking at the instruction reference for <code>AND</code> (Vol <code>2A</code> 3-<code>78</code>), for instance, shows opcodes <code>80</code>, <code>81</code>, and <code>83</code>, explicitly labeling <code>83</code> as sign-extended. But the opcode map (Table A-2 Vol <code>2D</code> A-7) shows <code>80</code>-<code>83</code> as defined except for <code>82</code> in <code>64</code>-bit mode. The instruction bit diagram (Table B-<code>13</code> Vol <code>2D</code> B-7) shows <code>80</code>-<code>83</code> valid for the arithmetic and logical instructions. <a class="footnote-backref" href="#fnref:immed" title="Jump back to footnote 8 in the text">↩</a></p> </li> <li id="fn:186"> <p>The 80286 was more thorough about detecting undefined opcodes than the 80186, even taking into account the differences in instruction set. The 80186 generates a trap when <code>0F</code>, <code>63</code>-<code>67</code>, <code>F1</code>, or <code>FFFF</code> is executed. The 80286 generates invalid opcode exception number 6 (#UD) on any undefined opcode, handling the following cases: <ul> <li> The first byte of an instruction is completely invalid (e.g., 64H). <li> The first byte indicates a 2-byte opcode and the second byte is invalid (e.g., 0F followed by 0FFH). <li> An invalid register is used with an otherwise valid opcode (e.g., MOV CS,AX). <li> An invalid opcode extension is given in the REG field of the ModR/M byte (e.g., 0F6H /1). <li> A register operand is given in an instruction that requires a memory operand (e.g., LGDT AX). </ul> <!-- http://www.bitsavers.org/components/intel/80186/210911-001_iAPX86_88_186_188_Programmers_Reference_1983.pdf page 5-3 --> <!-- page B-11 of http://bitsavers.trailing-edge.com/components/intel/80286/210498-005_80286_and_80287_Programmers_Reference_Manual_1987.pdf --> <a class="footnote-backref" href="#fnref:186" title="Jump back to footnote 9 in the text">↩</a></p> </li> <li id="fn:fault"> <p>In modern x86 processors, most undocumented instructions cause faults. However, there are still a few undocumented instructions that don't fault. These may be for internal use or corner cases of documented instructions. For details, see <a href="https://www.youtube.com/watch?v=KrksBdWcZgQ">Breaking the x86 Instruction Set</a>, a video from Black Hat 2017. <a class="footnote-backref" href="#fnref:fault" title="Jump back to footnote 10 in the text">↩</a></p> </li> <li id="fn:references"> <p>Several sources have discussed undocumented 8086 opcodes before. The article <a href="https://www.os2museum.com/wp/undocumented-8086-opcodes-part-i/">Undocumented 8086 Opcodes</a> describes undocumented opcodes in detail. <a href="https://en.wikipedia.org/wiki/X86_instruction_listings#Undocumented_x86_instructions">Wikipedia</a> has a list of undocumented x86 instructions. The book <a href="https://archive.org/details/undocumentedpc0000vang/page/58/mode/2up?view=theater">Undocumented PC</a> discusses undocumented instructions in the 8086 and later processors. This <a href="https://retrocomputing.stackexchange.com/questions/20031/undocumented-instructions-in-x86-cpu-prior-to-80386">StackExchange Retrocomputing</a> post describes undocumented instructions. These <a href="https://news.ycombinator.com/item?id=34960243">Hacker News comments</a> discuss some undocumented instructions. There are other sources with more myth than fact, claiming that the 8086 treats undocumented instructions as NOPs, for instance. <a class="footnote-backref" href="#fnref:references" title="Jump back to footnote 11 in the text">↩</a></p> </li> </ol> </div> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'><span class='post-comment-link'> <a class='comment-link' href='https://www.blogger.com/comment/fullpage/post/6264947694886887540/5516205124640022120' onclick=''> 15 comments: </a> </span> <span class='post-icons'> <span class='item-action'> <a href='https://www.blogger.com/email-post.g?blogID=6264947694886887540&postID=5516205124640022120' title='Email Post'> <img alt='' class='icon-action' height='13' src='http://img1.blogblog.com/img/icon18_email.gif' width='18'/> </a> </span> <span class='item-control blog-admin pid-1138732533'> <a href='https://www.blogger.com/post-edit.g?blogID=6264947694886887540&postID=5516205124640022120&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <span class='post-backlinks post-comment-link'> </span> <div class='post-share-buttons goog-inline-block'> <a class='goog-inline-block share-button sb-email' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=5516205124640022120&target=email' target='_blank' title='Email This'><span class='share-button-link-text'>Email This</span></a><a class='goog-inline-block share-button sb-blog' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=5516205124640022120&target=blog' onclick='window.open(this.href, "_blank", "height=270,width=475"); return false;' target='_blank' title='BlogThis!'><span class='share-button-link-text'>BlogThis!</span></a><a class='goog-inline-block share-button sb-twitter' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=5516205124640022120&target=twitter' target='_blank' title='Share to X'><span class='share-button-link-text'>Share to X</span></a><a class='goog-inline-block share-button sb-facebook' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=5516205124640022120&target=facebook' onclick='window.open(this.href, "_blank", "height=430,width=640"); return false;' target='_blank' title='Share to Facebook'><span class='share-button-link-text'>Share to Facebook</span></a><a class='goog-inline-block share-button sb-pinterest' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=5516205124640022120&target=pinterest' target='_blank' title='Share to Pinterest'><span class='share-button-link-text'>Share to Pinterest</span></a> </div> </div> <div class='post-footer-line post-footer-line-2'><span class='post-labels'> Labels: <a href='http://www.righto.com/search/label/8086' rel='tag'>8086</a>, <a href='http://www.righto.com/search/label/intel' rel='tag'>intel</a>, <a href='http://www.righto.com/search/label/microcode' rel='tag'>microcode</a>, <a href='http://www.righto.com/search/label/reverse-engineering' rel='tag'>reverse-engineering</a> </span> </div> <div class='post-footer-line post-footer-line-3'></div> </div> </div> </div> </div></div> <div class="date-outer"> <div class="date-posts"> <div class='post-outer'> <div class='post hentry' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://static.righto.com/images/8086-group/die-labeled-w600.jpg' itemprop='image_url'/> <meta content='6264947694886887540' itemprop='blogId'/> <meta content='7453359098571162761' itemprop='postId'/> <a name='7453359098571162761'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='http://www.righto.com/2023/05/8086-processor-group-decode-rom.html'>The Group Decode ROM: The 8086 processor's first step of instruction decoding</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-7453359098571162761' itemprop='description articleBody'> <style> .hilite {cursor:zoom-in} a:link img.hilite, a:visited img.hilite {color: #fff;} a:hover img.hilite {color: #f66;} </style> <style> pre.microcode {font-family: courier, fixed; padding: 10px; background-color: #f5f5f5; display:inline-block;border:none;} pre.microcode span {color: green; font-style:italic; font-family: sans-serif; font-size: 90%;} </style> <p>A key component of any processor is instruction decoding: analyzing a numeric opcode and figuring out what actions need to be taken. The Intel 8086 processor (1978) has a complex instruction set, making instruction decoding a challenge. The first step in decoding an 8086 instruction is something called the Group Decode ROM, which categorizes instructions into about 35 types that control how the instruction is decoded and executed. For instance, the Group Decode ROM determines if an instruction is executed in hardware or in microcode. It also indicates how the instruction is structured: if the instruction has a bit specifying a byte or word operation, if the instruction has a byte that specifies the addressing mode, and so forth.</p> <p><a href="https://static.righto.com/images/8086-group/die-labeled.jpg"><img alt="The 8086 die under a microscope, with main functional blocks labeled. This photo shows the chip with the metal and polysilicon removed, revealing the silicon underneath. Click on this image (or any other) for a larger version." class="hilite" height="633" src="https://static.righto.com/images/8086-group/die-labeled-w600.jpg" title="The 8086 die under a microscope, with main functional blocks labeled. This photo shows the chip with the metal and polysilicon removed, revealing the silicon underneath. Click on this image (or any other) for a larger version." width="600" /></a><div class="cite">The 8086 die under a microscope, with main functional blocks labeled. This photo shows the chip with the metal and polysilicon removed, revealing the silicon underneath. Click on this image (or any other) for a larger version.</div></p> <p>The diagram above shows the position of the Group Decode ROM on the silicon die, as well as other key functional blocks. The 8086 chip is partitioned into a Bus Interface Unit that communicates with external components such as memory, and the Execution Unit that executes instructions. Machine instructions are fetched from memory by the Bus Interface Unit and stored in the prefetch queue registers, which hold 6 bytes of instructions. To execute an instruction, the queue bus transfers an instruction byte from the prefetch queue to the instruction register, under control of a state machine called the Loader. Next, the Group Decode ROM categorizes the instruction according to its structure. In most cases, the machine instruction is implemented in low-level microcode. The instruction byte is transferred to the Microcode Address Register, where the Microcode Address Decoder selects the appropriate microcode routine that implements the instruction. The microcode provides the micro-instructions that control the Arithmetic/Logic Unit (ALU), registers, and other components to execute the instruction.</p> <p>In this blog post, I will focus on a small part of this process: how the Group Decode ROM decodes instructions. Be warned that this post gets down into the weeds, so you might want to start with one of my higher-level posts, such as <a href="https://www.righto.com/2022/11/how-8086-processors-microcode-engine.html">how the 8086's microcode engine works</a>.</p> <!-- According to patent [4449184](https://patents.google.com/patent/US4449184A), "A group decode ROM has its inputs coupled to the instruction register. The group decode ROM generates a plurality of group decode signals which are indicative of the genera of the single byte and multiple byte instructions being received and decoded by the lower control means." --> <h2>Microcode</h2> <p>Most instructions in the 8086 are implemented in microcode. Most people think of machine instructions as the basic steps that a computer performs. However, many processors have another layer of software underneath: microcode. With microcode, instead of building the CPU's control circuitry from complex logic gates, the control logic is largely replaced with code. To execute a machine instruction, the computer internally executes several simpler micro-instructions, specified by the microcode.</p> <p>Microcode is only used if the Group Decode ROM indicates that the instruction is implemented in microcode. In that case, the microcode address register is loaded with the instruction and the address decoder selects the appropriate microcode routine. However, there's a complication. If the second byte of the instruction is a Mod R/M byte, the Group Decode ROM indicates this and causes a memory addressing micro-subroutine to be called.</p> <p>Some simple instructions are implemented entirely in hardware and don't use microcode. These are known as 1-byte logic instructions (1BL) and are also indicated by the Group Decode ROM.</p> <h2>The Group Decode ROM's structure</h2> <p>The Group Decode ROM takes an 8-bit instruction as input, along with an interrupt signal. It produces 15 outputs that control how the instruction is handled. In this section I'll discuss the physical implementation of the Group Decode ROM; the various outputs are discussed in a later section.</p> <p>Although the Group Decode ROM is called a ROM, its implementation is really a PLA (Programmable Logic Array), two levels of highly-structured logic gates.<span id="fnref:rom"><a class="ref" href="#fn:rom">1</a></span> The idea of a PLA is to create two levels of NOR gates, each in a grid. This structure has the advantages that it implements the logic densely and is easy to modify. Although physically two levels of NOR gates, a PLA can be thought of as an <code>AND</code> layer followed by an <code>OR</code> layer. The <code>AND</code> layer matches particular bit patterns and then the <code>OR</code> layer combines multiple values from the first layer to produce arbitrary outputs.</p> <p><a href="https://static.righto.com/images/8086-group/rom-labeled.jpg"><img alt="The Group Decode ROM. This photo shows the metal layer on top of the die." class="hilite" height="443" src="https://static.righto.com/images/8086-group/rom-labeled-w600.jpg" title="The Group Decode ROM. This photo shows the metal layer on top of the die." width="600" /></a><div class="cite">The Group Decode ROM. This photo shows the metal layer on top of the die.</div></p> <p>Since the output values are highly structured, a PLA implementation is considerably more efficient than a ROM, since in a sense it combines multiple entries. In the case of the Group Decode ROM, using a ROM structure would require 256 columns (one for each 8-bit instruction pattern), while the PLA implementation requires just 36 columns, about 1/7 the size.</p> <p>The diagram below shows how one column of the Group Decode ROM is wired in the "AND" plane. In this die photo, I removed the metal layer with acid to reveal the polysilicon and silicon underneath. The vertical lines show where the metal line for ground and the column output had been. The basic idea is that each column implements a NOR gate, with a subset of the input lines selected as inputs to the gate. The pull-up resistor at the top pulls the column line high by default. But if any of the selected inputs are high, the corresponding transistor turns on, connecting the column line to ground and pulling it low. Thus, this implements a NOR gate. However, it is more useful to think of it as an AND of the complemented inputs (via <a href="https://en.wikipedia.org/wiki/De_Morgan%27s_laws">De Morgan's Law</a>): if all the inputs are "correct", the output is high. In this way, each column matches a particular bit pattern.</p> <p><a href="https://static.righto.com/images/8086-group/column-labeled.jpg"><img alt="Closeup of a column in the Group Decode ROM." class="hilite" height="575" src="https://static.righto.com/images/8086-group/column-labeled-w280.jpg" title="Closeup of a column in the Group Decode ROM." width="280" /></a><div class="cite">Closeup of a column in the Group Decode ROM.</div></p> <p>The structure of the ROM is implemented through the silicon doping pattern, which is visible above. A transistor is formed where a polysilicon wire crosses a doped silicon region: the polysilicon forms the gate, turning the transistor on or off. At each intersection point, a transistor can be created or not, depending on the doping pattern. If a particular transistor is created, then the corresponding input must be 0 to produce a high output.</p> <p>At the top of the diagram above, the column outputs are switched from the metal layer to polysilicon wires and become the inputs to the upper "OR" plane. This plane is implemented in a similar fashion as a grid of NOR gates. The plane is rotated 90 degrees, with the inputs vertical and each row forming an output.</p> <h2>Intermediate decoding in the Group Decode ROM</h2> <p>The first plane of the Group Decode ROM categorizes instructions into 36 types based on the instruction bit pattern.<span id="fnref:36"><a class="ref" href="#fn:36">2</a></span> The table below shows the 256 instruction values, colored according to their categorization.<span id="fnref:octal"><a class="ref" href="#fn:octal">3</a></span> For instance, the first blue block consists of the 32 ALU instructions corresponding to the bit pattern <code>00XXX0XX</code>, where <code>X</code> indicates that the bit can be 0 or 1. These instructions are all decoded and executed in a similar way. Almost all instructions have a single category, that is, they activate a single column line in the Group Decode ROM. However, a few instructions activate two lines and have two colors below.</p> <p><a href="https://static.righto.com/images/8086-group/grid.png"><img alt="Grid of 8086 instructions, colored according to the first level of the Group Decode Rom." class="hilite" height="600" src="https://static.righto.com/images/8086-group/grid-w600.png" title="Grid of 8086 instructions, colored according to the first level of the Group Decode Rom." width="600" /></a><div class="cite">Grid of 8086 instructions, colored according to the first level of the Group Decode Rom.</div></p> <p>Note that the instructions do not have arbitrary numeric opcodes, but are assigned in a way that makes decoding simpler. Because these blocks correspond to bit patterns, there is little flexibility. One of the challenges of instruction set design for early microprocessors was to assign numeric values to the opcodes in a way that made decoding straightforward. It's a bit like a jigsaw puzzle, fitting the instructions into the 256 available values, while making them easy to decode. </p> <h2>Outputs from the Group Decode ROM</h2> <p>The Group Decode ROM has 15 outputs, one for each row of the upper half. In this section, I'll briefly discuss these outputs and their roles in the 8086. For an interactive exploration of these signals, see <a href="https://righto.com/8086/groupRom.html">this page</a>, which shows the outputs that are triggered by each instruction.</p> <p>Out 0 indicates an <code>IN</code> or <code>OUT</code> instruction. This signal controls the M/IO (S2) status line, which distinguishes between a memory read/write and an I/O read/write. Apart from this, memory and I/O accesses are basically the same.</p> <p>Out 1 indicates (inverted) that the instruction has a Mod R/M byte and performs a read/modify/write on its argument. This signal is used by the Translation ROM when dispatching an address handler (<a href="https://www.righto.com/2023/02/8086-modrm-addressing.html">details</a>). (This signal distinguishes between, say, <code>ADD [AX],BX</code> and <code>MOV [AX],BX</code>. The former both reads and writes <code>[AX]</code>, while the latter only writes to it.)</p> <p>Out 2 indicates a "group 3/4/5" opcode, an instruction where the second byte specifies the particular instruction, and thus decoding needs to wait for the second byte. This controls the loading of the microcode address register.</p> <p>Out 3 indicates an instruction prefix (segment, <code>LOCK</code>, or <code>REP</code>). This causes the next byte to be decoded as a new instruction, while blocking interrupt handling.</p> <p>Out 4 indicates (inverted) a two-byte ROM instruction (2BR), i.e. an instruction is handled by the microcode ROM, but requires the second byte for decoding. This is an instruction with a Mod R/M byte. This signal controls the loader indicating that it needs to fetch the second byte. This signal is almost the same as output 1 with a few differences.</p> <p>Out 5 specifies the top bit for an ALU operation. The 8086 uses a 5-bit field to specify an ALU operation. If not specified explicitly by the microcode, the field uses bits 5 through 3 of the opcode. (These bits distinguish, say, an <code>ADD</code> instruction from <code>AND</code> or <code>SUB</code>.) This control line sets the top bit of the ALU field for instructions such as <code>DAA</code>, <code>DAS</code>, <code>AAA</code>, <code>AAS</code>, <code>INC</code>, and <code>DE</code> that fall into a different set from the "regular" ALU instructions.</p> <p>Out 6 indicates an instruction that sets or clears a condition code directly: <code>CLC</code>, <code>STC</code>, <code>CLI</code>, <code>STI</code>, <code>CLD</code>, or <code>STD</code> (but not <code>CMC</code>). This signal is used by the flag circuitry to update the condition code.</p> <p>Out 7 indicates an instruction that uses the <code>AL</code> or <code>AX</code> register, depending on the instruction's size bit. (For instance <code>MOVSB</code> vs <code>MOVSW</code>.) This signal is used by the register selection circuitry, the <code>M</code> register specifically.</p> <p>Out 8 indicates a <code>MOV</code> instruction that uses a segment register. This signal is used by the register selection circuitry, the <code>N</code> register specifically.</p> <p>Out 9 indicates the instruction has a <code>d</code> bit, where bit 1 of the instruction swaps the source and destination. This signal is used by the register selection circuitry, swapping the roles of the <code>M</code> and <code>N</code> registers according to the <code>d</code> bit.</p> <p>Out 10 indicates a one-byte logic (1BL) instruction, a one-byte instruction that is implemented in logic, not microcode. These instructions are the prefixes, <code>HLT</code>, and the condition-code instructions. This signal controls the loader, causing it to move to the next instruction.</p> <p>Out 11 indicates instructions where bit 0 is the byte/word indicator. This signal controls the register handling and the ALU functionality.</p> <p>Out 12 indicates an instruction that operates only on a byte: <code>DAA</code>, <code>DAS</code>, <code>AAA</code>, <code>AAS</code>, <code>AAM</code>, <code>AAD</code>, and <code>XLAT</code>. This signal operates in conjunction with the previous output to select a byte versus word.</p> <p>Out 13 forces the instruction to use a byte argument if instruction bit 1 is set, overriding the regular byte/word pattern. Specifically, it forces the <code>L8</code> (length 8 bits) condition for the <code>JMP</code> direct-within-segment and the ALU instructions that are immediate with sign extension (<a href="https://www.righto.com/2023/02/how-8086-processor-determines-length-of.html">details</a>).</p> <p>Out 14 allows a carry update. This prevents the carry from being updated by the <code>INC</code> and <code>DEC</code> operations. This signal is used by the flag circuitry.</p> <h3>Columns</h3> <p>Most of the Group Decode ROM's column signals are used to derive the outputs listed above. However, some column outputs are also used as control signals directly. These are listed below.</p> <p>Column 10 indicates an immediate <code>MOV</code> instruction. These instructions use instruction bit 3 (rather than bit 1) to select byte versus word, because the three low bits specify the register. This signal affects the <code>L8</code> condition described earlier and also causes the <code>M</code> register selection to be converted from a word register to a byte register if necessary.</p> <p>Column 12 indicates an instruction with bits 5-3 specifying the ALU instruction. This signal causes the <code>X</code> register to be loaded with the bits in the instruction that specify the ALU operation. (To be precise, this signal prevents the <code>X</code> register from being reloaded from the second instruction byte.)</p> <p>Column 13 indicates the <code>CMC</code> (Complement Carry) instruction. This signal is used by the flags circuitry to complement the carry flag (<a href="https://www.righto.com/2023/02/silicon-reverse-engineering-intel-8086.html">details</a>).</p> <p>Column 14 indicates the <code>HLT</code> (Halt) instruction. This signal stops instruction processing by blocking the instruction queue.</p> <p>Column 31 indicates a <code>REP</code> prefix. This signal causes the REPZ/NZ latch to be loaded with instruction bit 0 to indicate if the prefix is <code>REPNZ</code> or <code>REPZ</code>. It also sets the <code>REP</code> latch.</p> <p>Column 32 indicates a segment prefix. This signal loads the segment latches with the desired segment type.</p> <p>Column 33 indicates a <code>LOCK</code> prefix. It sets the <code>LOCK</code> latch, locking the bus.</p> <p>Column 34 indicates a <code>CLI</code> instruction. This signal immediately blocks interrupt handling to avoid an interrupt between the <code>CLI</code> instruction and when the interrupt flag bit is cleared.</p> <h2>Timing</h2> <p>One important aspect of the Group Decode ROM is that its outputs are not instantaneous. It takes a clock cycle to get the outputs from the Group Decode ROM. In particular, when instruction decoding starts, the timing signal <code>FC</code> (First Clock) is activated to indicate the first clock cycle. However, the Group Decode ROM's outputs are not available until the Second Clock <code>SC</code>.</p> <p>One consequence of this is that even the simplest instruction (such as a flag operation) takes two clock cycles, as does a prefix. The problem is that even though the instruction could be performed in one clock cycle, it takes two clock cycles for the Group Decode ROM to determine that the instruction only needs one cycle. This illustrates how a complex instruction format impacts performance.</p> <p>The <code>FC</code> and <code>SC</code> timing signals are generated by a state machine called the Loader. These signals may seem trivial, but there are a few complications. First, the prefetch queue may run empty, in which case the <code>FC</code> and/or <code>SC</code> signal is delayed until the prefetch queue has a byte available. Second, to increase performance, the 8086 can start decoding an instruction during the last clock cycle of the previous instruction. Thus, if the microcode indicates that there is one cycle left, the Loader can proceed with the next instruction. Likewise, for a one-byte instruction implemented in hardware (one-byte logic or 1BL), the loader proceeds as soon as possible.</p> <p>The diagram below shows the timing of an <code>ADD</code> instruction. Each line is half of a clock cycle. Execution is pipelined: the instruction is fetched during the first clock cycle (First Clock). During Second Clock, the Group Decode ROM produces its output. The microcode address register also generates the micro-address for the instruction's microcode. The microcode ROM supplies a micro-instruction during the third clock cycle and execution of the micro-instruction takes place during the fourth clock cycle.</p> <p><a href="https://static.righto.com/images/8086-group/diagram-labeled.jpg"><img alt="This diagram shows the execution of an ADD instruction and what is happening in various parts of the 8086. The arrows show the flow from step to step. The character 碌 is short for "micro"." class="hilite" height="395" src="https://static.righto.com/images/8086-group/diagram-labeled-w750.jpg" title="This diagram shows the execution of an ADD instruction and what is happening in various parts of the 8086. The arrows show the flow from step to step. The character 碌 is short for "micro"." width="750" /></a><div class="cite">This diagram shows the execution of an ADD instruction and what is happening in various parts of the 8086. The arrows show the flow from step to step. The character 碌 is short for "micro".</div></p> <p>The Group Decode ROM's outputs during Second Clock control the decoding. Most importantly, the <code>ADD imm</code> instruction used microcode; it is not a one-byte logic instruction (<code>1BL</code>). Moreover, it does not have a Mod R/M byte, so it does not need two bytes for decoding (<code>2BR</code>). For a <code>1BL</code> instruction, microcode execution would be blocked and the next instruction would be immediately fetched. On the other hand, for a <code>2BR</code> instruction, the loader would tell the prefetch queue that it was done with the second byte during the second half of Second Clock. Microcode execution would be blocked during the third cycle and the fourth cycle would execute a microcode subroutine to determine the memory address.</p> <p>For more details, see my article on the <a href="https://www.righto.com/2023/01/the-8086-processors-microcode-pipeline.html">8086 pipeline</a>.</p> <h2>Interrupts</h2> <p>The Group Decode ROM takes the 8 bits of the instruction as inputs, but it has an additional input indicating that an interrupt is being handled. This signal blocks most of the Group Decode ROM outputs. This prevents the current instruction's outputs from interfering with interrupt handling. I wrote about the 8086's interrupt handling in detail <a href="https://www.righto.com/2023/02/8086-interrupt.html">here</a>, so I won't go into more detail in this post.</p> <h2>Conclusions</h2> <p>The Group Decode ROM indicates one of the key differences between CISC processors (Complex Instruction Set Computer) such as the 8086 and the RISC processors (Reduced Instruction Set Computer) that became popular a few years later. A RISC instruction set is designed to make instruction decoding very easy, with a small number of uniform instruction forms. On the other hand, the 8086's CISC instruction set was designed for compactness and high code density. As a result, instructions are squeezed into the available opcode space. Although there is a lot of structure to the 8086 opcodes, this structure is full of special cases and any patterns only apply to a subset of the instructions. The Group Decode ROM brings some order to this chaotic jumble of instructions, and the number of outputs from the Group Decode ROM is a measure of the instruction set's complexity.</p> <p>The 8086's instruction set was extended over the decades to become the x86 instruction set in use today. During that time, more layers of complexity were added to the instruction set. Now, an x86 instruction can be up to 15 bytes long with multiple prefixes. Some prefixes change the register encoding or indicate a completely different instruction set such as <code>VEX</code> (Vector Extensions) or <code>SSE</code> (Streaming SIMD Extensions). Thus, x86 instruction decoding is very difficult, especially when trying to decode multiple instructions in parallel. This has an impact in modern systems, where x86 processors typically have 4 complex instruction decoders while Apple's ARM processors have 8 simpler decoders; this is <a href="https://debugger.medium.com/why-is-apples-m1-chip-so-fast-3262b158cba2">said</a> to give Apple a performance benefit. Thus, architectural decisions from 45 years ago are still impacting the performance of modern processors.</p> <p>I've written numerous <a href="https://www.righto.com/search/label/8086">posts on the 8086</a> so far and plan to continue reverse-engineering the 8086 die so follow me on Twitter <a href="https://twitter.com/kenshirriff">@kenshirriff</a> or <a href="http://www.righto.com/feeds/posts/default">RSS</a> for updates. I've also started experimenting with Mastodon recently as <a href="https://oldbytes.space/@kenshirriff">@<span class="__cf_email__" data-cfemail="acc7c9c2dfc4c5dedec5cacaecc3c0c8ced5d8c9df82dfdccdcfc9">[email protected]</span></a>. Thanks to Arjan Holscher for suggesting this topic.</p> <h2>Notes and references</h2> <div class="footnote"> <ol> <li id="fn:rom"> <p>You might wonder what the difference is between a ROM and a PLA. Both of them produce arbitrary outputs for a set of inputs. Moreover, you can replace a PLA with a ROM or vice versa. Typically a ROM has all the input combinations decoded, so it has a separate row for each input value, i.e. 2<sup>N</sup> rows. So you can think of a ROM as a fully-decoded PLA.</p> <p>Some ROMs are partially decoded, allowing identical rows to be combined and reducing the size of the ROM. This technique is used in the 8086 microcode, for instance. A partially-decoded ROM is fairly similar to a PLA, but the technical distinction is that a ROM has only one output row active at a time, while a PLA can have multiple output rows active and the results are OR'd together. (This definition is from <a href="https://amzn.to/3pzucaS">The Architecture of Microprocessors</a> p117.)</p> <p>The Group Decode ROM, however, has a few cases where multiple rows are active at the same time (for instance the segment register <code>POP</code> instructions). Thus, the Group Decode ROM is technically a PLA and not a ROM. This distinction isn't particularly important, but you might find it interesting. <a class="footnote-backref" href="#fnref:rom" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:36"> <p>The Group Decode ROM has 38 columns, but two columns (11 and 35) are unused. Presumably, these were provided as spares in case a bug fix or modification required additional decoding. <a class="footnote-backref" href="#fnref:36" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:octal"> <p>Like the 8008 and 8080, the 8086's instruction set was designed around a 3-bit octal structure. Thus, the 8086 instruction set makes much more sense if viewed in octal instead of hexadecimal. The table below shows the instructions with an octal organization. Each 8×8 block uses the two low octal digits, while the four large blocks are positioned according to the top octal digit (labeled). As you can see, the instruction set has a lot of structure that is obscured in the usual hexadecimal table.</p> <p><a href="https://static.righto.com/images/8086-group/grid-octal.jpg"><img alt="The 8086 instruction set, put in a table according to the octal opcode value." class="hilite" height="647" src="https://static.righto.com/images/8086-group/grid-octal-w600.jpg" title="The 8086 instruction set, put in a table according to the octal opcode value." width="600" /></a><div class="cite">The 8086 instruction set, put in a table according to the octal opcode value.</div></p> <p>For details on the octal structure of the 8086 instruction set, see <a href="https://gist.github.com/seanjensengrey/f971c20d05d4d0efc0781f2f3c0353da">The 80x86 is an Octal Machine</a>. <a class="footnote-backref" href="#fnref:octal" title="Jump back to footnote 3 in the text">↩</a></p> </li> </ol> </div> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'><span class='post-comment-link'> <a class='comment-link' href='https://www.blogger.com/comment/fullpage/post/6264947694886887540/7453359098571162761' onclick=''> 3 comments: </a> </span> <span class='post-icons'> <span class='item-action'> <a href='https://www.blogger.com/email-post.g?blogID=6264947694886887540&postID=7453359098571162761' title='Email Post'> <img alt='' class='icon-action' height='13' src='http://img1.blogblog.com/img/icon18_email.gif' width='18'/> </a> </span> <span class='item-control blog-admin pid-1138732533'> <a href='https://www.blogger.com/post-edit.g?blogID=6264947694886887540&postID=7453359098571162761&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <span class='post-backlinks post-comment-link'> </span> <div class='post-share-buttons goog-inline-block'> <a class='goog-inline-block share-button sb-email' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=7453359098571162761&target=email' target='_blank' title='Email This'><span class='share-button-link-text'>Email This</span></a><a class='goog-inline-block share-button sb-blog' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=7453359098571162761&target=blog' onclick='window.open(this.href, "_blank", "height=270,width=475"); return false;' target='_blank' title='BlogThis!'><span class='share-button-link-text'>BlogThis!</span></a><a class='goog-inline-block share-button sb-twitter' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=7453359098571162761&target=twitter' target='_blank' title='Share to X'><span class='share-button-link-text'>Share to X</span></a><a class='goog-inline-block share-button sb-facebook' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=7453359098571162761&target=facebook' onclick='window.open(this.href, "_blank", "height=430,width=640"); return false;' target='_blank' title='Share to Facebook'><span class='share-button-link-text'>Share to Facebook</span></a><a class='goog-inline-block share-button sb-pinterest' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=7453359098571162761&target=pinterest' target='_blank' title='Share to Pinterest'><span class='share-button-link-text'>Share to Pinterest</span></a> </div> </div> <div class='post-footer-line post-footer-line-2'><span class='post-labels'> Labels: <a href='http://www.righto.com/search/label/8086' rel='tag'>8086</a>, <a href='http://www.righto.com/search/label/intel' rel='tag'>intel</a>, <a href='http://www.righto.com/search/label/microcode' rel='tag'>microcode</a>, <a href='http://www.righto.com/search/label/reverse-engineering' rel='tag'>reverse-engineering</a> </span> </div> <div class='post-footer-line post-footer-line-3'></div> </div> </div> </div> </div></div> <div class="date-outer"> <div class="date-posts"> <div class='post-outer'> <div class='post hentry' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <meta content='https://static.righto.com/images/8086-mul/die-labeled-w600.jpg' itemprop='image_url'/> <meta content='6264947694886887540' itemprop='blogId'/> <meta content='8390323699966089555' itemprop='postId'/> <a name='8390323699966089555'></a> <h3 class='post-title entry-title' itemprop='name'> <a href='http://www.righto.com/2023/03/8086-multiplication-microcode.html'>Reverse-engineering the multiplication algorithm in the Intel 8086 processor</a> </h3> <div class='post-header'> <div class='post-header-line-1'></div> </div> <div class='post-body entry-content' id='post-body-8390323699966089555' itemprop='description articleBody'> <style> .hilite {cursor:zoom-in} a:link img.hilite, a:visited img.hilite {color: #fff;} a:hover img.hilite {color: #f66;} </style> <style> pre.microcode {font-family: courier, fixed; padding: 10px; background-color: #f5f5f5; display:inline-block;border:none;} pre.microcode span {color: green; font-style:italic; font-family: sans-serif; font-size: 90%;} </style> <p>While programmers today take multiplication for granted, most microprocessors in the 1970s could only add and subtract — multiplication required a slow and tedious loop implemented in assembly code.<span id="fnref:mul"><a class="ref" href="#fn:mul">1</a></span> One of the nice features of the Intel 8086 processor (1978) was that it provided machine instructions for multiplication,<span id="fnref:division"><a class="ref" href="#fn:division">2</a></span> able to multiply 8-bit or 16-bit numbers with a single instruction. Internally, the 8086 still performed a loop, but the loop was implemented in microcode: faster and transparent to the programmer. Even so, multiplication was a slow operation, about 24 to 30 times slower than addition.</p> <p>In this blog post, I explain the multiplication process inside the 8086, analyze the microcode that it used, and discuss the hardware circuitry that helped it out.<span id="fnref:microcode"><a class="ref" href="#fn:microcode">3</a></span> My analysis is based on reverse-engineering the 8086 from die photos. The die photo below shows the chip under a microscope. I've labeled the key functional blocks; the ones that are important to this post are darker. At the left, the ALU (Arithmetic/Logic Unit) performs the arithmetic operations at the heart of multiplication: addition and shifts. Multiplication also uses a few other hardware features: the X register, the F1 flag, and a loop counter. The microcode ROM at the lower right controls the process.</p> <p><a href="https://static.righto.com/images/8086-mul/die-labeled.jpg"><img alt="The 8086 die under a microscope, with main functional blocks labeled. This photo shows the chip with the metal and polysilicon removed, revealing the silicon underneath. Click on this image (or any other) for a larger version." class="hilite" height="592" src="https://static.righto.com/images/8086-mul/die-labeled-w600.jpg" title="The 8086 die under a microscope, with main functional blocks labeled. This photo shows the chip with the metal and polysilicon removed, revealing the silicon underneath. Click on this image (or any other) for a larger version." width="600" /></a><div class="cite">The 8086 die under a microscope, with main functional blocks labeled. This photo shows the chip with the metal and polysilicon removed, revealing the silicon underneath. Click on this image (or any other) for a larger version.</div></p> <h2>Microcode</h2> <p>The multiplication routines in the 8086 are implemented in microcode. Most people think of machine instructions as the basic steps that a computer performs. However, many processors (including the 8086) have another layer of software underneath: microcode. With microcode, instead of building the control circuitry from complex logic gates, the control logic is largely replaced with code. To execute a machine instruction, the computer internally executes several simpler micro-instructions, specified by the microcode. This is especially useful for a machine instruction such as multiplication, which requires many steps in a loop.</p> <!-- The 8086 uses a hybrid approach: although it uses microcode, much of the instruction functionality is implemented with gate logic. This approach removed duplication from the microcode and kept the microcode small enough for 1978 technology. In a sense, the microcode is parameterized. For instance, the microcode can specify a generic Arithmetic/Logic Unit (ALU) operation and a generic register. The gate logic examines the instruction to determine which specific operation to perform and the appropriate register. --> <p>A micro-instruction in the 8086 is encoded into 21 bits as shown below. Every micro-instruction has a move from a source register to a destination register, each specified with 5 bits. The meaning of the remaining bits depends on the type field and can be anything from an ALU operation to a memory read or write to a change of microcode control flow. Thus, an 8086 micro-instruction typically does two things in parallel: the move and the action. For more about 8086 microcode, see my <a href="https://www.righto.com/2022/11/how-8086-processors-microcode-engine.html">microcode blog post</a>.</p> <p><a href="https://static.righto.com/images/8086-mul/microcode-format.jpg"><img alt="The encoding of a micro-instruction into 21 bits. Based on NEC v. Intel: Will Hardware Be Drawn into the Black Hole of Copyright?" class="hilite" height="203" src="https://static.righto.com/images/8086-mul/microcode-format-w700.jpg" title="The encoding of a micro-instruction into 21 bits. Based on NEC v. Intel: Will Hardware Be Drawn into the Black Hole of Copyright?" width="700" /></a><div class="cite">The encoding of a micro-instruction into 21 bits. Based on <a href="https://digitalcommons.law.scu.edu/cgi/viewcontent.cgi?referer=&httpsredir=1&article=1031&context=chtlj">NEC v. Intel: Will Hardware Be Drawn into the Black Hole of Copyright?</a></div></p> <p>The behavior of an ALU micro-operation is important for multiplication. The ALU has three temporary registers that are invisible to the programmer: tmpA, tmpB, and tmpC. An ALU operation takes its first argument from any temporary register, while the second argument always comes from tmpB. An ALU operation requires two micro-instructions. The first micro-instruction specifies the ALU operation and source register, configuring the ALU. For instance, <code>ADD tmpA</code> to add tmpA to the default tmpB. In the next micro-instruction (or a later one), the ALU result can be accessed through the <code>危</code> register and moved to another register.</p> <p>Before I get into the microcode routines, I should explain two ALU operations that play a central role in multiplication: <code>LRCY</code> and <code>RRCY</code>, Left Rotate through Carry and Right Rotate through Carry. (These correspond to the <code>RCL</code> and <code>RCR</code> machine instructions, which rotate through carry left or right.) These operations shift the bits in a 16-bit word, similar to the <code><<</code> and <code>>></code> bit-shift operations in high-level languages, but with an additional feature. Instead of discarding the bit on the end, that bit is moved into the carry flag (<code>CF</code>). Meanwhile, the bit formerly in the carry flag moves into the word. You can think of this as rotating the bits while treating the carry flag as a 17th bit of the word.</p> <p><a href="https://static.righto.com/images/8086-mul/rotates.jpg"><img alt="The left rotate through carry and right rotate through carry micro-instructions." class="hilite" height="172" src="https://static.righto.com/images/8086-mul/rotates-w300.jpg" title="The left rotate through carry and right rotate through carry micro-instructions." width="300" /></a><div class="cite">The left rotate through carry and right rotate through carry micro-instructions.</div></p> <p>These shifts perform an important part of the multiplication process since shifting can be viewed as multiplying by two. <code>LRCY</code> also provides a convenient way to move the most-significant bit to the carry flag, where it can be tested for a conditional jump. (This is important because the top bit is used as the sign bit.) Similarly, <code>RRCY</code> provides access to the least significant bit, very important for the multiplication process. Another important property is that performing <code>RRCY</code> on an upper word and then <code>RRCY</code> on a lower word will perform a 32-bit shift, since the low bit of the upper word will be moved into the high bit of the lower word via the carry bit.</p> <h2>Binary multiplication</h2> <p>The shift-and-add method of multiplication (below) is similar to grade-school long multiplication, except it uses binary instead of decimal. In each row, the multiplicand is multiplied by one digit of the multiplier. (The multiplicand is the value that gets repeatedly added, and the multiplier controls how many times it gets added.) Successive rows are shifted left one digit. At the bottom, the rows are added together to yield the product. The example below shows how 6×5 is calculated in binary using long multiplication.</p> <style type="text/css"> div.mul {font-family: "courier-new", courier, monospace} div.mul .top {border-bottom: 1px solid #ccc;} div.mul .sum {border-top: 1px solid #ccc;} div.mul .dim {color: #aaa;} </style> <div class="mul"> <span> 0110</span> <br/><span class="top"> ×0101</span> <br/><span> 0110</span> <br/><span class="dim"> 0000</span> <br/><span> 0110</span> <br/><span class="dim"> 0000</span> <br/><span class="sum">00011110</span> </div> <p>Binary long multiplication is much simpler than decimal multiplication: at each step, you're multiplying by 0 or 1. Thus, each row is either zero or the multiplicand appropriately shifted (0110 in this case). (Unlike decimal long multiplication, you don't need to know the multiplication table.) This simplifies the hardware implementation, since each step either adds the multiplicand or doesn't. In other words, each step tests a bit of the multiplier, starting with the low bit, to determine if an add should take place or not. This bit can be obtained by shifting the multiplier one bit to the right each step.</p> <p>Although the diagram above shows the sum at the end, a real implementation performs the addition at each step of the loop, keeping a running total. Moreover, in the 8086, instead of shifting the multiplicand to the left during each step, the sum shifts to the right. (The result is the same but it makes the implementation easier.) Thus, multiplying 6×5 goes through the steps below.</p> <div class="mul"> <span> 0101</span> <br/><span class="top"> ×0110</span> <br/><span> 00000</span> <br/><span> 001010</span> <br/><span> 0011110</span> <br/><span> 00011110</span> </div> <p>Why would you shift the result to the right? There's a clever reason for this. Suppose you're multiplying two 16-bit numbers, which yields a 32-bit result. That requires four 16-bit words of storage if you use the straightforward approach. But if you look more closely, the first sum fits into 16 bits, and then you need one more bit at each step. Meanwhile, you're "using up" one bit of the multiplier at each step. So if you squeeze the sum and the multiplier together, you can fit them into two words. Shifting right accomplishes this, as the diagram below illustrates for <code>0xffff</code>×<code>0xf00f</code>. The sum (blue) starts in a 16-bit register called <code>tmpA</code> while the multiplier (green) is stored in the 16-bit <code>tmpB</code> register. In each step, they are both shifted right, so the sum gains one bit and the multiplier loses one bit. By the end, the sum takes up all 32 bits, split across both registers.</p> <style type="text/css"> table.mul {border-collapse: collapse; border: 1px solid #888; font-family: "courier-new", courier, monospace; padding: 2px; font-size:90%;} table.mul th {border-bottom: 1px solid #444;} table.mul th:nth-child(1) {border-left: 1px solid #444; border-right: 2px solid #444;} table.mul th:nth-child(3) {border-left: 1px solid #444;} table.mul th:nth-child(4) {padding: 0 4px;} table.mul tr:nth-child(2) {border-bottom: 1px solid #444;} table.mul tr:nth-child(2) {border-bottom: 1px solid #444;} table.mul td:nth-child(8) {border-right: 1px solid #444;} table.mul td:nth-child(16) {border-right: 2px solid #444;} table.mul td:nth-child(24) {border-right: 1px solid #444;} table.mul td {padding: 0px 4px;} table.mul td.a {background-color: #ddf;} table.mul td.b {background-color: #cfc;} </style> <table class="mul"> <tr style="font-family: sans-serif; color: blue"><th colspan=16>sum (tmpA)</th><th colspan=16 style="font-family:sans-serif; color: green">multiplier (tmpC)</th></tr> <tr><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td></tr> <tr><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">1</td><td class="b">1</td><td class="b">1</td></tr> <tr><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">1</td><td class="b">1</td></tr> <tr><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">1</td></tr> <tr><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td></tr> <tr><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td></tr> <tr><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td></tr> <tr><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td></tr> <tr><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">0</td><td class="b">0</td><td class="b">0</td><td class="b">0</td></tr> <tr><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">0</td><td class="b">0</td><td class="b">0</td></tr> <tr><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">0</td><td class="b">0</td></tr> <tr><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">0</td></tr> <tr><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td></tr> <tr><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="b">1</td><td class="b">1</td><td class="b">1</td></tr> <tr><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="b">1</td><td class="b">1</td></tr> <tr><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="b">1</td></tr> <tr><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td></tr> </table> <h2>The multiplication microcode</h2> <p>The 8086 has four multiply instructions to handle signed and unsigned multiplication of byte and word operands. These machine instructions are implemented in microcode. I'll start by describing the unsigned word multiplication, which multiplies two 16-bit values and produces a 32-bit result. The source word is provided by either a register or memory. It is multiplied by <code>AX</code>, the accumulator register. The 32-bit result is returned in the <code>DX</code> and <code>AX</code> registers.</p> <p>The microcode below is the main routine for word multiplication, both signed and unsigned. Each micro-instruction specifies a register move on the left, and an action on the right. The moves transfer words between the visible registers and the ALU's temporary registers, while the actions are mostly subroutine calls to other micro-routines.</p> <pre class="microcode"> move action AX → tmpC LRCY tmpC <span><b>iMUL rmw:</b></span> M → tmpB CALL X0 PREIMUL <span>called for signed multiplication</span> CALL CORX <span>the core routine</span> CALL F1 NEGATE <span>called for negative result</span> CALL X0 IMULCOF <span>called for signed multiplication</span> tmpC → AX JMPS X0 7 CALL MULCOF <span>called for unsigned multiplication</span> tmpA → DX RNI </pre> <p>The microcode starts by moving one argument <code>AX</code> into the ALU's temporary C register and setting up the ALU to perform a Left Rotate through Carry on this register, in order to access the sign bit. Next, it moves the second argument <code>M</code> into the temporary B register; <code>M</code> references the register or memory specified in the second byte of the instruction, the "ModR/M" byte. For a signed multiply instruction, the <code>PREIMUL</code> micro-subroutine is called, but I'll skip that for now. (The <code>X0</code> condition tests bit 3 of the instruction, which in this case distinguishes <code>MUL</code> from <code>IMUL</code>.) Next, the <code>CORX</code> subroutine is called, which is the heart of the multiplication.<span id="fnref:corx"><a class="ref" href="#fn:corx">4</a></span> If the result needs to be negated (indicated by the <code>F1</code> condition), the <code>NEGATE</code> micro-subroutine is called. For signed multiplication, <code>IMULCOF</code> is then called to set the carry and overflow flags, while <code>MULCOF</code> is called for unsigned multiplication. Meanwhile, the result bytes are moved from the temporary C and temporary registers to the <code>AX</code> and <code>DX</code> registers. Finally, <code>RNI</code> runs the next machine instruction, ending the microcode routine.</p> <h3><code>CORX</code></h3> <p>The heart of the multiplication code is the <code>CORX</code> routine, which performs the multiplication loop, computing the product through shifts and adds. The first two lines set up the loop, initializing the sum (tmpA) to 0. The number of loops is controlled by a special-purpose loop counter. The <code>MAXC</code> micro-instruction initializes the counter to 7 or 15, for a byte or word multiply respectively. The first shift of tmpC is performed, putting the low bit into the carry flag.</p> <p>The loop body performs the shift-and-add step. It tests the carry flag, the low bit of the multiplicand. It skips over the <code>ADD</code> if there is no carry (<code>NCY</code>). Otherwise, tmpB is added to tmpA. (As tmpA gets shifted to the right, tmpB gets added to higher and higher positions in the result.) The tmpA and tmpC registers are rotated right. This also puts the next bit of the multiplicand into the carry flag for the next cycle. The microcode jumps to the top of the loop if the counter is not zero (<code>NCZ</code>). Otherwise, the subroutine returns with the result in tmpA and tmpC.</p> <pre class="microcode"> ZERO → tmpA RRCY tmpC <span><b>CORX:</b> initialize right rotate</span> 危 → tmpC MAXC <span> get rotate result, initialize counter to max value</span> JMPS NCY 8 <span><b>5:</b> top of loop</span> ADD tmpA <span>conditionally add</span> 危 → tmpA F <span>sum to tmpA, update flags to get carry</span> RRCY tmpA <span><b>8:</b> 32-bit shift of tmpA/tmpC</span> 危 → tmpA RRCY tmpC 危 → tmpC JMPS NCZ 5 <span>loop to 5 if counter is not 0</span> RTN </pre> <h3><code>MULCOF</code></h3> <p>The last subroutine is <code>MULCOF</code>, which configures the carry and overflow flags. The 8086 uses the rule that if the upper half of the result is nonzero, the carry and overflow flags are set, otherwise they are cleared. The first two lines pass tmpA (the upper half of the result) through the ALU to set the zero flag for the conditional jump. As a side-effect, the other status flags will get set but these values are "undefined" in the documentation.<span id="fnref:flags"><a class="ref" href="#fn:flags">6</a></span> If the test is nonzero, the carry and overflow flags are set (<code>SCOF</code>), otherwise they are cleared (<code>CCOF</code>).<span id="fnref:overflow"><a class="ref" href="#fn:overflow">5</a></span> The <code>SCOF</code> and <code>CCOF</code> micro-operations were implemented solely for used by multiplication, illustrating how microcode can be designed around specific needs.</p> <pre class="microcode"> PASS tmpA <span><b>MULCOF:</b> pass tmpA through to test if zero</span> 危 → no dest JMPS 12 F <span>update flags</span> JMPS Z 8 <span><b>12:</b> jump if zero</span> SCOF RTN <span>otherwise set carry and overflow</span> CCOF RTN <span><b>8:</b> clear carry and overflow</span> </pre> <h2>8-bit multiplication</h2> <p>The 8086 has separate instructions for 8-bit multiplication. The process for 8-bit multiplication is similar to 16-bit multiplication, except the values are half as long and the shift-and-add loop executes 8 times instead of 16. As shown below, the 8-bit sum starts in the low half of the temporary A register and is shifted left into tmpC. Meanwhile, the 8-bit multiplier starts in the low half of tmpC and is shifted out to the right. At the end, the result is split between tmpA and tmpC.</p> <table class="mul"> <tr style="font-family: sans-serif; color: blue"><th colspan=16>sum (tmpA)</th><th colspan=16 style="font-family:sans-serif; color: green">multiplier (tmpC)</th></tr> <tr><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td class="a">0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="b">0</td><td class="b">1</td><td class="b">0</td><td class="b">1</td><td class="b">0</td><td class="b">1</td><td class="b">0</td><td class="b">1</td></tr> <tr><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="b">0</td><td class="b">1</td><td class="b">0</td><td class="b">1</td><td class="b">0</td><td class="b">1</td><td class="b">0</td></tr> <tr><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="b">0</td><td class="b">1</td><td class="b">0</td><td class="b">1</td><td class="b">0</td><td class="b">1</td></tr> <tr><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="b">0</td><td class="b">1</td><td class="b">0</td><td class="b">1</td><td class="b">0</td></tr> <tr><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="b">0</td><td class="b">1</td><td class="b">0</td><td class="b">1</td></tr> <tr><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="b">0</td><td class="b">1</td><td class="b">0</td></tr> <tr><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="b">0</td><td class="b">1</td></tr> <tr><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="b">0</td></tr> <tr><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">0</td><td class="a">1</td><td class="a">1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td></tr> </table> <p>The 8086 supports many instructions with byte and word versions, using 8-bit or 16-bit arguments. In most cases, the byte and word instructions use the same microcode, with the ALU and register hardware using bytes or words based on the instruction. However, the byte- and word-multiply instructions use different registers, requiring microcode changes. In particular, the multiplier is in <code>AL</code>, the low half of the accumulator. At the end, the 16-bit result is returned in <code>AX</code>, the full 16-bit accumulator; two micro-instructions assemble the result from tmpC and tmpA into the two bytes of the accumulator, 'AL' and 'AH' respectively. Apart from those changes, the microcode is the same as the word multiply microcode discussed earlier.</p> <pre class="microcode"> AL → tmpC LRCY tmpC <span><b>iMUL rmb:</b></span> M → tmpB CALL X0 PREIMUL CALL CORX CALL F1 NEGATE CALL X0 IMULCOF tmpC → AL JMPS X0 7 CALL MULCOF tmpA → AH RNI </pre> <h2>Signed multiplication</h2> <p>The 8086 (like most computers) represents signed numbers using a format called two's complement. While a regular byte holds a number from 0 to 255, a signed byte holds a number from -128 to 127. A negative number is formed by flipping all the bits (known as the one's complement) and then adding 1, yielding the two's complement value.<span id="fnref:complement"><a class="ref" href="#fn:complement">7</a></span> For instance, +5 is <code>0x05</code> while -5 is <code>0xfb</code>. (Note that the top bit of a number is set for a negative number; this is the sign bit.) The nice thing about two's complement numbers is that the same addition and subtraction operations work on both signed and unsigned values. Unfortunately, this is not the case for signed multiplication, since signed and unsigned values yield different results due to sign extension.</p> <p>The 8086 has separate multiplication instructions <code>IMUL</code> (Integer Multiply) to perform signed multiplication. The 8086 performs signed multiplication by converting the arguments to positive values, performing unsigned multiplication, and then negating the result if necessary. As shown above, signed and unsigned multiplication both use the same microcode, but the microcode conditionally calls some subroutines for signed multiplication. I will discuss those micro-subroutines below.</p> <h3><code>PREIMUL</code></h3> <p>The first subroutine for signed multiplication is <code>PREIMUL</code>, performing preliminary operations for integer multiplication. It converts the two arguments, stored in tmpC and tmpB, to positive values. It keeps track of the signs using an internal flag called <code>F1</code>, toggling this flag for a negative argument. This conveniently handles the rule that two negatives make a positive since complementing the <code>F1</code> flag twice will clear it.</p> <p>This microcode, below, illustrates the complexity of microcode and how micro-operations are carefully arranged to get the right values at the right time. The first micro-instruction performs one ALU operation and sets up a second operation. The calling code had set up the ALU to perform <code>LRCY tmpC</code>, so that's the result returned by 危 (and discarded). Performing a left rotate and discarding the result may seem pointless, but the important side-effect is that the top bit (i.e. the sign bit) ends up in the carry flag. The microcode does not have a conditional jump based on the sign, but has a conditional jump based on carry, so the point is to test if tmpC is negative. The first micro-instruction also sets up negation (<code>NEG tmpC</code>) for the <em>next</em> ALU operation.</p> <pre class="microcode"> 危 → no dest NEG tmpC <span><b>PREIMUL:</b> set up negation of tmpC</span> JMPS NCY 7 <span>jump if tmpC positive</span> 危 → tmpC CF1 <span>if negative, negate tmpC, flip F1</span> JMPS 7 <span>jump to shared code</span> LRCY tmpB <span><b>7:</b></span> 危 → no dest NEG tmpB <span>set up negation of tmpB</span> JMPS NCY 11 <span>jump if tmpB positive</span> 危 → tmpB CF1 RTN <span>if negative, negate tmpB, flip F1</span> RTN <span><b>11:</b> return</span> </pre> <p>For the remaining lines, if the carry is clear (<code>NCY</code>), the next two lines are skipped. Otherwise, the ALU result (<code>危</code>) is written to tmpC, making it positive, and the <code>F1</code> flag is complemented with <code>CF1</code>. (The second short jump (<code>JMPS</code>) may look unnecessary, but I reordered the code for clarity.) The second half of the microcode performs a similar test on tmpB. If tmpB is negative, it is negated and <code>F1</code> is toggled.</p> <h3><code>NEGATE</code></h3> <p>The microcode below is called after computing the result, if the result needs to be made negative. Negation is harder than you might expect because the result is split between the tmpA and tmpC registers. The two's complement operation (<code>NEG</code>) is applied to the low word, while either 2's complement or one's complement (<code>COM1</code>) is applied to the upper word, depending on the carry for mathematical reasons.<span id="fnref:neg"><a class="ref" href="#fn:neg">8</a></span> The code also toggles F1 and makes tmpB positive; I think this code is only useful for division, which also uses the <code>NEGATE</code> subroutine.</p> <pre class="microcode"> NEG tmpC <span><b>NEGATE:</b> negate tmpC</span> 危 → tmpC COM1 tmpA F <span>maybe complement tmpA</span> JMPS CY 6 NEG tmpA <span>negate tmpA if there's no carry</span> 危 → tmpA CF1 <span><b>6:</b> toggle F1 for some reason</span> LRCY tmpB <span><b>7:</b> test sign of tmpB</span> 危 → no dest NEG tmpB <span>maybe negate tmpB</span> JMPS NCY 11 <span>skip if tmpB positive</span> 危 → tmpB CF1 RTN <span>else negate tmpB, toggle F1</span> RTN <span><b>11:</b> return</span> </pre> <h3><code>IMULCOF</code></h3> <p>The <code>IMULCOF</code> routine is similar to <code>MULCOF</code>, but the calculation is a bit trickier for a signed result. This routine sets the carry and overflow flags if the upper half of the result is significant, that is, it is not just the sign extension of the lower half.<span id="fnref:imulcof"><a class="ref" href="#fn:imulcof">9</a></span> In other words, the top byte is not significant if it duplicates the top bit (the sign bit) of the lower byte. The trick in the microcode is to add the top bit of the lower byte to the upper byte by putting it in the carry flag and performing an add with carry (<code>ADC</code>) of 0. If the result is 0, the upper byte is not significant, handling the positive and negative cases. (This also holds for words instead of bytes.)</p> <pre class="microcode"> ZERO → tmpB LRCY tmpC <span><b>IMULCOF:</b> get top bit of tmpC</span> 危 → no dest ADC tmpA <span>add to tmpA and 0 (tmpB)</span> 危 → no dest F <span>update flags</span> JMPS Z 8 <span><b>12: jump if zero result</b></span> SCOF RTN <span>otherwise set carry and overflow</span> CCOF RTN <span><b>8:</b> clear carry and overflow</span> </pre> <h2>The hardware for multiplication</h2> <p>For the most part, the 8086 uses the regular ALU addition and shifts for the multiplication algorithm. Some special hardware features provide assistance.</p> <h3>Loop counter</h3> <p>The 8086 has a special 4-bit loop counter for multiplication. This counter starts at 7 for byte multiplication and 15 for word multiplication, based on the instruction. This loop counter allows the microcode to decrement the counter, test for the end, and perform a conditional branch in one micro-operation. The counter is implemented with four flip-flops, along with logic to compute the value after decrementing by one. The <code>MAXC</code> (Maximum Count) micro-instruction sets the counter to 7 or 15 for byte or word operations respectively. The <code>NCZ</code> (Not Counter Zero) micro-instruction has two actions. First, it performs a conditional jump if the counter is nonzero. Second, it decrements the counter.</p> <h3>X register</h3> <p>The multiplication microcode uses an internal register called the <code>X</code> register to distinguish between the <code>MUL</code> and <code>IMUL</code> instructions. The <code>X</code> register is a 3-bit register that holds the ALU opcode, indicated by bits 5–3 of the instruction.<span id="fnref:x-reg"><a class="ref" href="#fn:x-reg">10</a></span> Since the instruction is held in the Instruction Register, you might wonder why a separate register is required. The motivation is that some opcodes specify the type of ALU operation in the second byte of the instruction, the ModR/M byte, bits 5–3.<span id="fnref:opcode"><a class="ref" href="#fn:opcode">11</a></span> Since the ALU operation is sometimes specified in the first byte and sometimes in the second byte, the <code>X</code> register was added to handle both these cases.</p> <p>For the most part, the <code>X</code> register indicates which of the eight standard ALU operations is selected (<code>ADD</code>, <code>OR</code>, <code>ADC</code>, <code>SBB</code>, <code>AND</code>, <code>SUB</code>, <code>XOR</code>, <code>CMP</code>). However, a few instructions use bit 0 of the <code>X</code> register to distinguish between other pairs of instructions. For instance, it distinguishes between <code>MUL</code> and <code>IMUL</code>, <code>DIV</code> and <code>IDIV</code>, <code>CMPS</code> and <code>SCAS</code>, <code>MOVS</code> and <code>LODS</code>, or <code>AAA</code> and <code>AAS</code>. While these instruction pairs may appear to have arbitrary opcodes, they have been carefully assigned. The microcode can test this bit using the <code>X0</code> condition and perform conditional jumps.</p> <p>The implementation of the <code>X</code> register is straightforward, consisting of three flip-flops to hold the three bits of the instruction. The flip-flops are loaded from the prefetch queue bus during First Clock and during Second Clock for appropriate instructions, as the instruction bytes travel over the bus. Testing bit 0 of the <code>X</code> register with the <code>X0</code> condition is supported by the microcode condition evaluation circuitry, so it can be used for conditional jumps in the microcode.</p> <h3>The F1 flag</h3> <p>The multiplication microcode uses an internal flag called <code>F1</code>,<span id="fnref:f1"><a class="ref" href="#fn:f1">12</a></span> which has two distinct uses. The flag keeps track of a <code>REP</code> prefix for use with a string operation. But the <code>F1</code> flag is also used by signed multiplication and division to keep track of the sign. The <code>F1</code> flag can be toggled by microcode through the <code>CF1</code> (Complement F1) micro-instruction. The <code>F1</code> flag is implemented with a flip-flop, along with a multiplexer to select the value. It is cleared when a new instruction starts, set by a <code>REP</code> prefix, and toggled by the <code>CF1</code> micro-instruction.</p> <p>The diagram below shows how the F1 latch and the loop counter appear on the die. In this image, the metal layer has been removed, showing the silicon and the polysilicon wiring underneath.</p> <p><a href="https://static.righto.com/images/8086-mul/counter.jpg"><img alt="The counter and F1 latch as they appear on the die. The latch for the REP state is also here." class="hilite" height="308" src="https://static.righto.com/images/8086-mul/counter-w600.jpg" title="The counter and F1 latch as they appear on the die. The latch for the REP state is also here." width="600" /></a><div class="cite">The counter and F1 latch as they appear on the die. The latch for the REP state is also here.</div></p> <h2>Later advances in multiplication</h2> <p>The 8086 was pretty slow at multiplying compared to later Intel processors.<span id="fnref:performance"><a class="ref" href="#fn:performance">13</a></span> The 8086 took up to 133 clock cycles to multiply unsigned 16-bit values due to the complicated microcode loops. By 1982, the Intel 286 processor cut this time down to 21 clock cycles. The Intel 486 (1989) used an improved algorithm that could end early, so multiplying by a small number could take just 9 cycles.</p> <p>Although these optimizations improved performance, they still depended on looping over the bits. With the shift to 32-bit processors, the loop time became unwieldy. The solution was to replace the loop with hardware: instead of performing 32 shift-and-add loops, an array of adders could compute the multiplication in one step. This quantity of hardware was unreasonable in the 8086 era, but as Moore's law made transistors smaller and cheaper, hardware multiplication became practical. For instance, the Cyrix Cx486SLC (1992) had a 16-bit hardware multiplier that cut word multiply down to 3 cycles. The Intel Core 2 (2006) was even faster, able to complete a 32-bit multiplication every clock cycle.</p> <!-- Manual page 26-160 http://www.bitsavers.org/components/intel/80486/i486_Processor_Programmers_Reference_Manual_1990.pdf --> <!-- https://techmonitor.ai/technology/cyrix_promises_misery_for_intel_with_its_cx486slc --> <!-- Pentium: alpert 1993 --> <p>Hardware multiplication is a fairly complicated subject, with many optimizations to maximize performance while minimizing hardware.<span id="fnref:hardware"><a class="ref" href="#fn:hardware">14</a></span> Simply replacing the loop with a sequence of 32 adders is too slow because the result would be delayed while propagating through all the adders. The solution is to arrange the adders as a tree to provide parallelism. The first layer has 16 adders to add pairs of terms. The next layer adds pairs of these partial sums, and so forth. The resulting tree of adders is 5 layers deep rather than 32, reducing the time to compute the sum. Real multipliers achieve further performance improvements by splitting up the adders and creating a more complex tree: the venerable <a href="https://en.wikipedia.org/wiki/Wallace_tree">Wallace tree</a> (1964) and <a href="https://en.wikipedia.org/wiki/Dadda_multiplier">Dadda multiplier</a> (1965) are two popular approaches. Another optimization is the <a href="https://en.wikipedia.org/wiki/Booth%27s_multiplication_algorithm">Booth algorithm</a> (1951), which performs signed multiplication directly, without converting the arguments to positive values first. The Pentium 4 (2000) used a Booth encoder and a Wallace tree (<a href="https://past.date-conference.com/proceedings-archive/2017/pyear/PAPERS/2002/DATE02/PDFFILES/01B_1.PDF">ref</a>), but research in the early 2000s found the Dadda tree is faster and it is now more popular.</p> <h2>Conclusions</h2> <p>Multiplication is much harder to compute than addition or subtraction. The 8086 processor hid this complexity from the programmer by providing four multiplication instructions for byte and word multiplication of signed or unsigned values. These instructions implemented multiplication in microcode, performing shifts and adds in a loop. By using microcode subroutines and conditional execution, these four machine instructions share most of the microcode. As the microcode capacity of the 8086 was very small, this was a critical feature of the implementation.</p> <p>If you made it through all the discussion of microcode, congratulations! Microcode is even harder to understand than assembly code. Part of the problem is that microcode is very fine-grain, with even ALU operations split into multiple steps. Another complication is that 8086 microcode performs a register move and another operation in parallel, so it's hard to keep track of what's going on. Microcode can seem a bit like a jigsaw puzzle, with pieces carefully fit together as compactly as possible. I hope the explanations here made sense, or at least gave you a feel for how microcode operates.</p> <p>I've written multiple <a href="https://www.righto.com/search/label/8086">posts on the 8086</a> so far and plan to continue reverse-engineering the 8086 die so follow me on Twitter <a href="https://twitter.com/kenshirriff">@kenshirriff</a> or <a href="http://www.righto.com/feeds/posts/default">RSS</a> for updates. I've also started experimenting with Mastodon recently as <a href="https://oldbytes.space/@kenshirriff">@<span class="__cf_email__" data-cfemail="5338363d203b3a21213a3535133c3f37312a2736207d2023323036">[email protected]</span></a>.</p> <h2>Notes and references</h2> <div class="footnote"> <ol> <li id="fn:mul"> <p>Mainframes going back to ENIAC had multiply and divide instructions. However, early microprocessors took a step back and didn't supports these more complex operations. (My theory is that the decline in memory prices made it more cost-effective to implement multiply and divide in software than hardware.) The National Semiconductor <a href="https://en.wikipedia.org/wiki/IMP-16">IMP-16</a>, a 16-bit bit-slice microprocessor from 1973, may be the first with multiply and divide instructions. The 8-bit <a href="https://en.wikipedia.org/wiki/Motorola_6809">Motorola 6809</a> processor (1978) included 8-bit multiplication but not division. I think the 8086 was the first Intel processor to support multiplication. <a class="footnote-backref" href="#fnref:mul" title="Jump back to footnote 1 in the text">↩</a></p> </li> <li id="fn:division"> <p>The 8086 also supported division. Although the division instructions are similar to multiplication in many ways, I'm focusing on multiplication and ignoring division for this blog post. <a class="footnote-backref" href="#fnref:division" title="Jump back to footnote 2 in the text">↩</a></p> </li> <li id="fn:microcode"> <p>My microcode analysis is based on Andrew Jenner's <a href="https://www.reenigne.org/blog/8086-microcode-disassembled/">8086 microcode disassembly</a>. <a class="footnote-backref" href="#fnref:microcode" title="Jump back to footnote 3 in the text">↩</a></p> </li> <li id="fn:corx"> <p>I think <code>CORX</code> stands for <code>Core Multiply</code> and <code>CORD</code> stands for <code>Core Divide</code>. <a class="footnote-backref" href="#fnref:corx" title="Jump back to footnote 4 in the text">↩</a></p> </li> <li id="fn:overflow"> <p>The definitions of carry and overflow are different for multiplication compared to addition and subtraction. Note that the result of a multiplication operation will always fit in the available result space, which is twice as large as the arguments. For instance, the biggest value you can get by multiplying 16-bit values is <code>0xffff</code>×<code>0xffff</code>=<code>0xfffe0001</code> which fits into 32 bits. (Signed and 8-bit multiplications fit similarly.) This is in contrast to addition and subtraction, which can exceed their available space. A carry indicates that an addition exceeded its space when treated as unsigned, while an overflow indicates that an addition exceeded its space when treated as unsigned. <a class="footnote-backref" href="#fnref:overflow" title="Jump back to footnote 5 in the text">↩</a></p> </li> <li id="fn:flags"> <p>The Intel documentation states that the sign, carry, overflow, and parity flags are undefined after the <code>MUL</code> operation, even though the microcode causes them to be computed. The meaning of "undefined" is that programmers shouldn't count on the flag values because Intel might change the behavior in later chips. <a href="https://www.vogons.org/viewtopic.php?t=46211">This thread</a> discusses the effects of <code>MUL</code> on the flags, and how the behavior is different on the <a href="https://en.wikipedia.org/wiki/NEC_V20">NEC V20</a> chip. <a class="footnote-backref" href="#fnref:flags" title="Jump back to footnote 6 in the text">↩</a></p> </li> <li id="fn:complement"> <p>It may be worth explaining why the two's complement of a number is defined by adding 1 to the one's complement. The one's complement of a number simply flips all the bits. If you take a byte value <code>n</code>, <code>0xff</code> - <code>n</code> is the one's complement, since a 1 bit in <code>n</code> produces a 0 bit in the result.</p> <p>Now, suppose we want to represent -5 as a signed byte. Adding <code>0x100</code> will keep the same byte value with a carry out of the byte. But <code>0x100</code> - 5 = (1 + 0xff) - 5 = 1 + (<code>0xff</code> - 5) = 1 + (one's complement of 5). Thus, it makes sense mathematically to represent -5 by adding 1 to the one's complement of 5, and this holds for any value. <a class="footnote-backref" href="#fnref:complement" title="Jump back to footnote 7 in the text">↩</a></p> </li> <li id="fn:neg"> <p>The negation code is a bit tricky because the result is split across two words. In most cases, the upper word is bitwise complemented. However, if the lower word is zero, then the upper word is negated (two's complement). I'll demonstrate with 16-bit values to keep the examples small. The number 257 (0x0101) is negated to form -257 (0xfeff). Note that the upper byte is the one's complement (0x01 vs 0xfe) while the lower byte is two's complement (0x01 vs 0xff). On the other hand, the number 256 (0x0100) is negated to form -256 (0xff00). In this case, the upper byte is the two's complement (0x01 vs 0xff) and the lower byte is also the two's complement (0x00 vs 0x00).</p> <p>(Mathematical explanation: the two's complement is formed by taking the one's complement and adding 1. In most cases, there won't be a carry from the low byte to the upper byte, so the upper byte will remain the one's complement. However, if the low byte is 0, the complement is 0xff and adding 1 will form a carry. Adding this carry to the upper byte yields the two's complement of that byte.)</p> <p>To support multi-word negation, the 8086's <code>NEG</code> instruction clears the carry flag if the operand is 0, and otherwise sets the carry flag. (This is the opposite from the above because subtractions (including <code>NEG</code>) treat the carry flag as a borrow flag, with the opposite meaning.) The microcode <code>NEG</code> operation has identical behavior to the machine instruction, since it is used to implement the machine instruction.</p> <p>Thus to perform a two-word negation, the microcode negates the low word (tmpC) and updates the flags (<code>F</code>). If the carry is set, the one's complement is applied to the upper word (tmpA). But if the carry is cleared, the two's complement is applied to tmpA. <a class="footnote-backref" href="#fnref:neg" title="Jump back to footnote 8 in the text">↩</a></p> </li> <li id="fn:imulcof"> <p>The <code>IMULCOF</code> routine considers the upper half of the result significant if it is not the sign extension of the lower half. For instance, dropping the top byte of <code>0x0005</code> (+5) yields 0x05 (+5). Dropping the top byte of <code>0xfffb</code> (-5) yields 0xfb (-5). Thus, the upper byte is not significant in these cases. Conversely, dropping the top byte of <code>0x00fb</code> (+251) yields <code>0xfb</code> (-5), so the upper byte is significant. <a class="footnote-backref" href="#fnref:imulcof" title="Jump back to footnote 9 in the text">↩</a></p> </li> <li id="fn:x-reg"> <p>Curiously, the <a href="https://patents.google.com/patent/US4449184A/">8086 patent</a> states that the <code>X</code> register is a 4-bit register holding bits 3–6 of the byte (col. 9, line 20). But looking at the die, it is a 3-bit register holding bits 3–5 of the byte. <a class="footnote-backref" href="#fnref:x-reg" title="Jump back to footnote 10 in the text">↩</a></p> </li> <li id="fn:opcode"> <p>Some instructions are specified by bits 5–3 in the ModR/M byte rather than in the first opcode byte. The motivation is to avoid wasting bits for instructions that use a ModR/M byte but don't need a register specification. For instance, consider the instruction <code>ADD [BX],0x1234</code>. This instruction uses a ModR/M byte to specify the memory address. However, because it uses an immediate operand, it does not need the register specification normally provided by bits 5–3 of the ModR/M byte. This frees up the bits to specify the instruction. From one perspective, this is an ugly hack, while from another perspective it is a clever optimization. <a class="footnote-backref" href="#fnref:opcode" title="Jump back to footnote 11 in the text">↩</a></p> </li> <li id="fn:f1"> <p>Andrew Jenner discusses the F1 flag and the interaction between <code>REP</code> and multiplication <a href="https://www.reenigne.org/blog/8086-microcode-disassembled/">here</a>. <a class="footnote-backref" href="#fnref:f1" title="Jump back to footnote 12 in the text">↩</a></p> </li> <li id="fn:performance"> <p>Here are some detailed performance numbers. The 8086 processor takes 70–77 clock cycles to multiply 8-bit values and 118–133 clock cycles to multiply 16-bit values. Signed multiplies are a bit slower because of the sign calculations: 80–98 and 128–154 clock cycles respectively. The time is variable because of the conditional jumps in the multiplication process.</p> <p>The Intel 186 (1982) optimized multiplication slightly, bringing the register word multiply down to 35–37 cycles. The Intel 286 (also 1982) reduced this to 21 clocks. The 486 (1989) used a shift-add multiply function but it had an "early out" algorithm that stopped when the remaining bits were zero, so a 16-bit multiply could take from 9 to 22 clocks. The 8087 floating point coprocessor (1980) used radix-4 multiplication, multiplying by pairs of bits at a time and either adding or subtracting. This yields half the addition cycles. The Pentium's P5 micro-architecture (1993) took the unusual approach of reusing the floating-point unit's hardware multiplier for integer multiplication, taking 10 cycles for a 32-bit multiplication. <a class="footnote-backref" href="#fnref:performance" title="Jump back to footnote 13 in the text">↩</a></p> </li> <li id="fn:hardware"> <p><a href="https://inst.eecs.berkeley.edu/~eecs151/sp18/files/Lecture21.pdf">This presentation</a> gives a good overview of implementations of multiplication in hardware. <a class="footnote-backref" href="#fnref:hardware" title="Jump back to footnote 14 in the text">↩</a></p> </li> </ol> </div> <div style='clear: both;'></div> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'><span class='post-comment-link'> <a class='comment-link' href='https://www.blogger.com/comment/fullpage/post/6264947694886887540/8390323699966089555' onclick=''> 13 comments: </a> </span> <span class='post-icons'> <span class='item-action'> <a href='https://www.blogger.com/email-post.g?blogID=6264947694886887540&postID=8390323699966089555' title='Email Post'> <img alt='' class='icon-action' height='13' src='http://img1.blogblog.com/img/icon18_email.gif' width='18'/> </a> </span> <span class='item-control blog-admin pid-1138732533'> <a href='https://www.blogger.com/post-edit.g?blogID=6264947694886887540&postID=8390323699966089555&from=pencil' title='Edit Post'> <img alt='' class='icon-action' height='18' src='https://resources.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </span> <span class='post-backlinks post-comment-link'> </span> <div class='post-share-buttons goog-inline-block'> <a class='goog-inline-block share-button sb-email' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=8390323699966089555&target=email' target='_blank' title='Email This'><span class='share-button-link-text'>Email This</span></a><a class='goog-inline-block share-button sb-blog' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=8390323699966089555&target=blog' onclick='window.open(this.href, "_blank", "height=270,width=475"); return false;' target='_blank' title='BlogThis!'><span class='share-button-link-text'>BlogThis!</span></a><a class='goog-inline-block share-button sb-twitter' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=8390323699966089555&target=twitter' target='_blank' title='Share to X'><span class='share-button-link-text'>Share to X</span></a><a class='goog-inline-block share-button sb-facebook' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=8390323699966089555&target=facebook' onclick='window.open(this.href, "_blank", "height=430,width=640"); return false;' target='_blank' title='Share to Facebook'><span class='share-button-link-text'>Share to Facebook</span></a><a class='goog-inline-block share-button sb-pinterest' href='https://www.blogger.com/share-post.g?blogID=6264947694886887540&postID=8390323699966089555&target=pinterest' target='_blank' title='Share to Pinterest'><span class='share-button-link-text'>Share to Pinterest</span></a> </div> </div> <div class='post-footer-line post-footer-line-2'><span class='post-labels'> Labels: <a href='http://www.righto.com/search/label/8086' rel='tag'>8086</a>, <a href='http://www.righto.com/search/label/chips' rel='tag'>chips</a>, <a href='http://www.righto.com/search/label/microcode' rel='tag'>microcode</a>, <a href='http://www.righto.com/search/label/reverse-engineering' rel='tag'>reverse-engineering</a> </span> </div> <div class='post-footer-line post-footer-line-3'></div> </div> </div> </div> </div></div> </div> <div class='blog-pager' id='blog-pager'> <span id='blog-pager-older-link'> <a class='blog-pager-older-link' href='http://www.righto.com/search/label/microcode?updated-max=2023-03-15T08:53:00-07:00&max-results=20&start=3&by-date=false' id='Blog1_blog-pager-older-link' title='Older Posts'>Older Posts</a> </span> <a class='home-link' href='http://www.righto.com/'>Home</a> </div> <div class='clear'></div> </div></div> </div> </div> <div class='column-left-outer'> <div class='column-left-inner'> <aside> </aside> </div> </div> <div class='column-right-outer'> <div class='column-right-inner'> <aside> <div class='sidebar section' id='sidebar-right-1'><div class='widget HTML' data-version='1' id='HTML2'> <div class='widget-content'> <style> @import url('https://fonts.googleapis.com/css?family=Montserrat:300,400,500,700'); .form-preview { display: flex; flex-direction: column; justify-content: center; margin-top: 30px; padding: clamp(17px, 5%, 40px) clamp(17px, 7%, 50px); max-width: 350px; min-height: 200px; border-radius: 6px; box-shadow: 0 5px 25px rgba(34, 60, 47, 0.25); } .form-preview, .form-preview *{ box-sizing: border-box; } .form-preview .preview-heading { width: 100%; } .form-preview .preview-heading h5{ margin-top: 0; margin-bottom: 0; } .form-preview .preview-input-field { margin-top: 20px; width: 100%; } .form-preview .preview-input-field input { width: 100%; height: 40px; border-radius: 6px; border: 2px solid #e9e8e8; background-color: #fff; outline: none; } .form-preview .preview-input-field input::placeholder, .form-preview .preview-input-field input { opacity: 0.5; color: #000; font-family: "Montserrat"; font-size: 14px; font-weight: 500; line-height: 20px; text-align: center; } .form-preview .preview-submit-button { margin-top: 10px; width: 100%; } .form-preview .preview-submit-button button { width: 100%; height: 40px; border: 0; border-radius: 6px; line-height: 0px; } .form-preview .preview-submit-button button:hover { cursor: pointer; } </style><form data-v-4c58e686="" action="https://api.follow.it/subscription-form/U3NBTmZKVkI1YVpCa000a0RCZHFiQ3FYMko1cWRTZTN6K3hJdWM2QWxJbE1uVXdXUHZZVzJVQzVLZGh5Y0RCVXB2d2JSTzBobGhuY0FsZnlHbVdFZ2VTN2Q4Vy84RnIxUTgzVlcrbXNIR0Y0aW93d3REM2J6VS9RL0gxWURnV1d8ZWN0YStwUWdWWUFiOTIyWDVGWjdYYVdGZEVNcC9qODZacjlwWXRIcEJQRT0=/8" method="post"><div data-v-4c58e686="" class="form-preview" style="background-color: rgb(255, 255, 255); border-style: solid; border-width: 1px; border-color: rgb(204, 204, 204); position: relative;"><div data-v-4c58e686="" class="preview-heading"><h5 data-v-4c58e686="" style="font-family: Montserrat; font-weight: bold; color: rgb(0, 0, 0); font-size: 12px; text-align: center;">Get new posts by email:</h5></div> <div data-v-4c58e686="" class="preview-input-field"><input data-v-4c58e686="" type="email" name="email" placeholder="Enter your email" spellcheck="false" /></div> <div data-v-4c58e686="" class="preview-submit-button"><button data-v-4c58e686="" type="submit" style="font-family: Montserrat; font-weight: bold; color: rgb(255, 255, 255); font-size: 12px; text-align: center; background-color: rgb(0, 0, 0);">Subscribe</button></div></div></form> </div> <div class='clear'></div> </div><div class='widget HTML' data-version='1' id='HTML3'> <h2 class='title'>About the site</h2> <div class='widget-content'> <a href="https://www.righto.com/p/index.html">Contact info and site index</a> </div> <div class='clear'></div> </div><div class='widget PopularPosts' data-version='1' id='PopularPosts1'> <h2>Popular Posts</h2> <div class='widget-content popular-posts'> <ul> <li> <div class='item-thumbnail-only'> <div class='item-thumbnail'> <a href='http://www.righto.com/2024/11/antenna-diodes-in-pentium-processor.html' target='_blank'> <img alt='' border='0' src='https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_uJJqMtHx4dbQe0mP0giXztbMjLtoppkWVOocQ5bOkNFd1mYglX4m6c0EhbdMOlEEixTu-0wY2DNuzSzZeaUTEmb_Vbo-38XIkd1zvw5LpDjkGzq46rdsCC4ES3jBshRaRNA4g=w72-h72-p-k-no-nu'/> </a> </div> <div class='item-title'><a href='http://www.righto.com/2024/11/antenna-diodes-in-pentium-processor.html'>Antenna diodes in the Pentium processor</a></div> </div> <div style='clear: both;'></div> </li> <li> <div class='item-thumbnail-only'> <div class='item-thumbnail'> <a href='http://www.righto.com/2012/05/apple-iphone-charger-teardown-quality.html' target='_blank'> <img alt='' border='0' src='https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_u92q6ixjxfSFDwKHXaVI6wKXtY6SKmHTp3BpBWSN4g6gCTMlEEEWaMLS3lnPTf6Ow9RR4KKwNvZqUet0kEEoHrs1wt5TxHdFM-fJc12aJX2SV45pqlDkfC4oYJv4mfj1xIjmed8JCwyQ5dKp7PqPtu_iM=w72-h72-p-k-no-nu'/> </a> </div> <div class='item-title'><a href='http://www.righto.com/2012/05/apple-iphone-charger-teardown-quality.html'>Apple iPhone charger teardown: quality in a tiny expensive package</a></div> </div> <div style='clear: both;'></div> </li> <li> <div class='item-thumbnail-only'> <div class='item-thumbnail'> <a href='http://www.righto.com/2024/05/blog-post.html' target='_blank'> <img alt='' border='0' src='https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_vYxt-qy0xO431B0KkvRCmVhnWyFdy9pEaXJMioISELH1i2yH6Ykfg9T4blslwrM8gdsmPP7f-t5nmONKes5GHjgMHh109-8YG8e24k3QkrhNIN-i3-xZBk-8ESzWawty_xajXsgJGKrEy3PzCHQ-65=w72-h72-p-k-no-nu'/> </a> </div> <div class='item-title'><a href='http://www.righto.com/2024/05/blog-post.html'>Inside a vintage aerospace navigation computer of uncertain purpose</a></div> </div> <div style='clear: both;'></div> </li> <li> <div class='item-thumbnail-only'> <div class='item-thumbnail'> <a href='http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html' target='_blank'> <img alt='' border='0' src='https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_tRkHOit3Jm0mYQH5INl0OpQol0ddYAC3ceeDxBm5uCjUg9HW2nyissMFcRpFO6_Czu9w0caGBWEnJUsh571vkhnfqA9qUp-V3bNT4uB1UtcZE=w72-h72-p-k-no-nu'/> </a> </div> <div class='item-title'><a href='http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html'>A Multi-Protocol Infrared Remote Library for the Arduino</a></div> </div> <div style='clear: both;'></div> </li> <li> <div class='item-thumbnail-only'> <div class='item-thumbnail'> <a href='http://www.righto.com/2014/09/mining-bitcoin-with-pencil-and-paper.html' target='_blank'> <img alt='' border='0' src='https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_vBkI-52d5m0lEA9t0fm_DQnGVLytDRxtipRncqqSW3kmpcdPadqjwIrbizxjjhKtmDvji7YwZasWOtTZr7bw-EU0AIO_H36sfwbEzXQ20Le18m273SwpwzHhjjKqdpGiBv=w72-h72-p-k-no-nu'/> </a> </div> <div class='item-title'><a href='http://www.righto.com/2014/09/mining-bitcoin-with-pencil-and-paper.html'>Mining Bitcoin with pencil and paper: 0.67 hashes per day</a></div> </div> <div style='clear: both;'></div> </li> <li> <div class='item-thumbnail-only'> <div class='item-thumbnail'> <a href='http://www.righto.com/2024/08/pentium-navajo-fairchild-shiprock.html' target='_blank'> <img alt='' border='0' src='https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_vAIzOfFed7fTtxQJZCqc6d6YCNmq_-GFVgiOu7XmdFAQu-p3xCi4hgkzYWYzey3bgb9AisLYR_GARlG7Mf_lvzjIjw1XcWFcuta03aMp-Ebb41IF6r0mBat1wuyYaAfmB3v87y=w72-h72-p-k-no-nu'/> </a> </div> <div class='item-title'><a href='http://www.righto.com/2024/08/pentium-navajo-fairchild-shiprock.html'>The Pentium as a Navajo weaving</a></div> </div> <div style='clear: both;'></div> </li> <li> <div class='item-thumbnail-only'> <div class='item-thumbnail'> <a href='http://www.righto.com/2013/06/teardown-and-exploration-of-magsafe.html' target='_blank'> <img alt='' border='0' src='https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_vfchyeweOGsSIivwpbX5-2BEmE8deiE9fcrOk9JQI0dsz7_jNXlN5DpFGm97151WJT1UyALDopjVmpjTWKsvRz4hIz5p8U_fAHjVCU1e5OYa10TupMHtBNK91U6113LwlCGdY2kn2Mx9pDE_Y=w72-h72-p-k-no-nu'/> </a> </div> <div class='item-title'><a href='http://www.righto.com/2013/06/teardown-and-exploration-of-magsafe.html'>Teardown and exploration of Apple's Magsafe connector</a></div> </div> <div style='clear: both;'></div> </li> <li> <div class='item-thumbnail-only'> <div class='item-thumbnail'> <a href='http://www.righto.com/2023/01/inside-globus-ink-mechanical-navigation.html' target='_blank'> <img alt='' border='0' src='https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_t_-kuouK5mO0Upt1jceu-vbi_bElm1orbnb5uyJD8xa1EOpZu8NYCb12TR3vj0y--vyhPZhVfs9WTWVoUmCz1JAVtZ6gNGmJDhSPUQS_z2UpFzysYRKxLTdyWz4Mo53ptG_eg=w72-h72-p-k-no-nu'/> </a> </div> <div class='item-title'><a href='http://www.righto.com/2023/01/inside-globus-ink-mechanical-navigation.html'>Inside the Globus INK: a mechanical navigation computer for Soviet spaceflight</a></div> </div> <div style='clear: both;'></div> </li> </ul> <div class='clear'></div> </div> </div><div class='widget BlogSearch' data-version='1' id='BlogSearch1'> <h2 class='title'>Search This Blog</h2> <div class='widget-content'> <div id='BlogSearch1_form'> <form action='http://www.righto.com/search' class='gsc-search-box' target='_top'> <table cellpadding='0' cellspacing='0' class='gsc-search-box'> <tbody> <tr> <td class='gsc-input'> <input autocomplete='off' class='gsc-input' name='q' size='10' title='search' type='text' value=''/> </td> <td class='gsc-search-button'> <input class='gsc-search-button' title='search' type='submit' value='Search'/> </td> </tr> </tbody> </table> </form> </div> </div> <div class='clear'></div> </div><div class='widget Label' data-version='1' id='Label1'> <h2>Labels</h2> <div class='widget-content cloud-label-widget-content'> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/386'>386</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/6502'>6502</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/8008'>8008</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/8085'>8085</a> </span> <span class='label-size label-size-4'> <a dir='ltr' href='http://www.righto.com/search/label/8086'>8086</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/8087'>8087</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/8088'>8088</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/aerospace'>aerospace</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/alto'>alto</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/analog'>analog</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/Apollo'>Apollo</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/apple'>apple</a> </span> <span class='label-size label-size-4'> <a dir='ltr' href='http://www.righto.com/search/label/arc'>arc</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/arduino'>arduino</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/arm'>arm</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/beaglebone'>beaglebone</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/bitcoin'>bitcoin</a> </span> <span class='label-size label-size-1'> <a dir='ltr' href='http://www.righto.com/search/label/c%23'>c#</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/cadc'>cadc</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/calculator'>calculator</a> </span> <span class='label-size label-size-4'> <a dir='ltr' href='http://www.righto.com/search/label/chips'>chips</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/css'>css</a> </span> <span class='label-size label-size-1'> <a dir='ltr' href='http://www.righto.com/search/label/datapoint'>datapoint</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/dx7'>dx7</a> </span> <span class='label-size label-size-5'> <a dir='ltr' href='http://www.righto.com/search/label/electronics'>electronics</a> </span> <span class='label-size label-size-1'> <a dir='ltr' href='http://www.righto.com/search/label/f%23'>f#</a> </span> <span class='label-size label-size-1'> <a dir='ltr' href='http://www.righto.com/search/label/fairchild'>fairchild</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/fpga'>fpga</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/fractals'>fractals</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/genome'>genome</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/globus'>globus</a> </span> <span class='label-size label-size-1'> <a dir='ltr' href='http://www.righto.com/search/label/haskell'>haskell</a> </span> <span class='label-size label-size-1'> <a dir='ltr' href='http://www.righto.com/search/label/HP'>HP</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/html5'>html5</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/ibm'>ibm</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/ibm1401'>ibm1401</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/ibm360'>ibm360</a> </span> <span class='label-size label-size-4'> <a dir='ltr' href='http://www.righto.com/search/label/intel'>intel</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/ipv6'>ipv6</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/ir'>ir</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/java'>java</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/javascript'>javascript</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/math'>math</a> </span> <span class='label-size label-size-3'> <span dir='ltr'>microcode</span> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/oscilloscope'>oscilloscope</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/Pentium'>Pentium</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/photo'>photo</a> </span> <span class='label-size label-size-4'> <a dir='ltr' href='http://www.righto.com/search/label/power%20supply'>power supply</a> </span> <span class='label-size label-size-4'> <a dir='ltr' href='http://www.righto.com/search/label/random'>random</a> </span> <span class='label-size label-size-5'> <a dir='ltr' href='http://www.righto.com/search/label/reverse-engineering'>reverse-engineering</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/sheevaplug'>sheevaplug</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/snark'>snark</a> </span> <span class='label-size label-size-3'> <a dir='ltr' href='http://www.righto.com/search/label/space'>space</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/spanish'>spanish</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/synth'>synth</a> </span> <span class='label-size label-size-4'> <a dir='ltr' href='http://www.righto.com/search/label/teardown'>teardown</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/theory'>theory</a> </span> <span class='label-size label-size-1'> <a dir='ltr' href='http://www.righto.com/search/label/unicode'>unicode</a> </span> <span class='label-size label-size-2'> <a dir='ltr' href='http://www.righto.com/search/label/Z-80'>Z-80</a> </span> <div class='clear'></div> </div> </div><div class='widget BlogArchive' data-version='1' id='BlogArchive1'> <h2>Blog Archive</h2> <div class='widget-content'> <div id='ArchiveList'> <div id='BlogArchive1_ArchiveList'> <ul class='hierarchy'> <li class='archivedate expanded'> <a class='toggle' href='javascript:void(0)'> <span class='zippy toggle-open'> ▼  </span> </a> <a class='post-count-link' href='http://www.righto.com/2024/'> 2024 </a> <span class='post-count' dir='ltr'>(20)</span> <ul class='hierarchy'> <li class='archivedate expanded'> <a class='toggle' href='javascript:void(0)'> <span class='zippy toggle-open'> ▼  </span> </a> <a class='post-count-link' href='http://www.righto.com/2024/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> <ul class='posts'> <li><a href='http://www.righto.com/2024/11/antenna-diodes-in-pentium-processor.html'>Antenna diodes in the Pentium processor</a></li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2024/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2024/09/'> September </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2024/08/'> August </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2024/07/'> July </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2024/06/'> June </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2024/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2024/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2024/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2024/02/'> February </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2024/01/'> January </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2023/'> 2023 </a> <span class='post-count' dir='ltr'>(35)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2023/12/'> December </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2023/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2023/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2023/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2023/08/'> August </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2023/07/'> July </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2023/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2023/04/'> April </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2023/03/'> March </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2023/02/'> February </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2023/01/'> January </a> <span class='post-count' dir='ltr'>(8)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2022/'> 2022 </a> <span class='post-count' dir='ltr'>(18)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2022/11/'> November </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2022/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2022/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2022/06/'> June </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2022/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2022/04/'> April </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2022/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2022/02/'> February </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2022/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2021/'> 2021 </a> <span class='post-count' dir='ltr'>(26)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2021/12/'> December </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2021/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2021/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2021/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2021/07/'> July </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2021/06/'> June </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2021/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2021/04/'> April </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2021/03/'> March </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2021/02/'> February </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2021/01/'> January </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2020/'> 2020 </a> <span class='post-count' dir='ltr'>(33)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2020/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2020/11/'> November </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2020/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2020/09/'> September </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2020/08/'> August </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2020/07/'> July </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2020/06/'> June </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2020/05/'> May </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2020/04/'> April </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2020/03/'> March </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2020/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2019/'> 2019 </a> <span class='post-count' dir='ltr'>(18)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2019/11/'> November </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2019/10/'> October </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2019/09/'> September </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2019/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2019/07/'> July </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2019/04/'> April </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2019/02/'> February </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2019/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2018/'> 2018 </a> <span class='post-count' dir='ltr'>(17)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2018/12/'> December </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2018/09/'> September </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2018/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2018/06/'> June </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2018/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2018/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2018/03/'> March </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2018/02/'> February </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2018/01/'> January </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2017/'> 2017 </a> <span class='post-count' dir='ltr'>(21)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2017/12/'> December </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2017/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2017/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2017/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2017/07/'> July </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2017/06/'> June </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2017/04/'> April </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2017/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2017/02/'> February </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2017/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2016/'> 2016 </a> <span class='post-count' dir='ltr'>(34)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2016/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2016/10/'> October </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2016/09/'> September </a> <span class='post-count' dir='ltr'>(8)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2016/08/'> August </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2016/07/'> July </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2016/06/'> June </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2016/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2016/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2016/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2016/02/'> February </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2016/01/'> January </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2015/'> 2015 </a> <span class='post-count' dir='ltr'>(12)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2015/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2015/11/'> November </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2015/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2015/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2015/05/'> May </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2015/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2015/02/'> February </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2014/'> 2014 </a> <span class='post-count' dir='ltr'>(13)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2014/12/'> December </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2014/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2014/09/'> September </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2014/05/'> May </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2014/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2014/02/'> February </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2013/'> 2013 </a> <span class='post-count' dir='ltr'>(24)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2013/11/'> November </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2013/09/'> September </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2013/08/'> August </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2013/07/'> July </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2013/06/'> June </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2013/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2013/03/'> March </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2013/02/'> February </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2013/01/'> January </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2012/'> 2012 </a> <span class='post-count' dir='ltr'>(10)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2012/12/'> December </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2012/11/'> November </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2012/10/'> October </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2012/05/'> May </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2012/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2012/02/'> February </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2011/'> 2011 </a> <span class='post-count' dir='ltr'>(11)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2011/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2011/07/'> July </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2011/05/'> May </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2011/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2011/03/'> March </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2011/02/'> February </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2010/'> 2010 </a> <span class='post-count' dir='ltr'>(22)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2010/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2010/11/'> November </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2010/10/'> October </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2010/08/'> August </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2010/06/'> June </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2010/05/'> May </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2010/04/'> April </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2010/03/'> March </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2010/01/'> January </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2009/'> 2009 </a> <span class='post-count' dir='ltr'>(22)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2009/12/'> December </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2009/11/'> November </a> <span class='post-count' dir='ltr'>(5)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2009/09/'> September </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2009/08/'> August </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2009/07/'> July </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2009/06/'> June </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2009/04/'> April </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2009/03/'> March </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2009/02/'> February </a> <span class='post-count' dir='ltr'>(2)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2009/01/'> January </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2008/'> 2008 </a> <span class='post-count' dir='ltr'>(27)</span> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2008/07/'> July </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2008/06/'> June </a> <span class='post-count' dir='ltr'>(1)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2008/05/'> May </a> <span class='post-count' dir='ltr'>(3)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2008/04/'> April </a> <span class='post-count' dir='ltr'>(4)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2008/03/'> March </a> <span class='post-count' dir='ltr'>(10)</span> </li> </ul> <ul class='hierarchy'> <li class='archivedate collapsed'> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> ►  </span> </a> <a class='post-count-link' href='http://www.righto.com/2008/02/'> February </a> <span class='post-count' dir='ltr'>(6)</span> </li> </ul> </li> </ul> </div> </div> <div class='clear'></div> </div> </div></div> </aside> </div> </div> </div> <div style='clear: both'></div> <!-- columns --> </div> <!-- main --> </div> </div> <div class='main-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> <footer> <div class='footer-outer'> <div class='footer-cap-top cap-top'> <div class='cap-left'></div> <div class='cap-right'></div> </div> <div class='fauxborder-left footer-fauxborder-left'> <div class='fauxborder-right footer-fauxborder-right'></div> <div class='region-inner footer-inner'> <div class='foot no-items section' id='footer-1'></div> <table border='0' cellpadding='0' cellspacing='0' class='section-columns columns-2'> <tbody> <tr> <td class='first columns-cell'> <div class='foot no-items section' id='footer-2-1'></div> </td> <td class='columns-cell'> <div class='foot no-items section' id='footer-2-2'></div> </td> </tr> </tbody> </table> <!-- outside of the include in order to lock Attribution widget --> <div class='foot section' id='footer-3'><div class='widget Attribution' data-version='1' id='Attribution1'> <div class='widget-content' style='text-align: center;'> Powered by <a href='https://www.blogger.com' target='_blank'>Blogger</a>. </div> <div class='clear'></div> </div></div> </div> </div> <div class='footer-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </footer> <!-- content --> </div> </div> <div class='content-cap-bottom cap-bottom'> <div class='cap-left'></div> <div class='cap-right'></div> </div> </div> </div> <script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script><script type='text/javascript'> window.setTimeout(function() { document.body.className = document.body.className.replace('loading', ''); }, 10); </script> <script type="text/javascript" src="https://www.blogger.com/static/v1/widgets/984859869-widgets.js"></script> <script type='text/javascript'> window['__wavt'] = 'AOuZoY4AbbB_KFr4wUmZNUAxv1LwPUqvPQ:1732768293611';_WidgetManager._Init('//www.blogger.com/rearrange?blogID\x3d6264947694886887540','//www.righto.com/search/label/microcode','6264947694886887540'); _WidgetManager._SetDataContext([{'name': 'blog', 'data': {'blogId': '6264947694886887540', 'title': 'Ken Shirriff\x27s blog', 'url': 'http://www.righto.com/search/label/microcode', 'canonicalUrl': 'http://www.righto.com/search/label/microcode', 'homepageUrl': 'http://www.righto.com/', 'searchUrl': 'http://www.righto.com/search', 'canonicalHomepageUrl': 'http://www.righto.com/', 'blogspotFaviconUrl': 'http://www.righto.com/favicon.ico', 'bloggerUrl': 'https://www.blogger.com', 'hasCustomDomain': true, 'httpsEnabled': false, 'enabledCommentProfileImages': true, 'gPlusViewType': 'FILTERED_POSTMOD', 'adultContent': false, 'analyticsAccountNumber': 'UA-3782444-1', 'encoding': 'UTF-8', 'locale': 'en', 'localeUnderscoreDelimited': 'en', 'languageDirection': 'ltr', 'isPrivate': false, 'isMobile': false, 'isMobileRequest': false, 'mobileClass': '', 'isPrivateBlog': false, 'isDynamicViewsAvailable': true, 'feedLinks': '\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Ken Shirriff\x26#39;s blog - Atom\x22 href\x3d\x22http://www.righto.com/feeds/posts/default\x22 /\x3e\n\x3clink rel\x3d\x22alternate\x22 type\x3d\x22application/rss+xml\x22 title\x3d\x22Ken Shirriff\x26#39;s blog - RSS\x22 href\x3d\x22http://www.righto.com/feeds/posts/default?alt\x3drss\x22 /\x3e\n\x3clink rel\x3d\x22service.post\x22 type\x3d\x22application/atom+xml\x22 title\x3d\x22Ken Shirriff\x26#39;s blog - Atom\x22 href\x3d\x22https://www.blogger.com/feeds/6264947694886887540/posts/default\x22 /\x3e\n', 'meTag': '\x3clink rel\x3d\x22me\x22 href\x3d\x22https://www.blogger.com/profile/08097301407311055124\x22 /\x3e\n', 'adsenseHostId': 'ca-host-pub-1556223355139109', 'adsenseHasAds': false, 'adsenseAutoAds': false, 'boqCommentIframeForm': true, 'loginRedirectParam': '', 'view': '', 'dynamicViewsCommentsSrc': '//www.blogblog.com/dynamicviews/4224c15c4e7c9321/js/comments.js', 'dynamicViewsScriptSrc': '//www.blogblog.com/dynamicviews/2fafd358a4bcb2b4', 'plusOneApiSrc': 'https://apis.google.com/js/platform.js', 'disableGComments': true, 'interstitialAccepted': false, 'sharing': {'platforms': [{'name': 'Get link', 'key': 'link', 'shareMessage': 'Get link', 'target': ''}, {'name': 'Facebook', 'key': 'facebook', 'shareMessage': 'Share to Facebook', 'target': 'facebook'}, {'name': 'BlogThis!', 'key': 'blogThis', 'shareMessage': 'BlogThis!', 'target': 'blog'}, {'name': 'X', 'key': 'twitter', 'shareMessage': 'Share to X', 'target': 'twitter'}, {'name': 'Pinterest', 'key': 'pinterest', 'shareMessage': 'Share to Pinterest', 'target': 'pinterest'}, {'name': 'Email', 'key': 'email', 'shareMessage': 'Email', 'target': 'email'}], 'disableGooglePlus': true, 'googlePlusShareButtonWidth': 0, 'googlePlusBootstrap': '\x3cscript type\x3d\x22text/javascript\x22\x3ewindow.___gcfg \x3d {\x27lang\x27: \x27en\x27};\x3c/script\x3e'}, 'hasCustomJumpLinkMessage': false, 'jumpLinkMessage': 'Read more', 'pageType': 'index', 'searchLabel': 'microcode', 'pageName': 'microcode', 'pageTitle': 'Ken Shirriff\x27s blog: microcode'}}, {'name': 'features', 'data': {}}, {'name': 'messages', 'data': {'edit': 'Edit', 'linkCopiedToClipboard': 'Link copied to clipboard!', 'ok': 'Ok', 'postLink': 'Post Link'}}, {'name': 'template', 'data': {'name': 'custom', 'localizedName': 'Custom', 'isResponsive': false, 'isAlternateRendering': false, 'isCustom': true}}, {'name': 'view', 'data': {'classic': {'name': 'classic', 'url': '?view\x3dclassic'}, 'flipcard': {'name': 'flipcard', 'url': '?view\x3dflipcard'}, 'magazine': {'name': 'magazine', 'url': '?view\x3dmagazine'}, 'mosaic': {'name': 'mosaic', 'url': '?view\x3dmosaic'}, 'sidebar': {'name': 'sidebar', 'url': '?view\x3dsidebar'}, 'snapshot': {'name': 'snapshot', 'url': '?view\x3dsnapshot'}, 'timeslide': {'name': 'timeslide', 'url': '?view\x3dtimeslide'}, 'isMobile': false, 'title': 'Ken Shirriff\x27s blog', 'description': 'Computer history, restoring vintage computers, IC reverse engineering, and whatever', 'url': 'http://www.righto.com/search/label/microcode', 'type': 'feed', 'isSingleItem': false, 'isMultipleItems': true, 'isError': false, 'isPage': false, 'isPost': false, 'isHomepage': false, 'isArchive': false, 'isSearch': true, 'isLabelSearch': true, 'search': {'label': 'microcode', 'resultsMessage': 'Showing posts with the label microcode', 'resultsMessageHtml': 'Showing posts with the label \x3cspan class\x3d\x27search-label\x27\x3emicrocode\x3c/span\x3e'}}}]); _WidgetManager._RegisterWidget('_HeaderView', new _WidgetInfo('Header1', 'header', document.getElementById('Header1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogView', new _WidgetInfo('Blog1', 'main', document.getElementById('Blog1'), {'cmtInteractionsEnabled': false, 'navMessage': 'Showing posts with label \x3cb\x3emicrocode\x3c/b\x3e. \x3ca href\x3d\x22http://www.righto.com/\x22\x3eShow all posts\x3c/a\x3e'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_HTMLView', new _WidgetInfo('HTML2', 'sidebar-right-1', document.getElementById('HTML2'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_HTMLView', new _WidgetInfo('HTML3', 'sidebar-right-1', document.getElementById('HTML3'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_PopularPostsView', new _WidgetInfo('PopularPosts1', 'sidebar-right-1', document.getElementById('PopularPosts1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogSearchView', new _WidgetInfo('BlogSearch1', 'sidebar-right-1', document.getElementById('BlogSearch1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_LabelView', new _WidgetInfo('Label1', 'sidebar-right-1', document.getElementById('Label1'), {}, 'displayModeFull')); _WidgetManager._RegisterWidget('_BlogArchiveView', new _WidgetInfo('BlogArchive1', 'sidebar-right-1', document.getElementById('BlogArchive1'), {'languageDirection': 'ltr', 'loadingMessage': 'Loading\x26hellip;'}, 'displayModeFull')); _WidgetManager._RegisterWidget('_AttributionView', new _WidgetInfo('Attribution1', 'footer-3', document.getElementById('Attribution1'), {}, 'displayModeFull')); </script> </body> </html>