{"id":1707,"date":"2012-09-29T20:00:32","date_gmt":"2012-09-29T19:00:32","guid":{"rendered":"https:\/\/www.reenigne.org\/blog\/?p=1707"},"modified":"2012-09-14T21:43:00","modified_gmt":"2012-09-14T20:43:00","slug":"the-cga-wait-states","status":"publish","type":"post","link":"https:\/\/www.reenigne.org\/blog\/the-cga-wait-states\/","title":{"rendered":"The CGA wait states"},"content":{"rendered":"<p>As part of my project to emulate an <a href=\"https:\/\/www.reenigne.org\/blog\/i-bought-an-xt\">IBM PC or XT<\/a> with cycle accuracy, I also wanted to emulate the CGA card with cycle accuracy. That meant figuring out exactly what the wait states are when accessing CGA memory. Here's what I found out.<\/p>\n<p>When talking about this stuff it helps to have a common terminology to talk about the several units of timing involved. This is the terminology I use:<\/p>\n<ul>\n<li>1 hdot = ~70ns = 1\/14.318MHz = 1 pixel time in 640-pixel mode<\/li>\n<li>1 ldot = 2 hdots = ~140ns = 1\/7.159MHz = 1 pixel time in 320-pixel mode<\/li>\n<li>1 ccycle = 3 hdots = ~210ns = 1\/4.77MHz = 1 CPU cycle<\/li>\n<li>1 cycle = 4 hdots = ~279ns = 1\/3.58MHz = 1 NTSC color burst cycle<\/li>\n<li>1 hchar = 8 hdots = ~559ns = 1\/1.79MHz = 1 character time in 80-column text mode<\/li>\n<li>1 lchar = 16 hdots = ~1.12us = 1\/895KHz = 1 character time in 40-column text mode<\/li>\n<\/ul>\n<p>The wait state algorithm for the original IBM CGA is basically \"wait 1 hchar, then wait for the next lchar, then wait for the next ccycle\". That works out at between 3 and 8 ccycles depending on the relative phase of the CPU and CGA clocks. There are actually 16 possible relative phases (one for each of the hdots within the lchar at which the CPU cycle starts).<\/p>\n<p>One relative phase has a 3 ccycle wait state and there are 3 relative phases for each of the other 5 possible wait state lengths (4, 5, 6, 7 and 8 ccycles respectively). 1+3+3+3+3+3=16. So the average wait state is (3+4*3+5*3+6*3+7*3+8*3)\/16 = 5.8125 ccycles, but you might measure a different average depending on how your piece of code ends up synchronizing with the CGA clock.<\/p>\n<p>In a way it's rather unfortunate because with a slight hardware modification I think the 1 hchar wait state could have been eliminated, making the average wait state about 3 ccycles shorter and roughly doubling the average speed of the CGA memory access.<\/p>\n<p>Also unfortunately, \"rep stosw\" gives almost the worst possible wait state behavior. I haven't tried it yet, but I suspect that it would be possible to write CGA code that self-synchronizes to get the best possible wait states (though of course that would probably only improve performance on machines that were cycle exact with the machine that it was tuned for).<\/p>\n<p>A third unfortunate thing is that the wait states are the same whereever the raster is on the screen - they aren't disabled during the retrace interval or anything like that. There's a good reason for that though - the CRTC continues to strobe through the CGA RAM throughout the overscan\/retrace areas for dynamic RAM refresh - allowing the CPU access to the full memory bandwidth could result in loss of video RAM data, since the CGA doesn't participate in the system DRAM refresh cycles (which is a good thing, because otherwise all those wait states would propagate to the entire memory system).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>As part of my project to emulate an IBM PC or XT with cycle accuracy, I also wanted to emulate the CGA card with cycle accuracy. That meant figuring out exactly what the wait states are when accessing CGA memory. Here's what I found out. When talking about this stuff it helps to have a [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,38,23],"tags":[],"class_list":["post-1707","post","type-post","status-publish","format-standard","hentry","category-computer","category-emulation","category-video"],"_links":{"self":[{"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/posts\/1707","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/comments?post=1707"}],"version-history":[{"count":4,"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/posts\/1707\/revisions"}],"predecessor-version":[{"id":1799,"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/posts\/1707\/revisions\/1799"}],"wp:attachment":[{"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/media?parent=1707"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/categories?post=1707"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.reenigne.org\/blog\/wp-json\/wp\/v2\/tags?post=1707"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}