// ---------------------------------------------------------------------------------------------------------------------
//                                      Gabriel Malone / Week 3 / Lab 3 / CS 210
// ---------------------------------------------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
// function prototypes // ----------------------------------------------------------------------------------------------
unsigned short	reverse_bytes(unsigned short);
unsigned short	booths_algorithm(uint8_t, uint8_t);
unsigned int	reverse_words(unsigned int);
unsigned int	ascii_to_integer(char*, int size);
void 			to_upper(char*, int size);
void 			to_lower(char*, int size);
unsigned short	full_adder(uint8_t, uint8_t);
char*			to_bits(unsigned int);
void            print_bits(char*);
void            print_string(char*, int);

// ---------------------------------------------------------------------------------------------------------------------

//                                                      MAIN

// ---------------------------------------------------------------------------------------------------------------------
int main(int argc, char** argv){
// ---------------------------------------------------------------------------------------------------------------------
	// CONVERTING ASCII TO INTEGER DEMO
	// -----------------------------------------------------------------------------------------------------------------
    char ascii_number [] = "123456";                                        // c style string as pointer to char (array)
	printf("%d", ascii_to_integer(ascii_number, 6));
	// -----------------------------------------------------------------------------------------------------------------
	// CONVERTING LOWER TO UPPERCASE DEMO
	// -----------------------------------------------------------------------------------------------------------------
	char strA [] = "abcdefg";
	to_upper(strA, 7);
	print_string(strA, 7);
	// -----------------------------------------------------------------------------------------------------------------
	// CONVERTING UPPER TO LOWER DEMO
	// -----------------------------------------------------------------------------------------------------------------
	char strB [] = "GOOBERT";
	to_lower(strB, 7);
	print_string(strB, 7);
	// -----------------------------------------------------------------------------------------------------------------
	// REVERSING WORDS DEMO
	// -----------------------------------------------------------------------------------------------------------------
	printf("\ndword reversed: %X\n",reverse_words(0xABCDEF00));
	// -----------------------------------------------------------------------------------------------------------------
	// REVERSING BYTES DEMO
	// -----------------------------------------------------------------------------------------------------------------
	printf("short word reversed: %X\n",reverse_bytes(0xABCD));
	// -----------------------------------------------------------------------------------------------------------------
	// PRINTING BITS DEMO
	// -----------------------------------------------------------------------------------------------------------------
	print_bits(to_bits(19));                                                           // enter any number up to 32 bits
	// -----------------------------------------------------------------------------------------------------------------
	// FULL ADDER DEMO
	// -----------------------------------------------------------------------------------------------------------------
	printf("\n%d",full_adder(11,255));
	// -----------------------------------------------------------------------------------------------------------------
	// BOOTHS DEMO
	// -----------------------------------------------------------------------------------------------------------------
	printf("\n%d", booths_algorithm(100,12));

}
// ---------------------------------------------------------------------------------------------------------------------

//                                                      METHODS

// ---------------------------------------------------------------------------------------------------------------------

// ---------------------------------------------------------------------------------------------------------------------
unsigned int reverse_words(unsigned int dword){
	// -----------------------------------------------------------------------------------------------------------------
	return ( (dword & 0x0000FFFF) << 16) + ( (dword & 0xFFFF0000) >> 16); // dword & LO mask << to HO pos and vice-versa
}

// ---------------------------------------------------------------------------------------------------------------------
unsigned short reverse_bytes(unsigned short word){
	// -----------------------------------------------------------------------------------------------------------------
	return ( (word & 0x00FF) << 8) + ( (word & 0xFF00) >> 8);              // word & LO mask << to HO pos and vice-versa
}

// ---------------------------------------------------------------------------------------------------------------------
unsigned short booths_algorithm(uint8_t mcnd, uint8_t mplr){
	// -----------------------------------------------------------------------------------------------------------------
	unsigned short itr = 0, PB = 0, CB = mplr & 1, LO = mplr, HO=0,product = 0x0000, phb;
	while (itr ++ < 8)                                                                        // need to loop for 8 bits
	{
		if (CB ^ PB)                                                                           // if xor results in true
		{                                                                             // add multiplicand to the HO byte
			if (CB)   HO = full_adder(HO, full_adder(~mcnd, 1));             // twos compliment addition for subtraction
			if (PB)   HO = full_adder(HO, mcnd);                                                             // addition
		}
		product = (HO << 8) | LO;    // new product is HO byte shifted into 16 bit position, w multiplier in LO position
		PB = product & 1;                                                                      // store previous end bit
		phb = product & 0x8000;                            // check if highest bit is 0 or 1 before shifting to maintain
		product >>= 1;                                                                 // shift product to the right one
		if (phb) product |= 0x8000;                                                 // force HO to 1 if originally was 1
		CB = product & 1;                                                                         // get current end bit
		HO = product >> 8;                                               // set new HO byte (the HO byte of the product)
		LO = product & 0x00FF;                                                                        // set new LO byte
	}
	return product;
}

