Decoding assembly instructions in a Game Boy disassemblerBit-twiddling for a custom image formatReading from a serial portC++11 implementation of Huffman-encodingByte array to floating point such as FPE2 in JavaBinary search for random access iterators in C++Interpreter for an assembly language with variadic instructionsLegacy boot E820 entries written to low memorySmall Virtual Machine executing its own byte-codeHex Dump Utility in x86-64 Assembly: Version 1.1Main loop in assembly for a Brainfuck interpreter

Air travel with refrigerated insulin

Why is indicated airspeed rather than ground speed used during the takeoff roll?

Why can't I get pgrep output right to variable on bash script?

Should a narrator ever describe things based on a character's view instead of facts?

Offset in split text content

Why doesn't Gödel's incompleteness theorem apply to false statements?

How would a solely written language work mechanically

Magnifying glass in hyperbolic space

How do I lift the insulation blower into the attic?

Reasons for having MCU pin-states default to pull-up/down out of reset

Would this string work as string?

Strange behavior in TikZ draw command

Is divisi notation needed for brass or woodwind in an orchestra?

I keep switching characters, how do I stop?

Output visual diagram of picture

How to test the sharpness of a knife?

Can a Knock spell open the door to Mordenkainen's Magnificent Mansion?

How can I, as DM, avoid the Conga Line of Death occurring when implementing some form of flanking rule?

Do people actually use the word "kaputt" in conversation?

How to preserve electronics (computers, ipads, phones) for hundreds of years?

Trouble reading roman numeral notation with flats

Why didn’t Eve recognize the little cockroach as a living organism?

Why is "la Gestapo" feminine?

C++ lambda syntax



Decoding assembly instructions in a Game Boy disassembler


Bit-twiddling for a custom image formatReading from a serial portC++11 implementation of Huffman-encodingByte array to floating point such as FPE2 in JavaBinary search for random access iterators in C++Interpreter for an assembly language with variadic instructionsLegacy boot E820 entries written to low memorySmall Virtual Machine executing its own byte-codeHex Dump Utility in x86-64 Assembly: Version 1.1Main loop in assembly for a Brainfuck interpreter













11












$begingroup$


I am coding a game boy disassembler in C++. The program (simplified here) converts a vector of byte into an assembly instruction and print it.



To do so, it iterates through the vector of bytes (char) with an iterator. I noticed that with the it!=char_vect.end() condition, there is the risk that the iterator goes out from the vector. So I have to check that I do not get out of the vector at each iteration and if it occurs, I throw an error.



#include <iostream>
#include <vector>

int disassemble(std::vector<char>::iterator& it)

int opcode = *it;
int opbytes = 1; //number of bytes used by the operator

switch(opcode)
case 0x76 : std::cout<<"HALT "<<std::endl; opbytes = 1; break;
case 0x10 : std::cout<<"STOP $"<<(int)*(it+1)<<std::endl; opbytes =2; break;
//... About 250 different cases
default : std::cout<<"Instruction not implemented yet"<<std::endl;


return opbytes;


int main()

std::vector<char> char_vect = 0x76, 0x10, 0x20; // Fill vector of char
std::vector<char>::iterator it = char_vect.begin();

// First I only did that
//while (it!=char_vect.end())
// it+=disassemble(it);
//

// But I have to check that I do not go out of the char_vect

while (it!=char_vect.end())
try
it+=disassemble(it);
//check that it do not overpass end
if (size_t(it - char_vect.begin())>char_vect.size())
throw "Try to disassemble instruction outside of memory bounds";


catch(const char * c)
std::cerr << "Fatal error: " << c << std::endl;
return 1;


return 1;



This code works fine. Now I wonder, I have a possible exception case which is the iterator going further than vect_char.end(). Is a try / catch an appropriate way to handle this case knowing that:




  1. The reason for the iterator to go out of bounds could be either:



    • A non valid input byte sequence, for example 0x76, 0x20, 0x10 since the instruction 0x10 expects an argument. This should not append.

    • A mistake in the disassemble function. For exemple with the valid input 0x76, 0x10, 0x20, if I code by mistake that 0x10 uses 3 opbytes instead of 2, the iterator will go out of bounds. This should not append either.


  2. If I iterated through the vect_char with an index, I would not have this issue and the code would be more compact


I never really used try/catch before and I do not know if it is intented for such situations, so I would like to know, how would you have handled this unexpected case?










share|improve this question









New contributor




Mai Kar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







$endgroup$







  • 3




    $begingroup$
    This question might be more on-topic over at SoftwareEngineering.StackExchange.com . However, before posting please follow their tour and read "How do I ask a good question?", "What topics can I ask about here?" and "What types of questions should I avoid asking?".
    $endgroup$
    – BCdotWEB
    Mar 13 at 13:14










  • $begingroup$
    Does or doesn't the current code work as intended? I assume you've tested it?
    $endgroup$
    – Mast
    Mar 13 at 13:35






  • 1




    $begingroup$
    @Mast Yes it does work as intended, I just tested it.
    $endgroup$
    – Mai Kar
    Mar 13 at 13:38






  • 2




    $begingroup$
    Your error is simply passing insufficient Information to disassemble().
    $endgroup$
    – Deduplicator
    Mar 13 at 14:24






  • 3




    $begingroup$
    I reformulated my demand because I think it was not clear enough. This question is mainly about validating my design choices, i.e. the use of iterators and try/catch.
    $endgroup$
    – Mai Kar
    Mar 13 at 15:28















11












$begingroup$


I am coding a game boy disassembler in C++. The program (simplified here) converts a vector of byte into an assembly instruction and print it.



To do so, it iterates through the vector of bytes (char) with an iterator. I noticed that with the it!=char_vect.end() condition, there is the risk that the iterator goes out from the vector. So I have to check that I do not get out of the vector at each iteration and if it occurs, I throw an error.



#include <iostream>
#include <vector>

int disassemble(std::vector<char>::iterator& it)

int opcode = *it;
int opbytes = 1; //number of bytes used by the operator

switch(opcode)
case 0x76 : std::cout<<"HALT "<<std::endl; opbytes = 1; break;
case 0x10 : std::cout<<"STOP $"<<(int)*(it+1)<<std::endl; opbytes =2; break;
//... About 250 different cases
default : std::cout<<"Instruction not implemented yet"<<std::endl;


return opbytes;


int main()

std::vector<char> char_vect = 0x76, 0x10, 0x20; // Fill vector of char
std::vector<char>::iterator it = char_vect.begin();

// First I only did that
//while (it!=char_vect.end())
// it+=disassemble(it);
//

