// To comple and run: // g++ GenHTML.cp -Wall -o GenHTML // ./GenHTML > AVRSelectedOpcodeBlocks.html // ./GenHTML --full > AVRAllOpcodes.html // open *.html // [TODO] // generate text to summarize the reserved instructions: number, groups, and contiguousness // omit the weird operand from the 2-word insructions // omit some of the "; NNN" comments (decimal numbers) // More colors in the summary. Flag the most interesting pages. // use CSS // [ ] roll over multi-mnemonic summary blocks #include #include #include #include using namespace std; vector instrs; vector mnemonics; vector blocksOf256Summary; vector blocksOf256FullDescription; vector singleWordInstructions; map gMnemonicDescriptionMap; bool gBlocksToGenerate[256]; int gReserved = 0; void DisplayBlock(int); //---------------------------------------------------------------------------------------- // HTML Generation //---------------------------------------------------------------------------------------- string gIndentString; const bool kIndent_No = false; const bool kIndent_Yes = true; class StTag { public: StTag(const string& tag, const string& attributes, bool indent) : fTag(tag), fIndent(indent) { printf("%s<%s%s>\n", gIndentString.c_str(), fTag.c_str(), attributes.empty() ? "" : (" " + attributes).c_str()); if (fIndent) gIndentString += '\t'; } ~StTag() { if (fIndent) gIndentString.resize(gIndentString.size() - 1); printf("%s\n", gIndentString.c_str(), fTag.c_str()); } private: string fTag; bool fIndent; }; class StTagSpan { public: StTagSpan(const string& tag, const string& attributes = "", bool enable = true) : fEnabled(enable), fTag(tag), fNested(sNesting > 0) { if (fEnabled) { sNesting++; printf("%s<%s%s>", (fNested > 0) ? "" : gIndentString.c_str(), fTag.c_str(), attributes.empty() ? "" : (" " + attributes).c_str()); } } ~StTagSpan() { if (fEnabled) { printf("", fTag.c_str()); if (!fNested) printf("\n"); sNesting--; } } private: bool fEnabled; string fTag; bool fNested; static int sNesting; }; int StTagSpan::sNesting = 0; // end of HTML Generation support string NumString(int n) { char s[20]; sprintf(s, "%d", n); return s; } string VersionOfAVRObjdump() { FILE* f = popen("avr-objdump --version", "r"); char vers[256]; fgets(vers, 256, f); pclose(f); return vers; } bool TwoWordInstruction(int opcode) { return find(singleWordInstructions.begin(), singleWordInstructions.end(), opcode) == singleWordInstructions.end(); } bool NewForXmega(int opcode) { if (opcode == 0x95F8 /* SPM #2 */) return true; // if (opcode == 0x94C8 /* SPM #2 ?? -- that's a typo in the Atmel docs, since this is already "cls" */) // return true; if ((opcode & 0xFF0F) == 0x940B /* DES k */) return true; return false; } bool BlockContainsAnyTwoWordInstructions(int block) { for (int opcode = block * 256; opcode < block * 256 + 256; ++opcode) { if (TwoWordInstruction(opcode)) return true; } return false; } bool ReservedInstruction(int opcode) { return instrs[opcode] == "[R]"; } void CreateMnemonicDescriptionMap() { map& m = gMnemonicDescriptionMap; m["[R]"] = "Reserved for future definition"; m["adc"] = "ADd register to register with Carry"; m["add"] = "ADd register to register"; m["adiw"] = "ADd Immediate (0..63) to Word (register pair)"; m["and"] = "AND register with register"; m["andi"] = "AND register (r16..r31) with the Immediate value (0..255)"; m["asr"] = "Arithmetic Shift Right register by one bit"; m["bclr"] = "Bit CLeaR in SREG"; m["bld"] = "Bit LoaD from T in SREG to the specified bit of the register"; m["brbc"] = "BRanch if Bit in SREG Clear"; m["brbs"] = "BRanch if Bit in SREG Set"; m["brcc"] = "BRanch if Carry Clear"; m["brcs"] = "BRanch if Carry Set"; m["break"] = "stop for debugger"; m["breq"] = "BRanch if EQual (Z flag set)"; m["brge"] = "BRanch if Greater or Equal (signed)"; m["brhc"] = "BRanch if Half-carry Clear"; m["brhs"] = "BRanch if Half-carry Set"; m["brid"] = "BRanch if global Interrupt flag disabled"; m["brie"] = "BRanch if global Interrupt flag enabled"; m["brlo"] = "BRanch if LOwer (unsigned)"; m["brlt"] = "BRanch if Less Than (signed)"; m["brmi"] = "BRanch if MInus (N flag set)"; m["brne"] = "BRanch if Not Equal (Z flag clear)"; m["brpl"] = "BRanch if PLus (N flag clear)"; m["brsh"] = "BRanch if Same or Higher (C clear)"; m["brtc"] = "BRanch if T flag Clear in SREG"; m["brts"] = "BRanch if T flag Set in SREG"; m["brvc"] = "BRanch if oVerflow flag Clear in SREG"; m["brvs"] = "BRanch if oVerflow flag Set in SREG"; m["bset"] = "Bit SET in SREG"; m["bst"] = "Bit STore from the specified bit in the register"; m["call"] = "CALL a subroutine at an absolute address"; m["cbi"] = "Clear one Bit in an I/O register"; // m["cbr"] = "Clear Bits in Register"; this is just ANDI m["clc"] = "CLear Carry flag in SREG"; m["clh"] = "CLear Half-carry flag in SREG"; m["cli"] = "CLear global Interrupt-enable flag in SREG"; m["cln"] = "CLear Negative flag in SREG"; // m["clr"] = "CLear Register"; // this is just EOR m["cls"] = "CLear Signed flag in SREG"; m["clt"] = "CLear T flag in SREG"; m["clv"] = "CLear oVerfloag flag in SREG"; m["clz"] = "CLear Zero flag in SREG"; m["com"] = "COMplement - flip all the bits in the register"; m["cp"] = "ComPare the two registers"; m["cpc"] = "ComPare the two registers with Carry"; m["cpi"] = "ComPare the register with the immediate value (0..255)"; m["cpse"] = "ComPare two registers and Skip next instruction if equal"; m["dec"] = "DECrement the register by one"; m["des"] = "Data Encryption Standard - perform one round of DES encryption or decryption"; m["eicall"] = "Extended Indirect CALL to subroutine (at EIND + Z)"; m["eijmp"] = "Extended Indirect JuMP (to EIND + Z)"; m["elpm"] = "Extended Load from Program Memory (from RAMPZ + Z)"; m["eor"] = "Exclusive OR register with register"; m["fmul"] = "Fractional MULtiply unsigned (from two registers, into R1:R0)"; m["fmuls"] = "Fractional MULtiply Signed (from two registers, into R1:R0)"; m["fmulsu"] = "Fractional MULtiply Signed with Unsigned (from two registers, into R1:R0)"; m["icall"] = "Indirect CALL to subroutine (at Z)"; m["ijmp"] = "Indirect JuMP (to Z)"; m["in"] = "IN - load the register from the I/O location"; m["inc"] = "INCrement the register by one"; m["jmp"] = "JuMP to the absolute address"; m["ld"] = "LoaD register indirect from data space"; m["ldd"] = "LoaD register indirect from data space with Displacement"; m["ldi"] = "LoaD Immediate value (0..255) into the register (r16..r31)"; m["lds"] = "Load register direct from Data Space"; m["lpm"] = "Load register from Program Memory (from Z)"; m["lsl"] = "Logical Shift Left one bit"; m["lsr"] = "Logical Shift Right one bit"; m["mov"] = "MOVe register to register"; m["movw"] = "MOVe Word - copy register pair to another register pair"; m["mul"] = "MULtiply unsigned (from two registers, into R1:R0)"; m["muls"] = "MULtiply Signed (from two registers, into R1:R0)"; m["mulsu"] = "MULtiply Signed by Unsigned (from two registers, into R1:R0)"; m["neg"] = "NEGate the register"; m["nop"] = "No OPeration"; m["or"] = "OR register with register"; m["ori"] = "OR register (R16..R31) with Immediate value (0..255)"; m["out"] = "OUT - store the register to an I/O location"; m["pop"] = "POP one byte from stack into the register"; m["push"] = "PUSH one register onto the stack"; m["rcall"] = "Relative CALL to subroutine"; m["ret"] = "RETurn from subroutine"; m["reti"] = "RETurn from Interrupt (and re-enable interrupts)"; m["rjmp"] = "Relative JuMP (within 2K words)"; m["rol"] = "ROtate register Left through carry"; m["ror"] = "ROtate register Right through carry"; m["sbc"] = "SuBtract register from register with Carry"; m["sbci"] = "SuBtract with Carry Immediate (0..255) from register"; m["sbi"] = "Set one Bit in an I/O register"; m["sbic"] = "Skip next instruction if Bit in I/O register is Clear"; m["sbis"] = "Skip next instruction if Bit in I/O register is Set"; m["sbiw"] = "SuBtract Immediate (0..255) from Word (register pair)"; // m["sbr"] = "Set Bits in Register"; // this is just ORI m["sbrc"] = "Skip next instruction if Bit in Register is Clear"; m["sbrs"] = "Skip next instruction if Bit in Register is Set"; m["sec"] = "SEt the Carry flag in SREG"; m["seh"] = "SEt the Half-carry flag in SREG"; m["sei"] = "SEt the Interrupt-enable flag in SREG"; m["sen"] = "SEt the Negative flag in SREG"; m["ser"] = "SEt all bits in Register (R16 to R31 only)"; m["ses"] = "SEt the Signed flag in SREG"; m["set"] = "SEt the T flag in SREG"; m["sev"] = "SEt the oVerflow flag in SREG"; m["sez"] = "SEt the Zero flag in SREG"; m["sleep"] = "sleep the processor to save power"; m["spm"] = "Store into Program Memory"; m["st"] = "STore"; // [TODO] better description m["std"] = "STore with Displacement"; // [TODO] better description m["sts"] = "STore direct to data Space"; m["sub"] = "SUBtract register from register without carry"; m["subi"] = "SUBract Immediate (subtract 0 to 255 from R16 to R31)"; m["swap"] = "SWAP the register's two nibbles"; m["tst"] = "TeST if the register is zero or negative"; m["wdr"] = "WatchDog Reset"; } int main(int argc, char* argv[]) { bool generateAllBlocks = false; int argNum = 1; if (argNum < argc) { string arg = argv[argNum++]; if (arg == "--full") generateAllBlocks = true; } FILE* everyWord = fopen("EveryAVRWord.S", "w"); for (int i = 0; i < 65536; ++i) fprintf(everyWord, ".word %d\n.word %d\n", i, i); fclose(everyWord); FILE* f = popen("avr-as EveryAVRWord.S; avr-objdump -zS a.out | cut -d: -f2", "r"); if (f == NULL) { perror("could not open avr-objdump output"); return 1; } CreateMnemonicDescriptionMap(); bzero(gBlocksToGenerate, 256 * sizeof(bool)); if (generateAllBlocks) { for (int block = 0x00; block <= 0xFF; ++block) gBlocksToGenerate[block] = true; } else { gBlocksToGenerate[0x00] = true; gBlocksToGenerate[0x03] = true; for (int block = 0x80; block <= 0x83; ++block) gBlocksToGenerate[block] = true; for (int block = 0x90; block <= 0x95; ++block) gBlocksToGenerate[block] = true; for (int block = 0xF0; block <= 0xFF; ++block) gBlocksToGenerate[block] = true; } for (int i = 0; i < 65536; ++i) { char line[256]; fgets(line, sizeof(line), f); char* nl = strchr(line, '\n'); if (nl != NULL) *nl = '\0'; // [1..5] = "xx xx" 2 byte hex (little endian) char hexStr[5]; sprintf(hexStr, "%04x", i); if (strlen(line) < 7 || line[1] != hexStr[2] || line[2] != hexStr[3] || line[4] != hexStr[0] || line[5] != hexStr[1]) { if (i > 0) { // instruction i-1 took 1 words instead of 2 singleWordInstructions.push_back(i - 1); } --i; continue; } char* startInstruction = strchr(line + 1, '\t') + 1; // doctor up the XMEGA instructions (which avr-objdump 2.17 doesn't know about) char temp[32]; if (startInstruction[0] == '.' /* .word ... */ && NewForXmega(i)) { if (i == 0x95F8) { startInstruction = "spm"; // "SPM_#2"; } else if ((i & 0xFF0F) == 0x940B) { snprintf(temp, sizeof(temp), "des\t%d", (i & 0xF0) >> 4); startInstruction = temp; } } // Trim off a " ; 0x" comment, except for "; undefined" if (strstr(startInstruction, "undefined") == NULL) { char* uselessComment = strstr(startInstruction, "\t; "); if (uselessComment != NULL) *uselessComment = 0; } string instructionString(startInstruction); char* blank = strchr(line, ' '); if (blank != NULL) *blank = '\0'; char* tab = strchr(startInstruction, '\t'); if (tab != NULL) *tab = '\0'; string mnemonic(startInstruction); if (mnemonic == ".word") { mnemonic = "[R]"; instructionString = mnemonic; ++gReserved; } instrs.push_back(instructionString); mnemonics.push_back(mnemonic); } pclose(f); // printf("size = %ld\n", instrs.size()); map counts; for (int i = 0; i < 65536; ++i) counts[mnemonics[i]]++; // printf("number of different instructions = %ld\n", counts.size()); for (int block = 0; block < 256; ++block) { map blockMap; for (int i = 0; i < 256; ++i) blockMap[mnemonics[block * 256 + i]]++; string thisBlock; const int numMnemonicsInBlock = blockMap.size(); bool differingCounts = false; for (map::iterator iter = blockMap.begin(); iter != blockMap.end(); ++iter) { if (iter->second != blockMap.begin()->second) { differingCounts = true; break; } } for (map::iterator iter = blockMap.begin(); iter != blockMap.end(); ++iter) { if (iter != blockMap.begin()) thisBlock += ", "; if (iter->second != 1 && numMnemonicsInBlock > 1 && differingCounts) { thisBlock += NumString(iter->second); thisBlock += " "; } thisBlock += iter->first; } const string fullDescription = thisBlock; if (numMnemonicsInBlock > 4) { thisBlock = "(MISC)"; } blocksOf256Summary.push_back(thisBlock); blocksOf256FullDescription.push_back(fullDescription); } printf("\n"); StTag html("HTML", "lang=\"en-US\"", kIndent_No); printf("\n"); { StTag head("HEAD", "", kIndent_Yes); { StTagSpan title("TITLE"); if (generateAllBlocks) printf("AVR instruction set - all opcodes"); else printf("AVR instruction set - selected opcode blocks"); } #if 0 printf(" \n"); #endif } printf("\n"); StTag body("BODY", "", kIndent_No); { StTagSpan summaryHeader("A", "id=\"Summary\""); } { StTagSpan summaryHeader("H1"); printf("Summary - Blocks of 256 instructions\n"); } { StTag table("TABLE", "border=1 frame=void", kIndent_Yes); { StTag tableHeader("TR", "", kIndent_Yes); for (int col = -1; col < 16; ++col) { if (col < 0) { StTagSpan data("TH", "width=\"6%\" align=\"center\" bgcolor=\"#333333\""); } else { StTagSpan data("TH", "width=\"6%\" align=\"center\" bgcolor=\"#cccccc\""); printf("x%Xzz", col); } } } for (int row = 0; row < 16; ++row) { StTag tableRow("TR", "", kIndent_Yes); { StTagSpan rowLabel("TH", "width=\"4%\" align=\"center\" bgcolor=\"#cccccc\""); printf("%Xxzz", row); } for (int col = 0; col < 16; ) { const int block = row * 16 + col; int lastCol = col + 1; while (lastCol < 16 && blocksOf256FullDescription[row * 16 + lastCol] == blocksOf256FullDescription[block]) ++lastCol; const int span = lastCol - col; string attributes = (span == 1) ? "" : ("colspan=" + NumString(span)); // if fullDescription != summary, do title="full" if (blocksOf256Summary[block] != blocksOf256FullDescription[block]) { attributes += " title=\"" + blocksOf256FullDescription[block] + "\""; } else { // describe the mnemonic(s) from gMnemonicDescriptionMap string singleMnemonic = blocksOf256Summary[block]; attributes += " title=\"" + gMnemonicDescriptionMap[singleMnemonic] + "\""; } if (!attributes.empty()) attributes += " "; attributes += "width=\"6%\" align=\"center\""; // Yellow background = block contains some 2-word instructions if (BlockContainsAnyTwoWordInstructions(block)) { attributes += " bgcolor=yellow"; } StTagSpan data("TD", attributes); StTagSpan link("A", "href=\"#Block" + NumString(block) + "\"", gBlocksToGenerate[block]); if (block == 0xF0 || block == 0xF4) printf("Conditional Branches"); else printf("%s", blocksOf256Summary[block].c_str()); col = lastCol; } } } // end of TABLE printf("

