{"id":1763,"date":"2017-12-03T10:52:57","date_gmt":"2017-12-03T10:52:57","guid":{"rendered":"http:\/\/www.sydneysmith.com\/wordpress\/?p=1763"},"modified":"2017-12-04T19:02:58","modified_gmt":"2017-12-04T19:02:58","slug":"z80sim-hard-breakpoints","status":"publish","type":"post","link":"https:\/\/www.sydneysmith.com\/wordpress\/1763\/z80sim-hard-breakpoints\/","title":{"rendered":"z80sim Hard Breakpoints"},"content":{"rendered":"<p><img loading=\"lazy\" src=\"http:\/\/www.sydneysmith.com\/wordpress\/wp-content\/uploads\/2017\/12\/z80sim-hard-breakpoints.png\" alt=\"\" width=\"677\" height=\"343\" class=\"alignnone size-full wp-image-1764\" srcset=\"https:\/\/www.sydneysmith.com\/wordpress\/wp-content\/uploads\/2017\/12\/z80sim-hard-breakpoints.png 677w, https:\/\/www.sydneysmith.com\/wordpress\/wp-content\/uploads\/2017\/12\/z80sim-hard-breakpoints-300x152.png 300w\" sizes=\"(max-width: 677px) 100vw, 677px\" \/><\/p>\n<p>The z80sim by Udo Munk comes with the ability to set soft breakpoints. A soft breakpoint is where an instruction is temporarily replaced with a HALT instruction and the program runs until it gets to that point. Then the emulator announces &#8220;we got to &#8230;&#8221;, puts the original instruction back, and lets the user choose what to do next. However, <!--more-->sometimes that doesn&#8217;t work.<\/p>\n<p>Take the situation where you are reading a program from a disk\/image. You&#8217;re about to load, and execute the program. You&#8217;d like to load it first, have a look at what it has in mind, and then step through it to see where things go wrong.<\/p>\n<p>You can&#8217;t set a soft breakpoint in a location in memory that you are about to load over. You need the soft breakpoint in the byte that gets loaded into memory; not in the byte that was overwritten by it.<\/p>\n<p>There are ways around that issue. The obvious one is to set a soft breakpoint in the instruction just before you jump into the loaded program. That works in most cases. Where it doesn&#8217;t is a ROM. You can&#8217;t write a soft breakpoint into a read-only memory. It is also difficult, but not impossible, if the last instruction before going to the loaded program is something like a &#8220;JP Z, program address&#8221;. That one instruction gets run lots of times, until all of the sector \/ track \/ program has been loaded. Only then, does it jump to the loaded program. With many debuggers \/ monitors you need to set and reset the breakpoint over and over again, until you&#8217;re up to the final one. Udo seems to have recognised this issue as he has included the ability to set a count against each of the soft breakpoints. It only &#8220;breaks&#8221; program execution once the specified count has been exhausted. You can run the loop 512 times and then break, for example.<\/p>\n<p>Another solution, if you have the space, is to change it to jump somewhere unused and that won&#8217;t get written over and then put a &#8220;JP&#8221; from there into the loaded program. That works if you don&#8217;t know when a buffer is going to fill or an XOFF is going to be received etc. You don&#8217;t need a count, just the exit circumstances.<\/p>\n<h2>Hard Breakpoints<\/h2>\n<p>It occurred to me, that there is an even easier way given that the Z80 cpu running the instructions is a piece of software. What is important, is when the CPU gets to a specific address in memory; not what&#8217;s in the memory. If the CPU itself (a piece of software in the case of an emulator) could stop when it gets to a specified address, we could happily load disk information over that address or relocate programs to it. It wouldn&#8217;t matter at all. Now, a hardware CPU, probably won&#8217;t come with an &#8220;AND STOP AT&#8221; instruction nor an &#8220;AND ALSO STOP AT&#8221; instruction. But, our software one can; and it doesn&#8217;t have to be a normal CPU instruction just some extra (virtual) pins on the side of the virtual chip.<\/p>\n<h2>Adding Hard Breakpoints to z80sim<\/h2>\n<p>sim1.c contains the cpu_z80() function. This is the bit that runs Z80 programs. It looks like:<br \/>\n<code><\/p>\n<pre>\r\nvoid cpu_z80(void)\r\n{\r\n    static int (*op_sim[256]) (void) = {\r\n        op_nop,    \/* 0x00 *\/\r\n        op_ldbcnn, \/* 0x01 *\/\r\n        ...\r\n        op_cpn,    \/* 0xfe *\/\r\n        op_rst38   \/* 0xff *\/\r\n    };\r\n\r\n    ...\r\n    do {\r\n            ...\r\n            states = (*op_sim[memrdr(PC++)]) (); \/* execute next opcode *\/\r\n            R++;    \/* increment refresh register *\/\r\n            ...\r\n    } while (cpu_state == CONTIN_RUN);\r\n    ...\r\n}\r\n<\/pre>\n<p><\/code><\/p>\n<p>It is possible to add a few lines before the &#8220;while&#8221; to exit the loop when a predetermined address is reached:<br \/>\n<code><\/p>\n<pre>\r\n            if (nHardBreakpoints)\r\n                for (i=0; i&lt;nHardBreakpoints; i++)\r\n                    if (PC==HardBreakpoints[i]) {\r\n                        printf(\"Hardware breakpoint %d reached at %04x\\n\", 1+i, PC);\r\n                        cpu_state = STOPPED;\r\n                        break;\r\n                    }\r\n<\/pre>\n<p><\/code><br \/>\nIf there are any hard breakpoints, look through them and, if our program counter (PC) matches one, say so and stop the CPU.<\/p>\n<p>You need to include some variables for information about the breakpoints:<br \/>\n<code><\/p>\n<pre>\r\nint nHardBreakpoints=0;\r\nint  HardBreakpoints[4];\r\n<\/pre>\n<p><\/code><\/p>\n<p>These aren&#8217;t static to the sim1.c module because we need to set them from the z80 simulator control module, simctl.c. That contains a do_go(char *s) function.<\/p>\n<p>The s parameter is the rest of the simulator command after the &#8220;g&#8221; for &#8220;go&#8221;. Normally, that would only contain the address to go from, or &#8220;\\0&#8221; to go from our current (PC) address.<\/p>\n<p>I&#8217;ve added a getHexArg() function as I want to check for 0, 1 or more addresses. It looks like:<br \/>\n<code><\/p>\n<pre>\r\nstatic int getHexArg(char *s, int *val) {\r\n    int i;\r\n\r\n    for (i=0; isspace((int)s[i]); i++) ;\r\n    if (!isxdigit((int)s[i])) return 0; \/\/ no hex digits found\r\n    *val= exatoi(s+i);\r\n    while (isxdigit((int)s[i])) i++;    \/\/ found. move past them\r\n    return i;\r\n}\r\n<\/pre>\n<p><\/code><br \/>\nThe idea here is to skip past any spaces, if we don&#8217;t now have a hex digit return an error, otherwise get the value of them (using Udo&#8217;s existing exatoi()), then skip past those and return how many characters of input we processed.<\/p>\n<p>We get two return values: the address in val, and how many characters as the return value of the function.<\/p>\n<p>Then it becomes easy to modify the start of do_go to this:<br \/>\n<code><\/p>\n<pre>\r\nstatic void do_go(char *s)\r\n{\r\n    int i, val, n;\r\n\r\n    \/\/ gss: allow g [addr] [, breakpoint]...\r\n    n= getHexArg(s,&val); s+=n;\r\n    if (n > 0) PC= val;\r\n\r\n    for (n=1, i=0; n>0 && i<4; i++) {\r\n        while (isspace((int)*s)) s++;\r\n        if (*s!=',') break;\r\n        s++; \r\n        n= getHexArg(s,&#038;val); s+=n;\r\n        if (n > 0) HardBreakpoints[i]= val;\r\n    }\r\n    nHardBreakpoints= i;\r\n    ...\r\n<\/pre>\n<p><\/code><br \/>\nWe attempt to get a hex arg. If there is one we set the PC to that address. We also move past that input (s+=n).<\/p>\n<p>Regardless of an address or not, we then look for up to 4 sequences of (optional spaces), a comma, and another hex arg.<\/p>\n<p>If we don&#8217;t see a comma we exit the loop (break). If we don&#8217;t see an address we exit the loop (for (&#8230;; n>0 &#8230;)).<\/p>\n<p>If we did see a comma, and if we did get an address (n > 0), we put that address in our HardBreakpoints[] table. At the end we set the number of hard breakpoints set.<\/p>\n<p>It lets me set breakpoints in places that are about to get overwritten. Examples are:<br \/>\n&#8211; 0080 before RDOS loads the boot sector there and jumps to it,<br \/>\n&#8211; 0100 before I load a CP\/M or CDOS program,<br \/>\n&#8211; in an instruction in another bank of memory before a cromix bank switch.<\/p>\n<p>It saves me from having to step through RDOS and from having to disassemble it enough to find where to set a soft breakpoint.<\/p>\n<p>It saves me from having to debug \/ step through the CP\/M loader to gain enough info to know where to set a soft breakpoint so I get control when a program loads. Of course, in that case, it is easier to use DEBUG, DDT or ZSID to load the program and let me debug it.<\/p>\n<p>I hope it is of use to you. It is present in my 0.07 Windows build of <a href=\"http:\/\/www.sydneysmith.com\/wordpress\/z80sim\/\">z80sim<\/a>. The full source code is available in <a href=\"http:\/\/www.sydneysmith.com\/products\/cpm\/z80sim\/\" rel=\"noopener\" target=\"_blank\">cpm\/z80sim\/<\/a>.<\/p>\n<p>This is part of the <a href=\"http:\/\/www.sydneysmith.com\/wordpress\/cpm-programs\/\">CP\/M topic<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The z80sim by Udo Munk comes with the ability to set soft breakpoints. A soft breakpoint is where an instruction is temporarily replaced with a HALT instruction and the program runs until it gets to that point. Then the emulator announces &#8220;we got to &#8230;&#8221;, puts the original instruction back, and lets the user choose &hellip; <a href=\"https:\/\/www.sydneysmith.com\/wordpress\/1763\/z80sim-hard-breakpoints\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">z80sim Hard Breakpoints<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[16,59],"tags":[],"_links":{"self":[{"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/posts\/1763"}],"collection":[{"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/comments?post=1763"}],"version-history":[{"count":6,"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/posts\/1763\/revisions"}],"predecessor-version":[{"id":1773,"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/posts\/1763\/revisions\/1773"}],"wp:attachment":[{"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/media?parent=1763"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/categories?post=1763"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sydneysmith.com\/wordpress\/wp-json\/wp\/v2\/tags?post=1763"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}