dhinson919 Posted October 10, 2018 Share Posted October 10, 2018 I've been working on a tool to help reverse-engineer 6502 roms. It's a long way from being finished but it can produce a code/data segments report that can help with this sort of thing. Attached are reports for the J, K and L revisions. There's an attempt to identify each separate segment of code or data based on a few heuristics and then each one is fingerprinted to help identify code that's common to multiple sets. It also attempts to recognize "dark code" segments that may be unused or called by non-standard means. A few more for comparison. 1050-revH.rom.seg.txt WSTR5.rom.seg.txt FLOPOS.rom.seg.txt 2 Quote Link to comment Share on other sites More sharing options...
+kheller2 Posted October 11, 2018 Share Posted October 11, 2018 PAGE 4 ATARI 1050 DISK DRIVE OS D8:FLOPOS1.M65 F07A 2091F1 2130 JSR DELAY1 ; wait another time F07D AD0004 2140 LDA FCNTRL F080 2901 2150 AND #1 ; now it must by ok F082 D019 2160 BNE FAIL ; otherwise it't wrong F084 A9F0 2170 LDA #$F0 ; get checksum of PROM F086 8501 2180 STA SEKBUF+1 ; start address F088 A900 2190 LDA #0 F08A 8500 2200 STA SEKBUF F08C 18 2210 CLC F08D A8 2220 TAY F08E 2230 PCHECK F08E 7100 2240 ADC (SEKBUF),Y ; add PROM bytes F090 C8 2250 INY F091 D0FB 2260 BNE PCHECK F093 E601 2270 INC SEKBUF+1 ; next page F095 D0F7 2280 BNE PCHECK F097 0900 2290 ORA #0 ; result must be zero F099 8500 2300 STA SEKBUF F09B F001 2310 BEQ TSTOK ; if so, everything is fine 2320 ; 2330 ; fatal error, break 2340 ; F09D 2350 FAIL F09D 00 2360 BRK ; hardware defect 2370 ; 2380 ; hardware test ok, do RAM test 2390 ; F09E 2400 TSTOK F09E A200 2410 LDX #0 ; write to RAM F0A0 2420 RAMTST F0A0 8A 2430 TXA F0A1 9500 2440 STA SEKBUF,X F0A3 E8 2450 INX F0A4 D0FA 2460 BNE RAMTST OS ROM test begins at line 2170 with actual rom addressof 0xF084. But the 8 bit checksum of the FLOPOS.ROM project is 0xF1 and will fail this test for zero checksum. Maybe it's defeated and made irrelevant in this manner? Maybe I don't understand? I did 8 bit checksum on the roms posted in #13 and none of them are zero checksum either so I don't think this section of code could ever work as designed... So they all pile up on the BRK instruction at line 2360? I am to understand that we have a bug with the BRK instruction, it does not operate as designed is about all I've been able to gather despite reading about it a lot. http://atariage.com/forums/topic/273221-dd-disc-track-structure-kryoflux-image/?do=findComment&comment=3917681 Embarassing 6502 math question... how can you keep doing an ADC on every byte in the ROM? A is only 8bits, even with the C flag (and/or V) so A will eventually be filled with FF+C, even with 2's complement math you run into a similar issue. Maybe I'm just confused Quote Link to comment Share on other sites More sharing options...
dhinson919 Posted October 11, 2018 Share Posted October 11, 2018 Embarassing 6502 math question... how can you keep doing an ADC on every byte in the ROM? A is only 8bits, even with the C flag (and/or V) so A will eventually be filled with FF+C, even with 2's complement math you run into a similar issue. Maybe I'm just confused Don't be embarrassed it's a perfectly reasonable question. The high order bytes are discarded. The catch is checksums aren't perfect. For 8-bit ones given any two sets of data there's a 1 in 256 chance (or something like that) that they will be falsely calculated as identical. So there's a small chance that there could be a ROM bit failure that's not detected. But apparently that's "good enough" for a consumer device such as this. An alternative to ADC could have been EOR since XOR is commonly used for simple checksums. Maybe the extra carry bit of ADC in the equation decreases the error chance a modicum over XORing? Quote Link to comment Share on other sites More sharing options...
+kheller2 Posted October 11, 2018 Share Posted October 11, 2018 Don't be embarrassed it's a perfectly reasonable question. The high order bytes are discarded. The catch is checksums aren't perfect. For 8-bit ones given any two sets of data there's a 1 in 256 chance (or something like that) that they will be falsely calculated as identical. So there's a small chance that there could be a ROM bit failure that's not detected. But apparently that's "good enough" for a consumer device such as this. An alternative to ADC could have been EOR since XOR is commonly used for simple checksums. Maybe the extra carry bit of ADC in the equation decreases the error chance a modicum over XORing? So writing a script that just adds up every byte in the ROM, constantly rolling over and taking a look at the LAST two additions (C is cleared when result is 0-255) to see if C is set is all thats needed to validate? Seems a bit wonky, but I'll roll with it. Quote Link to comment Share on other sites More sharing options...
dhinson919 Posted October 12, 2018 Share Posted October 12, 2018 So writing a script that just adds up every byte in the ROM, constantly rolling over and taking a look at the LAST two additions (C is cleared when result is 0-255) to see if C is set is all thats needed to validate? Seems a bit wonky, but I'll roll with it. Well you want to add up every byte tracking the carry bit and adding it in appropriately. In C it would look something like this unsigned char acc = 0; unsigned char carry = 0; short sum = 0; for (int i = 0; i < 0x1000; ++i) { sum = acc + data[i] + carry; carry = (sum > 0xFF) ? 1 : 0; acc = sum & 0xFF; } When I run this against the ROMs discussed here the only ones that pass the checksum (i.e. final acc == 0) are revK and revL. revH, revJ, FLOPOS and WSTR5 do not. WSTR5 may be excused because it was hacked to NOP the Z flag-setting comparison opcode of the checksum (ORA #0), so it passes checksum by virtue of the preceding INC opcode clearing Z (the devs exploited this). revJ and revH are excused because their checksums are BUGGED having no accumulator comparison opcode and end up passing for the same reason that WSTR5 does (the devs apparently unaware of this and were relying on the ADC Z flag setting). That leaves only FLOPOS with no excuses, which leads me to believe that it was never actually run on a 1050. Quote Link to comment Share on other sites More sharing options...
+kheller2 Posted October 12, 2018 Share Posted October 12, 2018 Well you want to add up every byte tracking the carry bit and adding it in appropriately. In C it would look something like this unsigned char acc = 0; unsigned char carry = 0; short sum = 0; for (int i = 0; i < 0x1000; ++i) { sum = acc + data[i] + carry; carry = (sum > 0xFF) ? 1 : 0; acc = sum & 0xFF; } When I run this against the ROMs discussed here the only ones that pass the checksum (i.e. final acc == 0) are revK and revL. revH, revJ, FLOPOS and WSTR5 do not. WSTR5 may be excused because it was hacked to NOP the Z flag-setting comparison opcode of the checksum (ORA #0), so it passes checksum by virtue of the preceding INC opcode clearing Z (the devs exploited this). revJ and revH are excused because their checksums are BUGGED having no accumulator comparison opcode and end up passing for the same reason that WSTR5 does (the devs apparently unaware of this and were relying on the ADC Z flag setting). That leaves only FLOPOS with no excuses, which leads me to believe that it was never actually run on a 1050. Wait, that's where I get lost. ADC will always set the C up or down depending on the condition of the add, ignoring if it was already set or not so why am I tracking it? Or is that wrong... The fact that your code computes a legit checksum however has me wondering if all the 8 bit checksum programs and my own little one are completely wrong. Quote Link to comment Share on other sites More sharing options...
phaeron Posted October 12, 2018 Share Posted October 12, 2018 This type of checksum is known as a one's complement sum. It's effectively a mod by 255 as the result will always be within $01 to $FF unless the source data is all zeroes. From a sum of all bytes where at least one byte is nonzero, the 1's complement sum is ((sum(bytes) - 1) mod 255) + 1. Using 255 instead of 256 has some advantages, like not missing an even number of errors in bit 7. Note that the 1050's checksum routine is missing a final ADC #0, so even in rev. L it's still slightly broken and not quite the same as the SIO checksum. 1 Quote Link to comment Share on other sites More sharing options...
+kheller2 Posted October 12, 2018 Share Posted October 12, 2018 This type of checksum is known as a one's complement sum. It's effectively a mod by 255 as the result will always be within $01 to $FF unless the source data is all zeroes. From a sum of all bytes where at least one byte is nonzero, the 1's complement sum is ((sum(bytes) - 1) mod 255) + 1. Using 255 instead of 256 has some advantages, like not missing an even number of errors in bit 7. Note that the 1050's checksum routine is missing a final ADC #0, so even in rev. L it's still slightly broken and not quite the same as the SIO checksum. Wow. Then all those 8bit checksum programs are wrong. Here is what I was missing, for those following along (as if you really care LOL)... The ADC adds the current contents of A, the memory location and also the value of 1 if the C flag is set. This last part I completely missed and isn't expressly called out in most online documentation "describing" ADC, but is quite annoying called out in the book "Assembly Language Programming for the Atari Computers". And since ORA ignores the C flag in its ORing, the "K" code checksum passes since C was set. It all becomes clear now Thank you! The last ML coding I did was at University on an IBM 370 or 390... can't remember which. I should probably dig out my 3" thick book on C Algorithms too. LOL. Quote Link to comment Share on other sites More sharing options...
dhinson919 Posted October 13, 2018 Share Posted October 13, 2018 Wait, that's where I get lost. ADC will always set the C up or down depending on the condition of the add, ignoring if it was already set or not so why am I tracking it? Or is that wrong... The fact that your code computes a legit checksum however has me wondering if all the 8 bit checksum programs and my own little one are completely wrong. You may be overthinking it a bit. A checksum is just a fancy term for a hash code where a hash is just a way to reduce a large number into a smaller number that is as unique as possible. With the idea that if two hash codes are identical then there's a high probability that they were reduced from the same large number. In the case of the 1050 the large number is 32,768 bits wide or the set of all bits of the rom. The challenge is to reduce that to an 8 bit wide hash code. Which is almost laughable, but maybe not unreasonable for an 8-bit floppy drive with less than 256 bytes of ram to work with. There are any number of ways that could have been done since all that matters is that it's relatively fast and the end result is as unique as possible. The 1050 engineers decided to use the ADC operation as their algorithm. They could've also used the EOR operation with similar results since ADDs and XORs are closely related. Or maybe 9-bit CRC. The important thing is that you don't really have to understand the algorithms used as long as you apply them consistently. ADC is a little bit tricky because of the carry-around so if those checksum programs aren't taking that into consideration then they are essentially using a different hash algorithm. Quote Link to comment Share on other sites More sharing options...
dhinson919 Posted October 13, 2018 Share Posted October 13, 2018 This type of checksum is known as a one's complement sum. It's effectively a mod by 255 as the result will always be within $01 to $FF unless the source data is all zeroes. From a sum of all bytes where at least one byte is nonzero, the 1's complement sum is ((sum(bytes) - 1) mod 255) + 1. Using 255 instead of 256 has some advantages, like not missing an even number of errors in bit 7. Note that the 1050's checksum routine is missing a final ADC #0, so even in rev. L it's still slightly broken and not quite the same as the SIO checksum. You're correct from the perspective of doing signed math, but as I pointed out in the previous post that's not what's really going on. The checksum logic is just computing a hash code of the rom contents. So the final carry bit being lost is not an issue as long as all checksum computations do it. I didn't know that formula though, thanks for that. Quote Link to comment Share on other sites More sharing options...
+kheller2 Posted October 13, 2018 Share Posted October 13, 2018 Just to be clear with everyone, my misunderstanding and math errors occurred because of how ADC is defined in most places I looked. Basically almost everything I read stated that adc adds two umbers and sets the carry flag up or down. Great. Not much said that adc is actually the value of the carry bit plus the two numbers and then the flag is adjusted up or down. In my poor mental world that meant I better use or store (in two bytes) the result of the add before doing another or the msb is lost so why should I track it at all if its only ever used after a sum (not before - which it actually is). Trust me I feel dumb for not getting that. The checksum makes perfect sense once I grasped that adc is c+a+value and then set c. That one little value of 1added over and over as needed makes the check work. It didnt help that several 8-Bit math calculators I used online did not work this way and reenforced my misunderstanding. Now how do you calculate the checksum value to store in the code w/o manually figuring it out. Its easy to manually guess and figure it out now but something Mathematical would be better. 2 Quote Link to comment Share on other sites More sharing options...
_The Doctor__ Posted October 13, 2018 Share Posted October 13, 2018 This of course is another case of a lost piece of information and the understanding needed to make it work in the real world again. Please distill it into a form for all and have that re propagated into the wild, a note such as this would be well served into one of the Phaeron nuggets of knowledge papers. Quote Link to comment Share on other sites More sharing options...
dhinson919 Posted October 13, 2018 Share Posted October 13, 2018 Just to be clear with everyone, my misunderstanding and math errors occurred because of how ADC is defined in most places I looked. Basically almost everything I read stated that adc adds two umbers and sets the carry flag up or down. Great. Not much said that adc is actually the value of the carry bit plus the two numbers and then the flag is adjusted up or down. In my poor mental world that meant I better use or store (in two bytes) the result of the add before doing another or the msb is lost so why should I track it at all if its only ever used after a sum (not before - which it actually is). Trust me I feel dumb for not getting that. The checksum makes perfect sense once I grasped that adc is c+a+value and then set c. That one little value of 1added over and over as needed makes the check work. It didnt help that several 8-Bit math calculators I used online did not work this way and reenforced my misunderstanding. Now how do you calculate the checksum value to store in the code w/o manually figuring it out. Its easy to manually guess and figure it out now but something Mathematical would be better. The checksum salt is at location $F004 so injecting it into the rom data would look like unsigned char acc = 0; unsigned char carry = 0; short sum = 0; unsigned char salt; for (int i = 0; i < 0x1000; ++i) { // skip location of salt: if (i == 4) continue; sum = acc + data[i] + carry; carry = (sum > 0xFF) ? 1 : 0; acc = sum & 0xFF; } salt = 0xFF - acc; data[4] = salt; Quote Link to comment Share on other sites More sharing options...
+kheller2 Posted October 13, 2018 Share Posted October 13, 2018 This of course is another case of a lost piece of information and the understanding needed to make it work in the real world again. Please distill it into a form for all and have that re propagated into the wild, a note such as this would be well served into one of the Phaeron nuggets of knowledge papers. I'm not sure this is nugget of information that is lost, just something I overlooked, perhaps, when reading about ADC. Most online references, only make mention of the carry bit being set, not that it was used in the actual computation. Direct from the MCS6500 MOS programming manual: "This instruction adds the value of memory and carry from the previous operation [emphasis mine] to the value of the accumulator and stores that result in the accumulator. The symbolic representation for this instruction is: A + M + C -> A" All I can say is that its really good to have copies of these very old books. 3 Quote Link to comment Share on other sites More sharing options...
+Stephen Posted October 13, 2018 Share Posted October 13, 2018 I'm not sure this is nugget of information that is lost, just something I overlooked, perhaps, when reading about ADC. Most online references, only make mention of the carry bit being set, not that it was used in the actual computation. Direct from the MCS6500 MOS programming manual: "This instruction adds the value of memory and carry from the previous operation [emphasis mine] to the value of the accumulator and stores that result in the accumulator. The symbolic representation for this instruction is: A + M + C -> A" All I can say is that its really good to have copies of these very old books. Odd that many books wouldn't mention it. Most that I read clearly stated that before using the add instruction, you have to clear the carry, and before subtract you have to set it for accurate results. Cool that it was figured out though. That's why I love these forums, there's a wealth of information and also, many people willing to share that information. Let's keep those 6502s humming 2 Quote Link to comment Share on other sites More sharing options...
dhinson919 Posted October 14, 2018 Share Posted October 14, 2018 This of course is another case of a lost piece of information and the understanding needed to make it work in the real world again. Please distill it into a form for all and have that re propagated into the wild, a note such as this would be well served into one of the Phaeron nuggets of knowledge papers. You're absolutely right Doctor. Here's is my contribution to this effort. Atari 1050 ROM Checksum Study October 2018 atariage.com:dhinson919 Below are the results of an investigation into the Atari 1050 ROM checksum routine. Rumors abound that it is broken and many of them are correct, however it depends on which ROM is in use. Some of the accusations are incorrect based on a few simple misunderstandings and that has led to an aura of mystery surrounding the otherwise unremarkable 15-instruction sequence of opcodes. In summary, later revisions of the Atari production ROM checksum correctly as designed. However earlier revisions do not but then “fail to fail” as designed due to a bug in the checksum subroutine. One later production ROM shows evidence of intentional sabotaging of the routine. Still another community-supplied ROM simply fails due to an incorrect checksum salt. Table 1 contains information about the ROMs studied. Figure 1 shows the C language pseudo-code for the checksum routine used by the study. Table 2 shows the checksum findings. Table 3 compares the instructions of the various checksum routines side-by-side. The reasons some of the routines fail to work correctly are explained. Finally, a few myths busted by this study are discussed. Table 1: Source Files Nickname File MD5 -------- ------------- -------------------------------- revH 1050-revH.rom ab79b267f5e7ea2c9acbd45e1f4fbd57 revJ 1050-revJ.rom dd296b6e2aacfd89cc94cc7e6dcebfd3 revK 1050-revK.rom 5acf59fff75d36a079771b34d7c7d349 revL 1050-revL.rom 2575d0514c5fe2dd6f5bd3f0ec434eb7 WSTR5 WSTR5.rom 5754ab8bef5c93e0caf17425d96bd80e FLOPOS FLOPOS.rom ad4b6ec7de5f3fe165df02d832e31be4 Figure 1: 1050 Checksum Algorithm short sum; unsigned char A = 0; // accumulator unsigned char C = 0; // carry bit for (int offset = 0; offset < 0x1000; ++offset) { sum = A + data[offset] + C; A = sum & 0xFF; C = (sum > 0xFF) ? 1 : 0; } assert(A == 0); Table 2: Checksum Findings Found Correct Calculated Nickname Checksum Salt* Checksum Salt* Checksum -------- -------------- -------------- ---------- revH $F6 $F7 $FE = FAIL revJ $F6 $CE $28 = FAIL revK $57 $57 $00 = PASS revL $38 $38 $00 = PASS WSTR5 $46 $61 $E4 = FAIL FLOPOS $57 $FA $5C = FAIL * Checksum salt location = $F004 Table 3: Checksum Routines Compared revK, revL & revH & revJ FLOPOS WSTR5 -------------- -------------- -------------- F084 LDA #$F0 LDA #$F0 LDA #$F0 F086 STA $01 STA $01 STA $01 F088 LDA #0 LDA #0 LDA #0 F08A STA SEKBUF STA SEKBUF STA SEKBUF F08C CLC CLC CLC F08D TAY TAY TAY F08E ADC (SEKBUF),Y ADC (SEKBUF),Y ADC (SEKBUF),Y F090 INY INY INY F091 BNE $F08E BNE $F08E BNE $F08E F093 INC $01 INC $01 INC $01 F095 BNE $F08E BNE $F08E BNE $F08E F097 STA SEKBUF ORA #0 NOP F098 NOP F099 BEQ $F09C STA SEKBUF STA SEKBUF F09B BRK BEQ $F09E BEQ $F09E F09D BRK BRK Discussion: Why do some checksum tests pass when they should fail? ------------------------------------------------------------------- * revH & revJ Both of these revisions use incorrect salts, yet revJ is reported to have originated from a dump of an early production device ROM and revH from a possible predecessor to it. So it’s puzzling that the checksum test would fail with revJ since devices with it worked fine in the field. Also curious, since it shares the same code segment and salt with revH that would indicate that the checksum failure was potentially a long-standing problem spanning multiple production runs. How could that be? It appears that a benevolent bug was in play. The BEQ instruction at F099 triggers because the Z flag is always set due to the final INC instruction execution at F093. Control flow passes through the BNE at F095 unbranched because SEKBUF+1 overflows (as designed) during the final INC, setting Z. But then there are no further instructions to evaluate the accumulator to override INC’s Z setting. This was apparently discovered and corrected in the subsequent revisions. * revK & revL The bug in the prior revisions was corrected by inserting an ORA between the checksum loop and the final branch. Since the operand to ORA is #0 it was clearly intended just for its status flags setting side effect. In this case the checksum in the accumulator would’ve been tested for zero. Thus the checksum in these revisions functions as designed. * WSTR5 For reasons unknown the engineers of this revision reverted the bug correction applied to the previous revisions by overwriting the ORA instruction with NOPs. Thus the Z flag is always set at the final BEQ due to INC’s influence as was the case with revJ, effectively ignoring the checksum result. This raises a couple of questions though. It’s understandable why NOP’s would have been used to preserve the offsets of all of the other code and data segments after this early subroutine. But if the intent of the engineers was to completely disable the checksum routine then why did they not NOP the entire loop saving many processor cycles? Or simply branch around the unwanted code in the beginning? Could it be that they also had a misunderstanding about the Z flag influence of the INC and INY instructions, and about the purpose of the otherwise dubious-looking ORA #0 instruction, and thought they were instead relying on the Z flag left by the ADC as it would at first seem reasonable to do? If that was their intent then they may never have known that they were actually mistaken after this patch was initially tried since it would appear to have the same effect as a correct checksum, which in fact they did not have. (A genuine mystery...) * FLOPOS The actual deployment history of this community-contributed alteration of the revK revision by Pascher & Derks is a subject of speculation. But it seems clear that this ROM image could have never functioned correctly on a 1050 since it uses the “correct” checksum routine found in revK but with an incorrect checksum salt. Discussion: Myths Busted ------------------------- * Myth #1, “The BRK instruction must be buggy!” This would’ve been a wonderful explanation, but the kind of theory that requires extraordinary evidence which doesn’t exist and in the end it’s not needed to understand the behaviors. * Myth #2, “The checksums only pass when running on a live 1050 due to defective chip-selected data bus input!” This one was a bit more plausible and I was personally intrigued by it enough to spend a few hours mapping the chip-selects of all of the 1050s devices using schematics from both Atari and SAMS Publishing. Based the assumed accuracy of the published schematics I can dispel this myth. The relevant chips can only talk to the data bus when the ROM range (A12 line) isn’t being addressed. * Myth #3, “Maybe Atari engineers had inside knowledge of 6502 microcode bugs and exploited them in ways that seem like magic!” Sadly I confess I was probably the originator of this rumor. Speculation that was the result of being up way too late one frustrating night pondering the seemingly enigmatic 1050 checksum. Which was, as is often the case with sufficiently advanced technology, not mysterious at all just misunderstood. 4 Quote Link to comment Share on other sites More sharing options...
dhinson919 Posted October 14, 2018 Share Posted October 14, 2018 Note that the 1050's checksum routine is missing a final ADC #0, so even in rev. L it's still slightly broken and not quite the same as the SIO checksum. You're correct from the perspective of doing signed math, but as I pointed out in the previous post that's not what's really going on. The checksum logic is just computing a hash code of the rom contents. So the final carry bit being lost is not an issue as long as all checksum computations do it. I didn't know that formula though, thanks for that. Table 2: Checksum Findings Found Correct Calculated Nickname Checksum Salt* Checksum Salt* Checksum -------- -------------- -------------- ---------- revH $F6 $F7 $FE = FAIL revJ $F6 $CE $28 = FAIL revK $57 $57 $00 = PASS revL $38 $38 $00 = PASS WSTR5 $46 $61 $E4 = FAIL FLOPOS $57 $FA $5C = FAIL * Checksum salt location = $F004 @phaeron, I believe you might have explained why the revH checksum salt was "off by 1" as shown in the table above. It appears that the code that the Atari engineers used to generate that salt did in fact use one's compliment checksumming as you explained which would've added in the remaining carry bit as a final step. The result of that calculation would be $F6 and that's what they burned. However because the checksum routine in the 1050 does not add in the final carry, which is relevant in this case, the value actually needed would have been $F7. The remarkable thing is it is all moot because the revH checksum test was broken and it appears they did not know! Since the test was broken the checksum was then never an issue for the revJ engineers which is why they didn't bother to update the $F6 from the previous revision. Only until revK was the problem noticed and instead of fixing the 1050 checksum routine they chose to modify whatever they used to generate the salt! Gentlemen, thank you. This comedy of errors has all been worth the price of admission. 4 Quote Link to comment Share on other sites More sharing options...
_The Doctor__ Posted October 14, 2018 Share Posted October 14, 2018 Makes you wonder who was smoking or doing what at Atari sometimes crazy genius parties. lot's of- oops! but hey it works anyway! happy dances and moves on. Quote Link to comment Share on other sites More sharing options...
1050 Posted October 15, 2018 Share Posted October 15, 2018 Gentlemen, thank you. This comedy of errors has all been worth the price of admission. Interesting having you on board as well. Curious as to the term you use as checksum salt though. Haven't run into it before and wondering if the salt part of it is an acronym perhaps? Pray tell what is it if so? You seem to have discovered the location for the checksum storage correctly too. I was disappointed to learn that my hex editor's choice of checksum generation was so lacking in it's description and/or methods used. The internet was of no or little help either but by shear luck I figured out a way to get to where I wanted to be in the first place. My choices include something called checksum-8 which I assumed was it. I was wrong, that is only the right portion of an uncompounded add. Truncated if you will. checksum-16 adds two more digits to the left of the same two right side digits. Checksum-32 gives more digits to the left even further. If you take those digits and add them to a total add then and only then do you get the accumulated add as done by the rolling over with carry of the A register by relentless ADC operations - which is what I wanted. In other words what you achieved with your C snippet which to me is for study only since I have no idea how to make that work for me. I do have your listed results but I was not getting the same results until I ran BUG/65 in an emulator using this code typed in rev-J.rom was loaded to 0x7000 by the emulator monitor previously and G6000 @6015 was entered into BUG/65 to get 29h sitting in reg A. Other attempts weren't necessary after I found out how to work with checksum-32 results moar better than the internet could describe it to me. That came as a result when I selected all the checksums at once and noticed that the 8 version was just the truncated form of all those higher and they were truncated in turn as well. Oh, so those are not rolled over with carry... And the light came on. Further studies into the BRK instruction show that it's related to power up, RESET, and NMI vectors and behavior as well. Except the B flag is set and pushed after the PC is pushed and the vector loaded and ran is from FFFE-FFFF. Well sir, that is the blink forever more with the busy light routine and that clearly NEVER happens with Atari 1050 code unless there is a defective chip in the works somewhere. So something is odd somewhere with BRK command or the way it is meant to be used. I found this on the BRK instruction to notice that our own Rybags commented on that page. https://www.pagetable.com/?p=410 This is by far more information on the BRK instruction than I have ever seen before and yet there are also wrong info given elsewhere. Looking over Rockwell information I see that NMOS version ignores BRK vector and CMOS 65Cxx version uses it instead. So it never was a thing for us to be able to use. Atari sure wrote a lot of code for a neutered instruction. And different code could be made to same effect but they didn't do that either. Very curious situation. 1 Quote Link to comment Share on other sites More sharing options...
dhinson919 Posted October 15, 2018 Share Posted October 15, 2018 Interesting having you on board as well. Curious as to the term you use as checksum salt though. Haven't run into it before and wondering if the salt part of it is an acronym perhaps? Pray tell what is it if so? A salt is data added to other data to affect the outcome of a hash. It's usually used to make the original data less guessable but in this case it's used to make the hash result entirely guessable. If you take those digits and add them to a total addthen and only then do you get the accumulated add as done by the rolling over with carry of the A register by relentless ADC operations - which is what I wanted. In other words what you achieved with your C snippet which to me is for study only since I have no idea how to make that work for me. I do have your listed results but I was not getting the same results until I ran BUG/65 in an emulator using this code typed in In your snippet I suspect the final ADC was what was originally intended in the revH (and probably earlier) code. That would make the operation a complete one's complement operation as Phaeron pointed out. It was either left out by mistake or intentionally since a final carry addition is not actually needed to hash the rom effectively. But what is needed is a zero test of A which is also what ADC provided. Not replacing it with another zero testing Z setting instruction is what broke the checksum routine. So it never was a thing for us to be able to use.Atari sure wrote a lot of code for a neutered instruction. And different code could be made to same effect but they didn't do that either. Very curious situation. It seems plausible that BRK could behave differently in chips from different makers. It's not intended to be used routinely so maybe there was less concern about keeping its behavior compatible with other chips. 1 Quote Link to comment Share on other sites More sharing options...
+kheller2 Posted October 16, 2018 Share Posted October 16, 2018 Ok, so after building some silly tables in Excel I'm 100% convinced of the following: STA STATUS ;** was STA ERROR in K version [Hmm, maybe this is right.. but.. need to validate] JSR DELAY1 ; 20 ms delay after SEEK ;** was JMP DELAY1 in K version [Hmm.. maybe this is right] LDA #0 ;** was LDA #1 in K version [Wrong, see above post] RSTAT JSR FORCE ; stop controller ;** was JSR DFORCE in K version [Double vs single force, wonder if there was a reason for that] INC SBUF+1 ;** was INC SBUF in K version [Read routine test, VERY VERY wrong, You stored track 39 in SBUF+1, how you gonna INC that?!?] DEC SBUF ;** was DEC SBUF+1 in K version [Read routine test, VERY VERY wrong, You stored track 0 in SBUF, how you gonna DEC that?] INC SBUF+1 ;** was INC SBUF in K version [Write routine test, wrong, just a copy of the above wrongness] DEC SBUF ;** was DEC SBUF+1 in K version [wrong] AND #$7F ; motor on ;** was AND #$F7 in K version [We all agree this is wrong] Looking at the code again, I'm not sure about this change for JSR DELAY1. That doesn't seem right, because DELAY1 will do a RTS which dumps you right back into this section of code and the next block of code does the IRQ to interrupt the FDC twice... which makes no sense that you would want to do that every time for any track's greater than track 20. So, one last thing to investigate so we can write this whole "fixed" version off the books as being wrong. 3 Quote Link to comment Share on other sites More sharing options...
phaeron Posted October 16, 2018 Share Posted October 16, 2018 BRK doesn't differ between chips from different manufacturers. It differs between different chip models, specifically the 6502 (NMOS) vs the 65C02 (CMOS). Neither the 6502 nor the 65C02 has a dedicated BRK vector -- they reuse the IRQ vector for it. The sharing of the same vector is where the issue with the NMOS 6502 occurs -- essentially, an interrupt arriving at the right time can hijack a BRK instruction sequence and take it over. This then either causes the BRK to be lost or makes it at least more expensive to distinguish. The CMOS 65C02 version prevents the interrupt from overlapping the BRK instruction so the BRK is guaranteed to execute separately from interrupts. It's still expensive to separate BRK cleanly, just less so, which is why the 65C816 added a separate BRK vector entirely. That having been said, the 1050 uses a 6507 instead of a 6502. The 6507 doesn't have this problem of BRK conflicting with interrupts because it has no interrupts -- the pins for them aren't connected. This means that software is free to use BRK as a one-byte subroutine call, which the 1050 does by routing BRK to the 'epic fail' routine. 3 Quote Link to comment Share on other sites More sharing options...
Nezgar Posted October 19, 2018 Share Posted October 19, 2018 I received DavidMil's 1050 'Rev H' chip today, dropped it in my EPROM programmer, dumped it, and verified that the binary matches the one I patched in post #73. (CRC32 6D9D589B) The first byte was indeed $FB as I predicted like Rev J, K, L, and the rest of the bytes were consistent with the previous dump: and at offset 0FFH you can see the "H" version byte: I also took some high res macro shots of this particular chip and its label. I didn't notice this before, but if you look closely it appears there is a letter "H" written with a red ball-point pen on the end of the label. I took another macro shot, with a flashlight lighting from the side to highlight the texture of the label, which help make the "H" more visible! Corroborates the "H" found in the dump. I'd like to know if this version has any differences that would cause copy protection checks to behave differently than J/K/L. But maybe that behaviour is only a trait of the fabled Rev E/F. Search is now on for those! 6 Quote Link to comment Share on other sites More sharing options...
dhinson919 Posted October 19, 2018 Share Posted October 19, 2018 The only difference between H and J other than the revision code is a curious change in the subroutine known as RDBYTE in the P&D annotation (see bottom). This is where the CPU works with the RIOT chip to read in a byte from the SIO port. Two locations that are NOPs in H are replaced with a LSR $B6 in J. The reason that's curious is that $B6 does not appear to have any other use in the ROM code. It would be equivalent to SBUF+5 in the P&D. SBUF is on the tail end of the used RIOT RAM. SBUF, SBUF+1 and SBUF+2 are mentioned in P&D but nothing past that. I've also confirmed using a different disassembly that location $B6 isn't used anywhere else. So what's going on? It could be debugging code. That the instruction replaces NOPs suggests that the NOPs may have replaced this instruction originally in a prior revision in order to disable it but then it was reinstated in J. The LSR of $B6 would happen every time a byte was read from the RIOT (/SIO port). Why would one bother to right-shift a location that isn't read anywhere else? Well if you had a test jig that allowed you to inject a value into that RIOT RAM location then you could use the changing bit pattern resulting from the LSR to gate some other external logic that supplies test input to the SIO. For example if you wanted to gate the input for 8 byte read cycles you would inject $FF. 4 byte read cycles would be $0F. Alternating byte cycles would look like $55. Etc. Or it could simply be a way to delay 1 extra clock cycle in this time-sensitive code by converting 2 NOPs (4 cycles) to a LSR of an otherwise unused address (5 cycles). Both seem a bit dubious to be honest but it's the only theories I have at the moment. revH revJ ---------- ---------- F3D1 STX $B3 F3D3 BIT DRA F3D6 BVC $F3FD F3D8 BIT DRB F3DB BVC $F3D3 F3DD SEC F3DE LDA #$80 F3E0 LDX #5 F3E2 DEX F3E3 BNE $F3E2 F3E5 LDX #6 F3E7 DEX F3E8 BNE $F3E7 F3EA NOP LSR $B6 F3EB NOP " F3EC BIT DRB F3EF BVC $F3F4 F3F1 CLC F3F2 BCC $F3F6 F3F4 SEC F3F5 NOP F3F6 ROR A F3F7 BCC $F3E5 F3F9 TAY F3FA LDX $B3 F3FC RTS 2 Quote Link to comment Share on other sites More sharing options...
+kheller2 Posted October 19, 2018 Share Posted October 19, 2018 So H was 4 cycles, J was 5 cycles, and oddly enough in K and L they made that 3 NOPs, so 6 cycles. Using LSR also kept it at 2 bytes, so no code shifting.. if that mattered. 1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.