// But I have to check that I do not go out of the char_vect

while (it!=char_vect.end())
try
it+=disassemble(it);
//check that it do not overpass end
if (size_t(it - char_vect.begin())>char_vect.size())
throw "Try to disassemble instruction outside of memory bounds";


catch(const char * c)
std::cerr << "Fatal error: " << c << std::endl;
return 1;


return 1;



This code works fine. Now I wonder, I have a possible exception case which is the iterator going further than vect_char.end(). Is a try / catch an appropriate way to handle this case knowing that:




  1. The reason for the iterator to go out of bounds could be either:



    • A non valid input byte sequence, for example 0x76, 0x20, 0x10 since the instruction 0x10 expects an argument. This should not append.

    • A mistake in the disassemble function. For exemple with the valid input 0x76, 0x10, 0x20, if I code by mistake that 0x10 uses 3 opbytes instead of 2, the iterator will go out of bounds. This should not append either.


  2. If I iterated through the vect_char with an index, I would not have this issue and the code would be more compact


I never really used try/catch before and I do not know if it is intented for such situations, so I would like to know, how would you have handled this unexpected case?










share|improve this question









New contributor




Mai Kar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







$endgroup$







  • 3




    $begingroup$
    This question might be more on-topic over at SoftwareEngineering.StackExchange.com . However, before posting please follow their tour and read "How do I ask a good question?", "What topics can I ask about here?" and "What types of questions should I avoid asking?".
    $endgroup$
    – BCdotWEB
    Mar 13 at 13:14










  • $begingroup$
    Does or doesn't the current code work as intended? I assume you've tested it?
    $endgroup$
    – Mast
    Mar 13 at 13:35






  • 1




    $begingroup$
    @Mast Yes it does work as intended, I just tested it.
    $endgroup$
    – Mai Kar
    Mar 13 at 13:38






  • 2




    $begingroup$
    Your error is simply passing insufficient Information to disassemble().
    $endgroup$
    – Deduplicator
    Mar 13 at 14:24






  • 3




    $begingroup$
    I reformulated my demand because I think it was not clear enough. This question is mainly about validating my design choices, i.e. the use of iterators and try/catch.
    $endgroup$
    – Mai Kar
    Mar 13 at 15:28













11












11








11





$begingroup$


I am coding a game boy disassembler in C++. The program (simplified here) converts a vector of byte into an assembly instruction and print it.



To do so, it iterates through the vector of bytes (char) with an iterator. I noticed that with the it!=char_vect.end() condition, there is the risk that the iterator goes out from the vector. So I have to check that I do not get out of the vector at each iteration and if it occurs, I throw an error.



#include <iostream>
#include <vector>

int disassemble(std::vector<char>::iterator& it)

int opcode = *it;
int opbytes = 1; //number of bytes used by the operator

switch(opcode)
case 0x76 : std::cout<<"HALT "<<std::endl; opbytes = 1; break;
case 0x10 : std::cout<<"STOP $"<<(int)*(it+1)<<std::endl; opbytes =2; break;
//... About 250 different cases
default : std::cout<<"Instruction not implemented yet"<<std::endl;


return opbytes;


int main()

std::vector<char> char_vect = 0x76, 0x10, 0x20; // Fill vector of char
std::vector<char>::iterator it = char_vect.begin();

// First I only did that
//while (it!=char_vect.end())
// it+=disassemble(it);
//

// But I have to check that I do not go out of the char_vect

while (it!=char_vect.end())
try
it+=disassemble(it);
//check that it do not overpass end
if (size_t(it - char_vect.begin())>char_vect.size())
throw "Try to disassemble instruction outside of memory bounds";


catch(const char * c)
std::cerr << "Fatal error: " << c << std::endl;
return 1;


return 1;



This code works fine. Now I wonder, I have a possible exception case which is the iterator going further than vect_char.end(). Is a try / catch an appropriate way to handle this case knowing that:




  1. The reason for the iterator to go out of bounds could be either:



    • A non valid input byte sequence, for example 0x76, 0x20, 0x10 since the instruction 0x10 expects an argument. This should not append.

    • A mistake in the disassemble function. For exemple with the valid input 0x76, 0x10, 0x20, if I code by mistake that 0x10 uses 3 opbytes instead of 2, the iterator will go out of bounds. This should not append either.


  2. If I iterated through the vect_char with an index, I would not have this issue and the code would be more compact


I never really used try/catch before and I do not know if it is intented for such situations, so I would like to know, how would you have handled this unexpected case?










share|improve this question









New contributor




Mai Kar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







$endgroup$




I am coding a game boy disassembler in C++. The program (simplified here) converts a vector of byte into an assembly instruction and print it.



To do so, it iterates through the vector of bytes (char) with an iterator. I noticed that with the it!=char_vect.end() condition, there is the risk that the iterator goes out from the vector. So I have to check that I do not get out of the vector at each iteration and if it occurs, I throw an error.



#include <iostream>
#include <vector>

int disassemble(std::vector<char>::iterator& it)

int opcode = *it;
int opbytes = 1; //number of bytes used by the operator

switch(opcode)
case 0x76 : std::cout<<"HALT "<<std::endl; opbytes = 1; break;
case 0x10 : std::cout<<"STOP $"<<(int)*(it+1)<<std::endl; opbytes =2; break;
//... About 250 different cases
default : std::cout<<"Instruction not implemented yet"<<std::endl;


return opbytes;


int main()

std::vector<char> char_vect = 0x76, 0x10, 0x20; // Fill vector of char
std::vector<char>::iterator it = char_vect.begin();

// First I only did that
//while (it!=char_vect.end())
// it+=disassemble(it);
//

// But I have to check that I do not go out of the char_vect

while (it!=char_vect.end())
try
it+=disassemble(it);
//check that it do not overpass end
if (size_t(it - char_vect.begin())>char_vect.size())
throw "Try to disassemble instruction outside of memory bounds";


catch(const char * c)
std::cerr << "Fatal error: " << c << std::endl;
return 1;


return 1;



This code works fine. Now I wonder, I have a possible exception case which is the iterator going further than vect_char.end(). Is a try / catch an appropriate way to handle this case knowing that:




  1. The reason for the iterator to go out of bounds could be either:



    • A non valid input byte sequence, for example 0x76, 0x20, 0x10 since the instruction 0x10 expects an argument. This should not append.

    • A mistake in the disassemble function. For exemple with the valid input 0x76, 0x10, 0x20, if I code by mistake that 0x10 uses 3 opbytes instead of 2, the iterator will go out of bounds. This should not append either.


  2. If I iterated through the vect_char with an index, I would not have this issue and the code would be more compact