[R] = Reserved
\n"); // printf("Hover to see mnemonic descriptions
\n"); printf("Yellow boxes contain some two-word instructions

\n"); printf("

Number of reserved opcodes: %d (%.1f%%)

\n", gReserved, gReserved * 100.0 / 65536); // Generate all the desired tables of 256 individual opcode words for (int block = 0; block <= 0xFF; ++block) { if (gBlocksToGenerate[block]) DisplayBlock(block); } printf("
\n"); printf("

Generated from avr-objdump version '%s'

\n", VersionOfAVRObjdump().c_str()); return 0; } // // Generate a table for one block of 256 opcodes // void DisplayBlock(int block) { const int base = block * 256; int numUndefined = 0; int numTwoWord = 0; int numReserved = 0; int numXmega = 0; vector mnemonicsInThisBlock; { string name = "id=\"Block" + NumString(block) + "\""; StTagSpan anchor("A", name); } { StTagSpan header("H1", ""); printf("Opcodes %02Xxx (0x%02X00 - 0x%02XFF)\n", block, block, block); } { StTag table("TABLE", "border=1 frame=void", kIndent_Yes); { StTag tableHeader("TR", "", kIndent_Yes); for (int col = -1; col < 16; ++col) { if (col < 0) { StTagSpan data("TH", "width=\"6%\" align=\"center\" bgcolor=\"#333333\""); } else { StTagSpan data("TH", "width=\"6%\" align=\"center\" bgcolor=\"#cccccc\""); printf("%X", col); } } } for (int row = 0; row < 16; ++row) { StTag tableRow("TR", "", kIndent_Yes); { StTagSpan rowLabel("TH", "width=\"4%\" align=\"center\" bgcolor=\"#cccccc\""); printf("%02X%Xx", block, row); } for (int col = 0; col < 16; ) { const int instr = row * 16 + col; const int opcode = base + instr; int lastCol = col + 1; while (lastCol < 16 && instrs[base + row * 16 + lastCol] == instrs[opcode]) ++lastCol; const int span = lastCol - col; string attributes = (span == 1) ? "" : ("colspan=" + NumString(span)); if (TwoWordInstruction(opcode)) { attributes += " bgcolor=\"yellow\""; numTwoWord += span; } else if (NewForXmega(opcode)) { attributes += " bgcolor=\"green\""; numXmega += span; } else if (ReservedInstruction(opcode)) { attributes += " bgcolor=\"#888888\""; numReserved += span; } else if (strstr(instrs[opcode].c_str(), "undefined")) { attributes += " bgcolor=\"red\""; numUndefined += span; } // Describe the mnemonic { attributes += " title=\"" + gMnemonicDescriptionMap[mnemonics[opcode]] + "\""; } if (!attributes.empty()) attributes += " "; attributes += "width=\"6%\" align=\"center\""; StTagSpan data("TD", attributes); printf("%s", instrs[opcode].c_str()); if (find(mnemonicsInThisBlock.begin(), mnemonicsInThisBlock.end(), mnemonics[opcode]) == mnemonicsInThisBlock.end()) mnemonicsInThisBlock.push_back(mnemonics[opcode]); col = lastCol; } } } // end of TABLE if (numUndefined + numTwoWord + numReserved + numXmega > 0) { StTag indent("BLOCKQUOTE", "", kIndent_Yes); StTag paragraph("P", "", kIndent_Yes); if (numXmega > 0) printf("Green = opcode newly defined for ATxmega (%d)
\n", numXmega); if (numUndefined > 0) printf("Red = instruction has undefined behavior (%d)
\n", numUndefined); if (numTwoWord > 0) printf("Yellow = two-word instruction (%d)
\n", numTwoWord); if (numReserved > 0) printf("[R] = Reserved (%d)
\n", numReserved); } // Give definitions of all the mnenoics used in the table sort(mnemonicsInThisBlock.begin(), mnemonicsInThisBlock.end()); { StTag indent("BLOCKQUOTE", "", kIndent_Yes); { StTag t("TABLE", "", kIndent_Yes); for (vector::iterator iter = mnemonicsInThisBlock.begin(); iter != mnemonicsInThisBlock.end(); ++iter) { if (*iter == "[R]") // don't show [R] in the table (we already displayed the count, above) continue; StTag row("TR", "", kIndent_Yes); { StTagSpan td("TD"); StTagSpan italics("I"); printf("%s", iter->c_str()); } { StTagSpan td("TD"); printf(" - %s", gMnemonicDescriptionMap[*iter].c_str()); } } } } // '^ summary' link { StTagSpan link("A", "href=\"#Summary\""); printf("^ summary"); } }