// ---------------------------------------------------------------------------------------------------------------------
unsigned int ascii_to_integer(char *ascii_number, int size){
	//------------------------------------------------------------------------------------------------------------------
	unsigned int converted = 0x00000000;
	for (int i = 0 ; i < size ; i ++ ) {       // clear HO 11's then convert each number to its correct decimal position
		converted = converted * 10 + ( *ascii_number ++ &= ~( 3 << 4 ) );
	}                                                   // 0 + 1 = 1 * 10 = 10, 10 + 2 = 12, 12*10 = 120 + 3 = 123, etc.
	return converted;  // then set to 0 to clear those bits with &. the result is multiplied by current base 10 position
}

// ---------------------------------------------------------------------------------------------------------------------
void to_upper(char* d, int size){
	// -----------------------------------------------------------------------------------------------------------------
	for (int i = 0 ; i < size ; i ++)  *d ++ &= ~ ( 1 << 5 );                                                
								   // set 1, move it into position, flip it to zero, and the bit with that zero to clear
}
// ---------------------------------------------------------------------------------------------------------------------

// ---------------------------------------------------------------------------------------------------------------------
void to_lower(char* c, int size){
	// -----------------------------------------------------------------------------------------------------------------
	for (int i = 0 ; i < size ; i ++) *c ++ |= 1 << 5;   // set 1 , move to position , or the 1 with the bit to set to 1
}

// ---------------------------------------------------------------------------------------------------------------------
unsigned short full_adder(uint8_t one, uint8_t two) {
	// -----------------------------------------------------------------------------------------------------------------
	// ~~ deep seek helped fix my original attempt which failed to carry correctly sometimes & did not have final carry
	// -----------------------------------------------------------------------------------------------------------------
	int      shift = 0;
	uint8_t  carry = 0;
	uint16_t sum   = 0;                                                          // Store 8-bit sum + carry-out in bit 8
	// -----------------------------------------------------------------------------------------------------------------
	while (shift < 8) {                                                                         // go through all 8 bits
		uint8_t a_bit = (one >> shift) & 1;                                                        // start from LO bits
		uint8_t b_bit = (two >> shift) & 1;                                                        // start from LO bits
		uint8_t sum_bit = a_bit ^ b_bit ^ carry;          // sum_bit is 1 when there's an odd number of 1s in the inputs
		carry = (a_bit & b_bit) | (a_bit & carry) | (b_bit & carry); // carry-out exists if at least 2 of 3 inputs are 1
		sum |= (sum_bit << shift ++ );        // set the current bit with the sum_bit then increment shift for next loop
	}
	return sum |= (carry << 8);                                 // when done, store any carry-out in the 9th bit (bit 8)
}

// ---------------------------------------------------------------------------------------------------------------------
char *to_bits(unsigned int num){
	// -----------------------------------------------------------------------------------------------------------------
	char* bits = malloc(sizeof(unsigned int) * sizeof(char));// 4 bytes for int and 1 byte for char = 4 bytes or 32 bits
	int shifts = 0;                                                 // how many shifts will be performed (32, 1 per bit)
	// -----------------------------------------------------------------------------------------------------------------
	while (shifts < 32)                                                                                // start shifting
		*bits ++ = ( num & 1 << shifts ++ ) ? '1' : '0';   // deref the char array, inc post, mask w/ << 1, pick outcome
	// -----------------------------------------------------------------------------------------------------------------
	return bits;                                                        // return the pointer (will need to be re-wound)
}


// ---------------------------------------------------------------------------------------------------------------------
//                                                    extra helpers
// ---------------------------------------------------------------------------------------------------------------------
void print_bits(char* ptr){
	// -----------------------------------------------------------------------------------------------------------------
	int cntr = 0;   // while pointer isn't null, backtrack from current pointer address in memory, print char at mem add
	while (*--ptr) (cntr == 4) ? cntr = 0 , printf("_%c",*ptr) : printf("%c",*ptr) , cntr ++ ;
}
// ---------------------------------------------------------------------------------------------------------------------


// ---------------------------------------------------------------------------------------------------------------------
void print_string(char* string, int size){
	// -----------------------------------------------------------------------------------------------------------------
	for (int i = 0 ; i < size ; i ++) (i == 0) ? printf("\n%c", string[i]) : printf("%c", string[i]);
}
// ---------------------------------------------------------------------------------------------------------------------