I never really used try/catch before and I do not know if it is intented for such situations, so I would like to know, how would you have handled this unexpected case?







c++ error-handling iterator assembly serialization






share|improve this question









New contributor




Mai Kar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.











share|improve this question









New contributor




Mai Kar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









share|improve this question




share|improve this question








edited Mar 13 at 15:45







Mai Kar













New contributor




Mai Kar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.









asked Mar 13 at 13:12









Mai KarMai Kar

617




617




New contributor




Mai Kar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.





New contributor





Mai Kar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.






Mai Kar is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.







  • 3




    $begingroup$
    This question might be more on-topic over at SoftwareEngineering.StackExchange.com . However, before posting please follow their tour and read "How do I ask a good question?", "What topics can I ask about here?" and "What types of questions should I avoid asking?".
    $endgroup$
    – BCdotWEB
    Mar 13 at 13:14










  • $begingroup$
    Does or doesn't the current code work as intended? I assume you've tested it?
    $endgroup$
    – Mast
    Mar 13 at 13:35






  • 1




    $begingroup$
    @Mast Yes it does work as intended, I just tested it.
    $endgroup$
    – Mai Kar
    Mar 13 at 13:38






  • 2




    $begingroup$
    Your error is simply passing insufficient Information to disassemble().
    $endgroup$
    – Deduplicator
    Mar 13 at 14:24






  • 3




    $begingroup$
    I reformulated my demand because I think it was not clear enough. This question is mainly about validating my design choices, i.e. the use of iterators and try/catch.
    $endgroup$
    – Mai Kar
    Mar 13 at 15:28












  • 3




    $begingroup$
    This question might be more on-topic over at SoftwareEngineering.StackExchange.com . However, before posting please follow their tour and read "How do I ask a good question?", "What topics can I ask about here?" and "What types of questions should I avoid asking?".
    $endgroup$
    – BCdotWEB
    Mar 13 at 13:14










  • $begingroup$
    Does or doesn't the current code work as intended? I assume you've tested it?
    $endgroup$
    – Mast
    Mar 13 at 13:35






  • 1




    $begingroup$
    @Mast Yes it does work as intended, I just tested it.
    $endgroup$
    – Mai Kar
    Mar 13 at 13:38






  • 2




    $begingroup$
    Your error is simply passing insufficient Information to disassemble().
    $endgroup$
    – Deduplicator
    Mar 13 at 14:24






  • 3




    $begingroup$
    I reformulated my demand because I think it was not clear enough. This question is mainly about validating my design choices, i.e. the use of iterators and try/catch.
    $endgroup$
    – Mai Kar
    Mar 13 at 15:28







3




3




$begingroup$
This question might be more on-topic over at SoftwareEngineering.StackExchange.com . However, before posting please follow their tour and read "How do I ask a good question?", "What topics can I ask about here?" and "What types of questions should I avoid asking?".
$endgroup$
– BCdotWEB
Mar 13 at 13:14




$begingroup$
This question might be more on-topic over at SoftwareEngineering.StackExchange.com . However, before posting please follow their tour and read "How do I ask a good question?", "What topics can I ask about here?" and "What types of questions should I avoid asking?".
$endgroup$
– BCdotWEB
Mar 13 at 13:14












$begingroup$
Does or doesn't the current code work as intended? I assume you've tested it?
$endgroup$
– Mast
Mar 13 at 13:35




$begingroup$
Does or doesn't the current code work as intended? I assume you've tested it?
$endgroup$
– Mast
Mar 13 at 13:35




1




1




$begingroup$
@Mast Yes it does work as intended, I just tested it.
$endgroup$
– Mai Kar
Mar 13 at 13:38




$begingroup$
@Mast Yes it does work as intended, I just tested it.
$endgroup$
– Mai Kar
Mar 13 at 13:38




2




2




$begingroup$
Your error is simply passing insufficient Information to disassemble().
$endgroup$
– Deduplicator
Mar 13 at 14:24




$begingroup$
Your error is simply passing insufficient Information to disassemble().
$endgroup$
– Deduplicator
Mar 13 at 14:24




3




3




$begingroup$
I reformulated my demand because I think it was not clear enough. This question is mainly about validating my design choices, i.e. the use of iterators and try/catch.
$endgroup$
– Mai Kar
Mar 13 at 15:28




$begingroup$
I reformulated my demand because I think it was not clear enough. This question is mainly about validating my design choices, i.e. the use of iterators and try/catch.
$endgroup$
– Mai Kar
Mar 13 at 15:28










3 Answers
3






active

oldest

votes


















12












$begingroup$

I have some ideas about how you might be able to improve your program.



Avoid problems



Rather than trying to deal with the problem for every instruction, one approach is avoiding it entirely. One way to do that is to simply append a number of bytes to the end of the vector. If the maximum bytes for an instruction is $n$, then append $n-1$ bytes to the end and stop when you've advanced into the padded area.



Check before advancing



One could also pass the number of remaining bytes to the disassemble function. However, the mechanism I'd suggest would be to pass a range, e.g.:



int diassembleOne(std::vector<char>::iterator& it, std::vector<char>::iterator& end) 
// figure out number of bytes for this opcode
if (std::distance(it, end) > opbytes)
it = end;
// throw or return 0

// disassemble the instruction thoroughly
std::advance(it, opbytes);
return opbytes;



Use const iterators



If all the code is doing is disassembling, then it shouldn't alter the underlying vector. For that reason, I'd recommend passing a std::vector<char>::const_iterator &.



Use classes



I'd suggest using an Opcode class like this:



class Opcode 
char code;
short int bytes;
std::string_view name;
bool operator==(char opcode) const return code == opcode;
int decode(std::vector<char>::const_iterator& it, std::ostream& out=std::cout) const
out << name;
++it;
for (int ibytes-1; i; --i)
out << static_cast<unsigned>(*it++);

out << 'n';
return bytes;

;

constexpr std::array<Opcode,2> instructions
0x10, 2, "STOP $" ,
0x76, 2, "HALT " ,
;


Pass a pair of iterators to the dissemble function



As mentioned before, you can pass a pair of iterators to the disassemble function. Using that plus the class above:



int disassembleOne(std::vector<char>::const_iterator& it, std::vector<char>::const_iterator& end)
auto opstd::find(instructions.begin(), instructions.end(), *it);
if (op == instructions.end())
std::cout << "Instruction not foundn";
it = end;
return 0; // instruction not found or off the end of the passed array

