Thread: A few more assembly to C++ questions

  1. #1
    Registered User
    Join Date
    Apr 2021
    Posts
    26

    A few more assembly to C++ questions

    So I have a solid start on getting the 3/4 of the code I've transliterated moving towards proper C++.

    I've got some questions that I would like input on to get an idea how to tackle the other 25% of the code.

    In the setup phase, near the beginning is a section where the 6821 PIA and 6840 timer IC are initialized. The original code disables the interrupts before this section and re-enables them afterwards. I'm not sure how critical this part may be. The compiler complained about the noInterrupts() and interrupts() instructions I'd used after seeing mention of these calls somewhere on the interwebs...?

    The second thing I'm wondering about is how to handle binary to BCD conversion - there are a number of times where the 6800-class DAA instruction is invoked.

    There are also a number of occasions where two 8-bit numbers are assembled into a 16-bit number for a bitwise shift (by loading the upper byte into Accumulator A, the lower byte into Accumulator B, and then operating on both as the 16-bit Accumulator D). How do I do this in C++?

    Another prickly one is the fact that there are a number of ROL and ROR instructions (these rotate the bits in one direction through the carry flag, THEN back into the accumulator or memory location).

    One last thing I thought of that would be easy for you folks to answer is how to wait for a keypress or an external input to change state. I know this is a perfect case for using a while or do...while but I'm a bit fuzzy on the concept yet.
    In other words, I'm looking for the C++ equivalent of this old Basic code:

    10 GET A$: IF A$ = "" THEN 10

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    38,714
    > I'm not sure how critical this part may be.
    Very - Setting up the initial hardware environment is a delicate operation, especially when that hardware can also generate interrupts.
    Not at all - the basic hardware will have been minimally configured by the bootstrap of your chosen platform.

    It boils down to whether you're really programming on the bare metal, or something with an OS already present.

    > The compiler complained about the noInterrupts() and interrupts() instructions
    Which compiler?
    Messing with interrupts is compiler / OS / platform specific. There is no portable code here.

    > The second thing I'm wondering about is how to handle binary to BCD conversion
    > there are a number of times where the 6800-class DAA instruction is invoked.
    Where it would take say a binary value such as 0x1b (aka 27 decimal) and transform that into 0x27 in the register.
    The two nibbles in the byte represent the 10's and units of the numeric value.

    Mostly this is just /10 and %10 to turn your binary data into something humans can read in base 10.
    DAA just allows you to do this on values in the range 0 to 99 without lots of expensive (for an 8-bit micro) / and % instructions.

    > There are also a number of occasions where two 8-bit numbers are assembled into a 16-bit number for a bitwise shift
    You would use an int.
    As in
    int a = (high<<8) | (low);

    > Another prickly one is the fact that there are a number of ROL and ROR instructions
    You have to build your own using <<, >>, &, | operations.
    But look at the wider context of how it is used.

    The same goes for all of the above.
    Look at the context to derive equivalent functionality in C++.

    > 10 GET A$: IF A$ = "" THEN 10
    std::getline - cppreference.com
    Like
    Code:
    std::string line;
    while ( std::getline(std::cin,line) && line == "" );
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Apr 2021
    Posts
    45
    Quote Originally Posted by etech58761 View Post
    The compiler complained about the noInterrupts() and interrupts() instructions I'd used after seeing mention of these calls somewhere on the interwebs...?
    If these functions aren't defined for you, then you'll need to write them yourself. I'd suggest that you create a bunch of "low level" functions in a header file, using the pre-processor to select from different options based on the CPU.

    For example, to enable interrupts you might do something like:
    Code:
    static inline void
    enable_interrupts()
    {
    #if CPU_IS_8051
    
        const u8 enable = (IEFLAGS.EA + IEFLAGS.ES | IEFLAGS.ET0 | IEFLAGS.ET1 | IEFLAGS.EX0 | IEFLAGS.EX1);
    
        INLINE_ASSEMBLY_START
            MOV A, enable
            MOV EI, A
        INLINE_ASSEMBLY_END
    
    #elif CPU_IS_ARDUINO
        interrupts();    // ARDUINO library includes this function
    #else
        #error "Need to port this function to this compiler."
    #endif
    }
    The second thing I'm wondering about is how to handle binary to BCD conversion - there are a number of times where the 6800-class DAA instruction is invoked.
    This is a straightforward programming problem. You can have two alternatives: one where there is an opcode, and one where there is not. Write a software function to do the conversion (or, more likely, Ask The Duck to find you someone else's function and copy it) and store that in a routine called "software_toBCD". Then conditionally either call the software version, or call the CPU-specific version that invoked the inline assembly.

    There are also a number of occasions where two 8-bit numbers are assembled into a 16-bit number for a bitwise shift (by loading the upper byte into Accumulator A, the lower byte into Accumulator B, and then operating on both as the 16-bit Accumulator D). How do I do this in C++?
    Declare a 16-bit number and compute it:

    Code:
        u8 value_1, value2;
    
        u16 result = value_1 << 8 | value_2;
    Another prickly one is the fact that there are a number of ROL and ROR instructions (these rotate the bits in one direction through the carry flag, THEN back into the accumulator or memory location).
    In this case, shift the bits, then mask off the non-wanted bits. Then shift the original value the other way, and 'or' the two together:

    Code:
        #include <ciso646>
        u16 input_value;
    
        u16 bottom_2_bits = (input_value & 0x0003) << 14;
        u16 top_14_bits = (input_value >> 2) & compl(0x03 << 14);
        u16 result = bottom_2_bits | top_14_bits;

  4. #4
    Registered User
    Join Date
    Apr 2021
    Posts
    26
    Interrupts:
    I'll just comment out the calls for now until I decide what hardware I will end up using, see how interrupts are handled on the chosen platform and go from there at that time.

    BCD:
    I may just have to walk backwards through the code to figure out the best approach needed. Worst case, I grab the 6800 reference, look up the DAA instruction, and whip up a bit of code to handle the needed conversion.

    16-bit accumulator:
    Based on the suggestion, here's my first try:

    Code:
    //    Original location: $FAE1
    //    Calls: 2
    LB_FAE1:
        {
        AccD = ( ( ram_0C7 << 8 ) | ram_0C6 )
        AccD = ( AccD << 2 )
        ram_C11 = ( AccD >> 8 )
    
        AccD = ( ( ram_0C6 << 8 ) | ram_0C5 )
        AccD = ( AccD << 2 )
        ram_C10 = ( AccD >> 8 )
        ram_C0F = ( AccD & 0xFF )
        }
    /*
        [ LDA A    ram_0C7 ]
        [ LDA B    ram_0C6 ]
        [ ASL D ]
        [ ASL D ]
        [ STA A    ram_C11 ]
    
        [ LDA A    ram_0C6 ]
        [ LDA B    ram_0C5 ]
        [ ASL D ]
        [ ASL D ]
        [ STA A    ram_C10 ]
        [ STA B    ram_C0F ]
    */
    ROL / ROR:

    Will analyze the code and combine left / right shifts as required, but want to see if my try at the 16-bit accumulator looks good and clean that up first.

  5. #5
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    784
    Reading through your threads reminds me of a project that I found where a 6502 chess program was converted to C by using 6502 emulation macros. From what I understand, the 6502 is somewhat similar to the 6800 so you could do something similar.

    See Microchess (under Microchess C Emulation).

    The original 6502 code looks like this:

    Code:
    CHESS           CLD                     ; INITIALIZE
                    LDX     #$FF            ; TWO STACKS
                    TXS
                    LDX     #$C8
                    STX     SP2
    and the C code looks nearly identical, like this:

    Code:
    CHESS_BEGIN:                                //
                    CLD;                        // INITIALIZE
                    LDXi    (0xFF);             // TWO STACKS
                    TXS;
                    LDXi    (0xC8);
                    STX     (SP2);

  6. #6
    Registered User
    Join Date
    Apr 2021
    Posts
    45
    Quote Originally Posted by etech58761 View Post
    Code:
    /*
        [ LDA A    ram_0C7 ]
        [ LDA B    ram_0C6 ]
        [ ASL D ]
        [ ASL D ]
        [ STA A    ram_C11 ]
    
        [ LDA A    ram_0C6 ]
        [ LDA B    ram_0C5 ]
        [ ASL D ]
        [ ASL D ]
        [ STA A    ram_C10 ]
        [ STA B    ram_C0F ]
    */
    This looks to me like a 24-bit number being shifted in two parts.

    The first pair of LDA's gets the upper 16 bits, shifts 2, stores a byte. The next pairs of LDA's gets the lower 16 bits, shifts 2, stores 2 bytes.

    Not sure what the context is, but it might help if you thought of ram_C0F .. ram_C11 as a 24-bit number. (The alternative is that it's a bunch of 8-bit numbers where the top two bits are being shuffled along? Not sure what that would do, maybe some kind of state update?)

  7. #7
    Registered User
    Join Date
    Apr 2021
    Posts
    26
    I do see some places where operations are performed on consecutive or grouped memory locations, so you may well be on to something in regards to 24-bit math. I'll keep that in mind.

    I already know there are sections involving 16-bit math as there is one MUL operand in the code.

    Once I get the bigger picture as to what's going on in places, I can begin to redo those portions in a more sensible manner.

  8. #8
    Registered User
    Join Date
    Apr 2021
    Posts
    26
    I've been focusing on the interrupt routine that runs in the background, and these 800-some bytes of code are a lot more challenging than I expected.

    I can't decide if they went the obfuscation route or if they chose the operations and steps used to make the code run more efficiently.

    I have already found one bit of code that made full use of the ROL / ROR opcodes to fetch and store the incoming serial data stream by way of the carry flag, so have to do this byte by byte and not by opcode... uffda

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Convert assembly>machine code, machine code>assembly
    By wenxinleong in forum C Programming
    Replies: 12
    Last Post: 06-23-2011, 10:42 PM
  2. questions....so many questions about random numbers....
    By face_master in forum C++ Programming
    Replies: 2
    Last Post: 07-30-2009, 08:47 AM
  3. Two basic questions about generated assembly
    By George2 in forum Windows Programming
    Replies: 2
    Last Post: 07-16-2008, 03:19 AM
  4. how i can use assembly with C
    By MaaSTaaR in forum C Programming
    Replies: 3
    Last Post: 08-23-2005, 12:52 AM

Tags for this Thread