if (std::distance(it, end) < op->bytes)
std::cout << "Not enough bytes left to decode " << op->name << 'n';
it = end;
return 0; // instruction not found or off the end of the passed array

return op->decode(it);



Now main becomes very simple:



int main()
const std::vector<char> char_vect = 0x76, 0x10, 0x20, 0x10; // Fill vector of char
auto endchar_vect.cend();

for (auto itchar_vect.cbegin(); it != end; disassembleOne(it, end))




Another way to do this would be to put more of the processing in the Opcode itself -- it would make sense that each opcode would know how to decode itself.



Be clear about caller expectations



This code, as with the original code, expects that the it being passed is a valid iterator that is not the end. It is good to document that in comments in the code.






share|improve this answer











$endgroup$












  • $begingroup$
    The loop in main doesn't use the result of the call to disassembleOne.
    $endgroup$
    – Roland Illig
    Mar 13 at 19:17










  • $begingroup$
    @RolandIllig It doesn't need to use the return value because the iterator, which is passed by reference, is updated within the function.
    $endgroup$
    – Edward
    Mar 13 at 19:32


















6












$begingroup$

I think the big switch is a problem. Consider a more data-driven approach where each opcode is described by a struct:



struct OpCode

unsigned char command_byte;
unsigned char mask; // if only a subset of bits determine command
unsigned char length;
// other members as needed - e.g. a pointer to the "print" function
;


Now, the code that reads instructions can determine whether the command is unterminated, without needing to repeat the logic for every multi-byte opcode.



I've included the mask so that we don't encode every single instruction (e.g. ld R1, R2 encodes the source and destination registers in the bits of a single-byte command; it would be tedious and error-prone to write them all separately here).



The simple length value I've shown isn't quite enough, given that the Game Boy's LR35902 processor supports the 0xCB prefix for extended Z80 instructions - we might want to handle that outside of the normal flow, and use it to switch between different instruction tables.

We get away with a simple length value here, because the only prefix instruction supported by the Game Boy's LR35902 processor is 0xCB, which is always followed by a single-byte instruction. If we were decoding Z80 instructions (with ED prefix), then we'd need something a little more sophisticated.






share|improve this answer









$endgroup$




















    3












    $begingroup$

    Your code's division into functions isn't very natural. The objective is to have each function performing one specific task, which should also be as orthogonal as possible to the tasks performed by the other functions. For instance, your disassemble function performs three different functions: it reads from the instruction stream, it interprets assembly code and returns the number of bytes that should be skipped to get to the next instruction. That's a not-so-coherent mix of responsibilities.



    There's also a bug in your code, because it+=disassemble(it); could point beyond char_vect.end() which is in itself undefined behavior, even if you don't dereference the iterator.



    I would build my disassembler around an iterator, or better even a range (if you don't mind external libraries or anticipating the next standard):



    #include <algorithm>
    #include <vector>
    #include <iostream>
    #include <range/v3/view.hpp>

    using iterator = std::vector<char>::const_iterator;
    struct truncated_instruction ;

    // one function to interpret the instruction
    void interpret(char instruction)
    if (instruction == 'x' throw bad_instruction();
    std::cout << (int) instruction << ' ';


    // one function to get the number of bytes to skip
    int nb_bytes(char c) return 1;

    class disassembled
    : public ranges::view_facade<disassembled>

    friend ranges::range_access;
    iterator first, last;
    char const & read() const return *first;
    bool equal(ranges::default_sentinel) const return first == last;
    void next()
    // one function to get to the next instruction
    auto bytes_to_skip = nb_bytes(*first);
    if (std::distance(first, last) < bytes_to_skip) throw truncated_instruction();
    // check if there's enough space left to advance before advancing
    std::advance(first, bytes_to_skip);

    public:
    disassembled() = default;
    explicit disassembled(const std::vector<char>& v) : first(v.begin()), last(v.end())
    ;

    int main()
    std::vector<char> char_vect = 0x76, 0x10, 0x20, 0x30;
    try
    for (auto instruction : disassembled(char_vect)) interpret(instruction);

    catch // ...






    share|improve this answer









    $endgroup$












      Your Answer





      StackExchange.ifUsing("editor", function ()
      return StackExchange.using("mathjaxEditing", function ()
      StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
      StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
      );
      );
      , "mathjax-editing");

      StackExchange.ifUsing("editor", function ()
      StackExchange.using("externalEditor", function ()
      StackExchange.using("snippets", function ()
      StackExchange.snippets.init();
      );
      );
      , "code-snippets");

      StackExchange.ready(function()
      var channelOptions =
      tags: "".split(" "),
      id: "196"
      ;
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function()
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled)
      StackExchange.using("snippets", function()
      createEditor();
      );

      else
      createEditor();

      );

      function createEditor()
      StackExchange.prepareEditor(
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: false,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      imageUploader:
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      ,
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      );



      );






      Mai Kar is a new contributor. Be nice, and check out our Code of Conduct.









      draft saved

      draft discarded


















      StackExchange.ready(
      function ()
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215349%2fdecoding-assembly-instructions-in-a-game-boy-disassembler%23new-answer', 'question_page');

      );

      Post as a guest















      Required, but never shown

























      3 Answers
      3






      active

      oldest

      votes








      3 Answers
      3






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      12












      $begingroup$

      I have some ideas about how you might be able to improve your program.



      Avoid problems



      Rather than trying to deal with the problem for every instruction, one approach is avoiding it entirely. One way to do that is to simply append a number of bytes to the end of the vector. If the maximum bytes for an instruction is $n$, then append $n-1$ bytes to the end and stop when you've advanced into the padded area.



      Check before advancing



      One could also pass the number of remaining bytes to the disassemble function. However, the mechanism I'd suggest would be to pass a range, e.g.:



      int diassembleOne(std::vector<char>::iterator& it, std::vector<char>::iterator& end) 
      // figure out number of bytes for this opcode
      if (std::distance(it, end) > opbytes)
      it = end;
      // throw or return 0

      // disassemble the instruction thoroughly
      std::advance(it, opbytes);
      return opbytes;



      Use const iterators



      If all the code is doing is disassembling, then it shouldn't alter the underlying vector. For that reason, I'd recommend passing a std::vector<char>::const_iterator &.



      Use classes



      I'd suggest using an Opcode class like this:



      class Opcode 
      char code;
      short int bytes;
      std::string_view name;
      bool operator==(char opcode) const return code == opcode;
      int decode(std::vector<char>::const_iterator& it, std::ostream& out=std::cout) const
      out << name;
      ++it;
      for (int ibytes-1; i; --i)
      out << static_cast<unsigned>(*it++);

      out << 'n';
      return bytes;

      ;

      constexpr std::array<Opcode,2> instructions
      0x10, 2, "STOP $" ,
      0x76, 2, "HALT " ,
      ;


      Pass a pair of iterators to the dissemble function



      As mentioned before, you can pass a pair of iterators to the disassemble function. Using that plus the class above:



      int disassembleOne(std::vector<char>::const_iterator& it, std::vector<char>::const_iterator& end)
      auto opstd::find(instructions.begin(), instructions.end(), *it);
      if (op == instructions.end())
      std::cout << "Instruction not foundn";
      it = end;
      return 0; // instruction not found or off the end of the passed array

      if (std::distance(it, end) < op->bytes)
      std::cout << "Not enough bytes left to decode " << op->name << 'n';
      it = end;
      return 0; // instruction not found or off the end of the passed array

      return op->decode(it);



      Now main becomes very simple:



      int main()
      const std::vector<char> char_vect = 0x76, 0x10, 0x20, 0x10; // Fill vector of char
      auto endchar_vect.cend();

      for (auto itchar_vect.cbegin(); it != end; disassembleOne(it, end))




      Another way to do this would be to put more of the processing in the Opcode itself -- it would make sense that each opcode would know how to decode itself.



      Be clear about caller expectations



      This code, as with the original code, expects that the it being passed is a valid iterator that is not the end. It is good to document that in comments in the code.






      share|improve this answer











      $endgroup$












      • $begingroup$
        The loop in main doesn't use the result of the call to disassembleOne.
        $endgroup$
        – Roland Illig
        Mar 13 at 19:17










      • $begingroup$
        @RolandIllig It doesn't need to use the return value because the iterator, which is passed by reference, is updated within the function.
        $endgroup$
        – Edward
        Mar 13 at 19:32















      12












      $begingroup$

      I have some ideas about how you might be able to improve your program.



      Avoid problems



      Rather than trying to deal with the problem for every instruction, one approach is avoiding it entirely. One way to do that is to simply append a number of bytes to the end of the vector. If the maximum bytes for an instruction is $n$, then append $n-1$ bytes to the end and stop when you've advanced into the padded area.



      Check before advancing



      One could also pass the number of remaining bytes to the disassemble function. However, the mechanism I'd suggest would be to pass a range, e.g.:



      int diassembleOne(std::vector<char>::iterator& it, std::vector<char>::iterator& end) 
      // figure out number of bytes for this opcode
      if (std::distance(it, end) > opbytes)
      it = end;
      // throw or return 0

      // disassemble the instruction thoroughly
      std::advance(it, opbytes);
      return opbytes;



      Use const iterators



      If all the code is doing is disassembling, then it shouldn't alter the underlying vector. For that reason, I'd recommend passing a std::vector<char>::const_iterator &.



      Use classes



      I'd suggest using an Opcode class like this:



      class Opcode 
      char code;
      short int bytes;
      std::string_view name;
      bool operator==(char opcode) const return code == opcode;
      int decode(std::vector<char>::const_iterator& it, std::ostream& out=std::cout) const
      out << name;
      ++it;
      for (int ibytes-1; i; --i)
      out << static_cast<unsigned>(*it++);

      out << 'n';
      return bytes;

      ;

      constexpr std::array<Opcode,2> instructions
      0x10, 2, "STOP $" ,
      0x76, 2, "HALT " ,
      ;


      Pass a pair of iterators to the dissemble function



      As mentioned before, you can pass a pair of iterators to the disassemble function. Using that plus the class above:



      int disassembleOne(std::vector<char>::const_iterator& it, std::vector<char>::const_iterator& end)
      auto opstd::find(instructions.begin(), instructions.end(), *it);
      if (op == instructions.end())
      std::cout << "Instruction not foundn";
      it = end;
      return 0; // instruction not found or off the end of the passed array

      if (std::distance(it, end) < op->bytes)
      std::cout << "Not enough bytes left to decode " << op->name << 'n';
      it = end;
      return 0; // instruction not found or off the end of the passed array

      return op->decode(it);



      Now main becomes very simple:



      int main()
      const std::vector<char> char_vect = 0x76, 0x10, 0x20, 0x10; // Fill vector of char
      auto endchar_vect.cend();

      for (auto itchar_vect.cbegin(); it != end; disassembleOne(it, end))




      Another way to do this would be to put more of the processing in the Opcode itself -- it would make sense that each opcode would know how to decode itself.



      Be clear about caller expectations



      This code, as with the original code, expects that the it being passed is a valid iterator that is not the end. It is good to document that in comments in the code.






      share|improve this answer











      $endgroup$












      • $begingroup$
        The loop in main doesn't use the result of the call to disassembleOne.
        $endgroup$
        – Roland Illig
        Mar 13 at 19:17










      • $begingroup$
        @RolandIllig It doesn't need to use the return value because the iterator, which is passed by reference, is updated within the function.
        $endgroup$
        – Edward
        Mar 13 at 19:32













      12












      12








      12





      $begingroup$

      I have some ideas about how you might be able to improve your program.



      Avoid problems



      Rather than trying to deal with the problem for every instruction, one approach is avoiding it entirely. One way to do that is to simply append a number of bytes to the end of the vector. If the maximum bytes for an instruction is $n$, then append $n-1$ bytes to the end and stop when you've advanced into the padded area.



      Check before advancing



      One could also pass the number of remaining bytes to the disassemble function. However, the mechanism I'd suggest would be to pass a range, e.g.:



      int diassembleOne(std::vector<char>::iterator& it, std::vector<char>::iterator& end) 
      // figure out number of bytes for this opcode
      if (std::distance(it, end) > opbytes)
      it = end;
      // throw or return 0

      // disassemble the instruction thoroughly
      std::advance(it, opbytes);
      return opbytes;



      Use const iterators



      If all the code is doing is disassembling, then it shouldn't alter the underlying vector. For that reason, I'd recommend passing a std::vector<char>::const_iterator &.



      Use classes



      I'd suggest using an Opcode class like this:



      class Opcode 
      char code;
      short int bytes;
      std::string_view name;
      bool operator==(char opcode) const return code == opcode;
      int decode(std::vector<char>::const_iterator& it, std::ostream& out=std::cout) const
      out << name;
      ++it;
      for (int ibytes-1; i; --i)
      out << static_cast<unsigned>(*it++);

      out << 'n';
      return bytes;

      ;

      constexpr std::array<Opcode,2> instructions
      0x10, 2, "STOP $" ,
      0x76, 2, "HALT " ,
      ;


      Pass a pair of iterators to the dissemble function



      As mentioned before, you can pass a pair of iterators to the disassemble function. Using that plus the class above:



      int disassembleOne(std::vector<char>::const_iterator& it, std::vector<char>::const_iterator& end)
      auto opstd::find(instructions.begin(), instructions.end(), *it);
      if (op == instructions.end())
      std::cout << "Instruction not foundn";
      it = end;
      return 0; // instruction not found or off the end of the passed array

      if (std::distance(it, end) < op->bytes)
      std::cout << "Not enough bytes left to decode " << op->name << 'n';
      it = end;
      return 0; // instruction not found or off the end of the passed array

      return op->decode(it);



      Now main becomes very simple:



      int main()
      const std::vector<char> char_vect = 0x76, 0x10, 0x20, 0x10; // Fill vector of char
      auto endchar_vect.cend();

      for (auto itchar_vect.cbegin(); it != end; disassembleOne(it, end))




      Another way to do this would be to put more of the processing in the Opcode itself -- it would make sense that each opcode would know how to decode itself.



      Be clear about caller expectations



      This code, as with the original code, expects that the it being passed is a valid iterator that is not the end. It is good to document that in comments in the code.






      share|improve this answer











      $endgroup$



      I have some ideas about how you might be able to improve your program.



      Avoid problems



      Rather than trying to deal with the problem for every instruction, one approach is avoiding it entirely. One way to do that is to simply append a number of bytes to the end of the vector. If the maximum bytes for an instruction is $n$, then append $n-1$ bytes to the end and stop when you've advanced into the padded area.



      Check before advancing



      One could also pass the number of remaining bytes to the disassemble function. However, the mechanism I'd suggest would be to pass a range, e.g.:



      int diassembleOne(std::vector<char>::iterator& it, std::vector<char>::iterator& end) 
      // figure out number of bytes for this opcode
      if (std::distance(it, end) > opbytes)
      it = end;
      // throw or return 0

      // disassemble the instruction thoroughly
      std::advance(it, opbytes);
      return opbytes;



      Use const iterators



      If all the code is doing is disassembling, then it shouldn't alter the underlying vector. For that reason, I'd recommend passing a std::vector<char>::const_iterator &.



      Use classes



      I'd suggest using an Opcode class like this:



      class Opcode 
      char code;
      short int bytes;
      std::string_view name;
      bool operator==(char opcode) const return code == opcode;
      int decode(std::vector<char>::const_iterator& it, std::ostream& out=std::cout) const
      out << name;
      ++it;
      for (int ibytes-1; i; --i)
      out << static_cast<unsigned>(*it++);

      out << 'n';
      return bytes;

      ;

      constexpr std::array<Opcode,2> instructions
      0x10, 2, "STOP $" ,
      0x76, 2, "HALT " ,
      ;


      Pass a pair of iterators to the dissemble function



      As mentioned before, you can pass a pair of iterators to the disassemble function. Using that plus the class above:



      int disassembleOne(std::vector<char>::const_iterator& it, std::vector<char>::const_iterator& end)
      auto opstd::find(instructions.begin(), instructions.end(), *it);
      if (op == instructions.end())
      std::cout << "Instruction not foundn";
      it = end;
      return 0; // instruction not found or off the end of the passed array

      if (std::distance(it, end) < op->bytes)
      std::cout << "Not enough bytes left to decode " << op->name << 'n';
      it = end;
      return 0; // instruction not found or off the end of the passed array

      return op->decode(it);



      Now main becomes very simple:



      int main()
      const std::vector<char> char_vect = 0x76, 0x10, 0x20, 0x10; // Fill vector of char
      auto endchar_vect.cend();

      for (auto itchar_vect.cbegin(); it != end; disassembleOne(it, end))




      Another way to do this would be to put more of the processing in the Opcode itself -- it would make sense that each opcode would know how to decode itself.



      Be clear about caller expectations



      This code, as with the original code, expects that the it being passed is a valid iterator that is not the end. It is good to document that in comments in the code.







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited Mar 13 at 16:22

























      answered Mar 13 at 16:12









      EdwardEdward

      47.4k378213




      47.4k378213











      • $begingroup$
        The loop in main doesn't use the result of the call to disassembleOne.
        $endgroup$
        – Roland Illig
        Mar 13 at 19:17










      • $begingroup$
        @RolandIllig It doesn't need to use the return value because the iterator, which is passed by reference, is updated within the function.
        $endgroup$
        – Edward
        Mar 13 at 19:32
















      • $begingroup$
        The loop in main doesn't use the result of the call to disassembleOne.
        $endgroup$
        – Roland Illig
        Mar 13 at 19:17










      • $begingroup$
        @RolandIllig It doesn't need to use the return value because the iterator, which is passed by reference, is updated within the function.
        $endgroup$
        – Edward
        Mar 13 at 19:32















      $begingroup$
      The loop in main doesn't use the result of the call to disassembleOne.
      $endgroup$
      – Roland Illig
      Mar 13 at 19:17




      $begingroup$
      The loop in main doesn't use the result of the call to disassembleOne.
      $endgroup$
      – Roland Illig
      Mar 13 at 19:17












      $begingroup$
      @RolandIllig It doesn't need to use the return value because the iterator, which is passed by reference, is updated within the function.
      $endgroup$
      – Edward
      Mar 13 at 19:32




      $begingroup$
      @RolandIllig It doesn't need to use the return value because the iterator, which is passed by reference, is updated within the function.
      $endgroup$
      – Edward
      Mar 13 at 19:32













      6












      $begingroup$

      I think the big switch is a problem. Consider a more data-driven approach where each opcode is described by a struct:



      struct OpCode

      unsigned char command_byte;
      unsigned char mask; // if only a subset of bits determine command
      unsigned char length;
      // other members as needed - e.g. a pointer to the "print" function
      ;


      Now, the code that reads instructions can determine whether the command is unterminated, without needing to repeat the logic for every multi-byte opcode.



      I've included the mask so that we don't encode every single instruction (e.g. ld R1, R2 encodes the source and destination registers in the bits of a single-byte command; it would be tedious and error-prone to write them all separately here).



      The simple length value I've shown isn't quite enough, given that the Game Boy's LR35902 processor supports the 0xCB prefix for extended Z80 instructions - we might want to handle that outside of the normal flow, and use it to switch between different instruction tables.

      We get away with a simple length value here, because the only prefix instruction supported by the Game Boy's LR35902 processor is 0xCB, which is always followed by a single-byte instruction. If we were decoding Z80 instructions (with ED prefix), then we'd need something a little more sophisticated.






      share|improve this answer









      $endgroup$

















        6












        $begingroup$

        I think the big switch is a problem. Consider a more data-driven approach where each opcode is described by a struct:



        struct OpCode

        unsigned char command_byte;
        unsigned char mask; // if only a subset of bits determine command
        unsigned char length;
        // other members as needed - e.g. a pointer to the "print" function
        ;


        Now, the code that reads instructions can determine whether the command is unterminated, without needing to repeat the logic for every multi-byte opcode.



        I've included the mask so that we don't encode every single instruction (e.g. ld R1, R2 encodes the source and destination registers in the bits of a single-byte command; it would be tedious and error-prone to write them all separately here).



        The simple length value I've shown isn't quite enough, given that the Game Boy's LR35902 processor supports the 0xCB prefix for extended Z80 instructions - we might want to handle that outside of the normal flow, and use it to switch between different instruction tables.

        We get away with a simple length value here, because the only prefix instruction supported by the Game Boy's LR35902 processor is 0xCB, which is always followed by a single-byte instruction. If we were decoding Z80 instructions (with ED prefix), then we'd need something a little more sophisticated.






        share|improve this answer









        $endgroup$















          6












          6








          6





          $begingroup$

          I think the big switch is a problem. Consider a more data-driven approach where each opcode is described by a struct:



          struct OpCode

          unsigned char command_byte;
          unsigned char mask; // if only a subset of bits determine command
          unsigned char length;
          // other members as needed - e.g. a pointer to the "print" function
          ;


          Now, the code that reads instructions can determine whether the command is unterminated, without needing to repeat the logic for every multi-byte opcode.



          I've included the mask so that we don't encode every single instruction (e.g. ld R1, R2 encodes the source and destination registers in the bits of a single-byte command; it would be tedious and error-prone to write them all separately here).



          The simple length value I've shown isn't quite enough, given that the Game Boy's LR35902 processor supports the 0xCB prefix for extended Z80 instructions - we might want to handle that outside of the normal flow, and use it to switch between different instruction tables.

          We get away with a simple length value here, because the only prefix instruction supported by the Game Boy's LR35902 processor is 0xCB, which is always followed by a single-byte instruction. If we were decoding Z80 instructions (with ED prefix), then we'd need something a little more sophisticated.






          share|improve this answer









          $endgroup$



          I think the big switch is a problem. Consider a more data-driven approach where each opcode is described by a struct:



          struct OpCode

          unsigned char command_byte;
          unsigned char mask; // if only a subset of bits determine command
          unsigned char length;
          // other members as needed - e.g. a pointer to the "print" function
          ;


          Now, the code that reads instructions can determine whether the command is unterminated, without needing to repeat the logic for every multi-byte opcode.



          I've included the mask so that we don't encode every single instruction (e.g. ld R1, R2 encodes the source and destination registers in the bits of a single-byte command; it would be tedious and error-prone to write them all separately here).



          The simple length value I've shown isn't quite enough, given that the Game Boy's LR35902 processor supports the 0xCB prefix for extended Z80 instructions - we might want to handle that outside of the normal flow, and use it to switch between different instruction tables.

          We get away with a simple length value here, because the only prefix instruction supported by the Game Boy's LR35902 processor is 0xCB, which is always followed by a single-byte instruction. If we were decoding Z80 instructions (with ED prefix), then we'd need something a little more sophisticated.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Mar 13 at 16:29









          Toby SpeightToby Speight

          26.5k742118




          26.5k742118





















              3












              $begingroup$

              Your code's division into functions isn't very natural. The objective is to have each function performing one specific task, which should also be as orthogonal as possible to the tasks performed by the other functions. For instance, your disassemble function performs three different functions: it reads from the instruction stream, it interprets assembly code and returns the number of bytes that should be skipped to get to the next instruction. That's a not-so-coherent mix of responsibilities.



              There's also a bug in your code, because it+=disassemble(it); could point beyond char_vect.end() which is in itself undefined behavior, even if you don't dereference the iterator.



              I would build my disassembler around an iterator, or better even a range (if you don't mind external libraries or anticipating the next standard):



              #include <algorithm>
              #include <vector>
              #include <iostream>
              #include <range/v3/view.hpp>

              using iterator = std::vector<char>::const_iterator;
              struct truncated_instruction ;

              // one function to interpret the instruction
              void interpret(char instruction)
              if (instruction == 'x' throw bad_instruction();
              std::cout << (int) instruction << ' ';


              // one function to get the number of bytes to skip
              int nb_bytes(char c) return 1;

              class disassembled
              : public ranges::view_facade<disassembled>

              friend ranges::range_access;
              iterator first, last;
              char const & read() const return *first;
              bool equal(ranges::default_sentinel) const return first == last;
              void next()
              // one function to get to the next instruction
              auto bytes_to_skip = nb_bytes(*first);
              if (std::distance(first, last) < bytes_to_skip) throw truncated_instruction();
              // check if there's enough space left to advance before advancing
              std::advance(first, bytes_to_skip);

              public:
              disassembled() = default;
              explicit disassembled(const std::vector<char>& v) : first(v.begin()), last(v.end())
              ;

              int main()
              std::vector<char> char_vect = 0x76, 0x10, 0x20, 0x30;
              try
              for (auto instruction : disassembled(char_vect)) interpret(instruction);

              catch // ...






              share|improve this answer









              $endgroup$

















                3












                $begingroup$

                Your code's division into functions isn't very natural. The objective is to have each function performing one specific task, which should also be as orthogonal as possible to the tasks performed by the other functions. For instance, your disassemble function performs three different functions: it reads from the instruction stream, it interprets assembly code and returns the number of bytes that should be skipped to get to the next instruction. That's a not-so-coherent mix of responsibilities.



                There's also a bug in your code, because it+=disassemble(it); could point beyond char_vect.end() which is in itself undefined behavior, even if you don't dereference the iterator.



                I would build my disassembler around an iterator, or better even a range (if you don't mind external libraries or anticipating the next standard):



                #include <algorithm>
                #include <vector>
                #include <iostream>
                #include <range/v3/view.hpp>

                using iterator = std::vector<char>::const_iterator;
                struct truncated_instruction ;

                // one function to interpret the instruction
                void interpret(char instruction)
                if (instruction == 'x' throw bad_instruction();
                std::cout << (int) instruction << ' ';


                // one function to get the number of bytes to skip
                int nb_bytes(char c) return 1;

                class disassembled
                : public ranges::view_facade<disassembled>

                friend ranges::range_access;
                iterator first, last;
                char const & read() const return *first;
                bool equal(ranges::default_sentinel) const return first == last;
                void next()
                // one function to get to the next instruction
                auto bytes_to_skip = nb_bytes(*first);
                if (std::distance(first, last) < bytes_to_skip) throw truncated_instruction();
                // check if there's enough space left to advance before advancing
                std::advance(first, bytes_to_skip);

                public:
                disassembled() = default;
                explicit disassembled(const std::vector<char>& v) : first(v.begin()), last(v.end())
                ;

                int main()
                std::vector<char> char_vect = 0x76, 0x10, 0x20, 0x30;
                try
                for (auto instruction : disassembled(char_vect)) interpret(instruction);

                catch // ...






                share|improve this answer









                $endgroup$















                  3












                  3








                  3





                  $begingroup$

                  Your code's division into functions isn't very natural. The objective is to have each function performing one specific task, which should also be as orthogonal as possible to the tasks performed by the other functions. For instance, your disassemble function performs three different functions: it reads from the instruction stream, it interprets assembly code and returns the number of bytes that should be skipped to get to the next instruction. That's a not-so-coherent mix of responsibilities.



                  There's also a bug in your code, because it+=disassemble(it); could point beyond char_vect.end() which is in itself undefined behavior, even if you don't dereference the iterator.



                  I would build my disassembler around an iterator, or better even a range (if you don't mind external libraries or anticipating the next standard):



                  #include <algorithm>
                  #include <vector>
                  #include <iostream>
                  #include <range/v3/view.hpp>

                  using iterator = std::vector<char>::const_iterator;
                  struct truncated_instruction ;

                  // one function to interpret the instruction
                  void interpret(char instruction)
                  if (instruction == 'x' throw bad_instruction();
                  std::cout << (int) instruction << ' ';


                  // one function to get the number of bytes to skip
                  int nb_bytes(char c) return 1;

                  class disassembled
                  : public ranges::view_facade<disassembled>

                  friend ranges::range_access;
                  iterator first, last;
                  char const & read() const return *first;
                  bool equal(ranges::default_sentinel) const return first == last;
                  void next()
                  // one function to get to the next instruction
                  auto bytes_to_skip = nb_bytes(*first);
                  if (std::distance(first, last) < bytes_to_skip) throw truncated_instruction();
                  // check if there's enough space left to advance before advancing
                  std::advance(first, bytes_to_skip);

                  public:
                  disassembled() = default;
                  explicit disassembled(const std::vector<char>& v) : first(v.begin()), last(v.end())
                  ;

                  int main()
                  std::vector<char> char_vect = 0x76, 0x10, 0x20, 0x30;
                  try
                  for (auto instruction : disassembled(char_vect)) interpret(instruction);

                  catch // ...






                  share|improve this answer









                  $endgroup$



                  Your code's division into functions isn't very natural. The objective is to have each function performing one specific task, which should also be as orthogonal as possible to the tasks performed by the other functions. For instance, your disassemble function performs three different functions: it reads from the instruction stream, it interprets assembly code and returns the number of bytes that should be skipped to get to the next instruction. That's a not-so-coherent mix of responsibilities.



                  There's also a bug in your code, because it+=disassemble(it); could point beyond char_vect.end() which is in itself undefined behavior, even if you don't dereference the iterator.



                  I would build my disassembler around an iterator, or better even a range (if you don't mind external libraries or anticipating the next standard):



                  #include <algorithm>
                  #include <vector>
                  #include <iostream>
                  #include <range/v3/view.hpp>

                  using iterator = std::vector<char>::const_iterator;
                  struct truncated_instruction ;

                  // one function to interpret the instruction
                  void interpret(char instruction)
                  if (instruction == 'x' throw bad_instruction();
                  std::cout << (int) instruction << ' ';


                  // one function to get the number of bytes to skip
                  int nb_bytes(char c) return 1;

                  class disassembled
                  : public ranges::view_facade<disassembled>

                  friend ranges::range_access;
                  iterator first, last;
                  char const & read() const return *first;
                  bool equal(ranges::default_sentinel) const return first == last;
                  void next()
                  // one function to get to the next instruction
                  auto bytes_to_skip = nb_bytes(*first);
                  if (std::distance(first, last) < bytes_to_skip) throw truncated_instruction();
                  // check if there's enough space left to advance before advancing
                  std::advance(first, bytes_to_skip);

                  public:
                  disassembled() = default;
                  explicit disassembled(const std::vector<char>& v) : first(v.begin()), last(v.end())
                  ;

                  int main()
                  std::vector<char> char_vect = 0x76, 0x10, 0x20, 0x30;
                  try
                  for (auto instruction : disassembled(char_vect)) interpret(instruction);

                  catch // ...







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Mar 13 at 15:30









                  papagagapapagaga

                  4,567321




                  4,567321




















                      Mai Kar is a new contributor. Be nice, and check out our Code of Conduct.









                      draft saved

                      draft discarded


















                      Mai Kar is a new contributor. Be nice, and check out our Code of Conduct.












                      Mai Kar is a new contributor. Be nice, and check out our Code of Conduct.











                      Mai Kar is a new contributor. Be nice, and check out our Code of Conduct.














                      Thanks for contributing an answer to Code Review Stack Exchange!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid


                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.

                      Use MathJax to format equations. MathJax reference.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function ()
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f215349%2fdecoding-assembly-instructions-in-a-game-boy-disassembler%23new-answer', 'question_page');

                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Moe incest case Sentencing See also References Navigation menu"'Australian Josef Fritzl' fathered four children by daughter""Small town recoils in horror at 'Australian Fritzl' incest case""Victorian rape allegations echo Fritzl case - Just In (Australian Broadcasting Corporation)""Incest father jailed for 22 years""'Australian Fritzl' sentenced to 22 years in prison for abusing daughter for three decades""RSJ v The Queen"

                      Who is our nearest planetary neighbor, on average?Santa Claus flies to the South PoleSeven Spheres of Unequal Mass, a weighing problem with a twistDescribe a large integerFast Mental Calculation of $7.5^7$Math in Space (without the help of celebrities)Find the value of $bigstar$: Puzzle 8 - InequalityWho drinks beer while running anyway?A Crucial DeliveryRanking And AverageHow long will my money last at roulette?

                      Daza language Contents Vocabulary Phonology References External links Navigation menudaza1242Daza"Dazaga"eeee178086576