// File: CMU-SAFARI[P]MQSim/src/ssd/GC_and_WL_Unit_Page_Level.cpp
// Function: SSD_Components::GC_and_WL_Unit_Page_Level::Check_gc_required(unsigned int, NVM::FlashMemory::Physical_Page_Address const&)
void GC_and_WL_Unit_Page_Level::Check_gc_required(const unsigned int free_block_pool_size, const NVM::FlashMemory::Physical_Page_Address& plane_address)
	{
		if (free_block_pool_size < block_pool_gc_threshold) {
			flash_block_ID_type gc_candidate_block_id = block_manager->Get_coldest_block_id(plane_address);
			PlaneBookKeepingType* pbke = block_manager->Get_plane_bookkeeping_entry(plane_address);

			if (pbke->Ongoing_erase_operations.size() >= max_ongoing_gc_reqs_per_plane) {
				return;
			}

			switch (block_selection_policy) {
				case SSD_Components::GC_Block_Selection_Policy_Type::GREEDY://Find the set of blocks with maximum number of invalid pages and no free pages
				{
					gc_candidate_block_id = 0;
					if (pbke->Ongoing_erase_operations.find(0) != pbke->Ongoing_erase_operations.end()) {
						gc_candidate_block_id++;
					}
					for (flash_block_ID_type block_id = 1; block_id < block_no_per_plane; block_id++) {
						if (pbke->Blocks[block_id].Invalid_page_count > pbke->Blocks[gc_candidate_block_id].Invalid_page_count
							&& pbke->Blocks[block_id].Current_page_write_index == pages_no_per_block
							&& is_safe_gc_wl_candidate(pbke, block_id)) {
							gc_candidate_block_id = block_id;
						}
					}
					break;
				}
				case SSD_Components::GC_Block_Selection_Policy_Type::RGA:
				{
					std::set<flash_block_ID_type> random_set;
					while (random_set.size() < rga_set_size) {
						flash_block_ID_type block_id = random_generator.Uniform_uint(0, block_no_per_plane - 1);
						if (pbke->Ongoing_erase_operations.find(block_id) == pbke->Ongoing_erase_operations.end()
							&& is_safe_gc_wl_candidate(pbke, block_id)) {
							random_set.insert(block_id);
							}
					}
					gc_candidate_block_id = *random_set.begin();
					for(auto &block_id : random_set) {
						if (pbke->Blocks[block_id].Invalid_page_count > pbke->Blocks[gc_candidate_block_id].Invalid_page_count
							&& pbke->Blocks[block_id].Current_page_write_index == pages_no_per_block) {
							gc_candidate_block_id = block_id;
						}
					}
					break;
				}
				case SSD_Components::GC_Block_Selection_Policy_Type::RANDOM:
				{
					gc_candidate_block_id = random_generator.Uniform_uint(0, block_no_per_plane - 1);
					unsigned int repeat = 0;

					//A write frontier block should not be selected for garbage collection
					while (!is_safe_gc_wl_candidate(pbke, gc_candidate_block_id) && repeat++ < block_no_per_plane) {
						gc_candidate_block_id = random_generator.Uniform_uint(0, block_no_per_plane - 1);
					}
					break;
				}
				case SSD_Components::GC_Block_Selection_Policy_Type::RANDOM_P:
				{
					gc_candidate_block_id = random_generator.Uniform_uint(0, block_no_per_plane - 1);
					unsigned int repeat = 0;

					//A write frontier block or a block with free pages should not be selected for garbage collection
					while ((pbke->Blocks[gc_candidate_block_id].Current_page_write_index < pages_no_per_block || !is_safe_gc_wl_candidate(pbke, gc_candidate_block_id))
						&& repeat++ < block_no_per_plane) {
						gc_candidate_block_id = random_generator.Uniform_uint(0, block_no_per_plane - 1);
					}
					break;
				}
				case SSD_Components::GC_Block_Selection_Policy_Type::RANDOM_PP:
				{
					gc_candidate_block_id = random_generator.Uniform_uint(0, block_no_per_plane - 1);
					unsigned int repeat = 0;

					//The selected gc block should have a minimum number of invalid pages
					while ((pbke->Blocks[gc_candidate_block_id].Current_page_write_index < pages_no_per_block 
						|| pbke->Blocks[gc_candidate_block_id].Invalid_page_count < random_pp_threshold
						|| !is_safe_gc_wl_candidate(pbke, gc_candidate_block_id))
						&& repeat++ < block_no_per_plane) {
						gc_candidate_block_id = random_generator.Uniform_uint(0, block_no_per_plane - 1);
					}
					break;
				}
				case SSD_Components::GC_Block_Selection_Policy_Type::FIFO:
					gc_candidate_block_id = pbke->Block_usage_history.front();
					pbke->Block_usage_history.pop();
					break;
				default:
					break;
			}

			//This should never happen, but we check it here for safty
			if (pbke->Ongoing_erase_operations.find(gc_candidate_block_id) != pbke->Ongoing_erase_operations.end()) {
				return;
			}
			
			NVM::FlashMemory::Physical_Page_Address gc_candidate_address(plane_address);
			gc_candidate_address.BlockID = gc_candidate_block_id;
			Block_Pool_Slot_Type* block = &pbke->Blocks[gc_candidate_block_id];

			//No invalid page to erase
			if (block->Current_page_write_index == 0 || block->Invalid_page_count == 0) {
				return;
			}
			
			//Run the state machine to protect against race condition
			block_manager->GC_WL_started(gc_candidate_address);
			pbke->Ongoing_erase_operations.insert(gc_candidate_block_id);
			address_mapping_unit->Set_barrier_for_accessing_physical_block(gc_candidate_address);//Lock the block, so no user request can intervene while the GC is progressing
			
			//If there are ongoing requests targeting the candidate block, the gc execution should be postponed
			if (block_manager->Can_execute_gc_wl(gc_candidate_address)) {
				Stats::Total_gc_executions++;
				tsu->Prepare_for_transaction_submit();

				NVM_Transaction_Flash_ER* gc_erase_tr = new NVM_Transaction_Flash_ER(Transaction_Source_Type::GC_WL, pbke->Blocks[gc_candidate_block_id].Stream_id, gc_candidate_address);
				//If there are some valid pages in block, then prepare flash transactions for page movement
				if (block->Current_page_write_index - block->Invalid_page_count > 0) {
					NVM_Transaction_Flash_RD* gc_read = NULL;
					NVM_Transaction_Flash_WR* gc_write = NULL;
					for (flash_page_ID_type pageID = 0; pageID < block->Current_page_write_index; pageID++) {
						if (block_manager->Is_page_valid(block, pageID)) {
							Stats::Total_page_movements_for_gc++;
							gc_candidate_address.PageID = pageID;
							if (use_copyback) {
								gc_write = new NVM_Transaction_Flash_WR(Transaction_Source_Type::GC_WL, block->Stream_id, sector_no_per_page * SECTOR_SIZE_IN_BYTE,
									NO_LPA, address_mapping_unit->Convert_address_to_ppa(gc_candidate_address), NULL, 0, NULL, 0, INVALID_TIME_STAMP);
								gc_write->ExecutionMode = WriteExecutionModeType::COPYBACK;
								tsu->Submit_transaction(gc_write);
							} else {
								gc_read = new NVM_Transaction_Flash_RD(Transaction_Source_Type::GC_WL, block->Stream_id, sector_no_per_page * SECTOR_SIZE_IN_BYTE,
									NO_LPA, address_mapping_unit->Convert_address_to_ppa(gc_candidate_address), gc_candidate_address, NULL, 0, NULL, 0, INVALID_TIME_STAMP);
								gc_write = new NVM_Transaction_Flash_WR(Transaction_Source_Type::GC_WL, block->Stream_id, sector_no_per_page * SECTOR_SIZE_IN_BYTE,
									NO_LPA, NO_PPA, gc_candidate_address, NULL, 0, gc_read, 0, INVALID_TIME_STAMP);
								gc_write->ExecutionMode = WriteExecutionModeType::SIMPLE;
								gc_write->RelatedErase = gc_erase_tr;
								gc_read->RelatedWrite = gc_write;
								tsu->Submit_transaction(gc_read);//Only the read transaction would be submitted. The Write transaction is submitted when the read transaction is finished and the LPA of the target page is determined
							}
							gc_erase_tr->Page_movement_activities.push_back(gc_write);
						}
					}
				}
				block->Erase_transaction = gc_erase_tr;
				tsu->Submit_transaction(gc_erase_tr);

				tsu->Schedule();
			}
		}
	}

// Assembly:
subq $0x1f8, %rsp            # imm = 0x1F8
movq %rdi, 0x1f0(%rsp)
movl %esi, 0x1ec(%rsp)
movq %rdx, 0x1e0(%rsp)
movq 0x1f0(%rsp), %rcx
movq %rcx, 0xa8(%rsp)
movl 0x1ec(%rsp), %eax
cmpl 0x60(%rcx), %eax
jae 0x840fb
movq 0xa8(%rsp), %rax
movq 0x38(%rax), %rdi
movq 0x1e0(%rsp), %rsi
callq 0x7ca80
movl %eax, %ecx
movq 0xa8(%rsp), %rax
movl %ecx, 0x1dc(%rsp)
movq 0x38(%rax), %rdi
movq 0x1e0(%rsp), %rsi
callq 0x7cb40
movq %rax, 0x1d0(%rsp)
movq 0x1d0(%rsp), %rdi
addq $0xb0, %rdi
callq 0x84110
movq 0xa8(%rsp), %rcx
movl 0x7c(%rcx), %ecx
cmpq %rcx, %rax
jb 0x8339a
jmp 0x840fb
movq 0xa8(%rsp), %rax
movl 0x2c(%rax), %eax
movq %rax, 0xa0(%rsp)
subq $0x5, %rax
ja 0x83af1
movq 0xa0(%rsp), %rax
leaq 0x2aaa6(%rip), %rcx     # 0xade6c
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movl $0x0, 0x1dc(%rsp)
movq 0x1d0(%rsp), %rdi
addq $0xb0, %rdi
movl $0x0, 0x1c4(%rsp)
leaq 0x1c4(%rsp), %rsi
callq 0x5a300
movq %rax, 0x1c8(%rsp)
movq 0x1d0(%rsp), %rdi
addq $0xb0, %rdi
callq 0x5a340
movq %rax, 0x1b8(%rsp)
leaq 0x1c8(%rsp), %rdi
leaq 0x1b8(%rsp), %rsi
callq 0x5a2e0
testb $0x1, %al
jne 0x83440
jmp 0x83451
movl 0x1dc(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x1dc(%rsp)
movl $0x1, 0x1b4(%rsp)
movq 0xa8(%rsp), %rcx
movl 0x1b4(%rsp), %eax
cmpl 0x104(%rcx), %eax
jae 0x83528
movq 0x1d0(%rsp), %rax
movq 0x10(%rax), %rax
movl 0x1b4(%rsp), %ecx
shlq $0x6, %rcx
addq %rcx, %rax
movl 0xc(%rax), %eax
movq 0x1d0(%rsp), %rcx
movq 0x10(%rcx), %rcx
movl 0x1dc(%rsp), %edx
shlq $0x6, %rdx
addq %rdx, %rcx
cmpl 0xc(%rcx), %eax
jbe 0x83510
movq 0xa8(%rsp), %rcx
movq 0x1d0(%rsp), %rax
movq 0x10(%rax), %rax
movl 0x1b4(%rsp), %edx
shlq $0x6, %rdx
addq %rdx, %rax
movl 0x4(%rax), %eax
cmpl 0x108(%rcx), %eax
jne 0x83510
movq 0xa8(%rsp), %rdi
movq 0x1d0(%rsp), %rsi
movl 0x1b4(%rsp), %edx
callq 0x81b80
testb $0x1, %al
jne 0x83502
jmp 0x83510
movl 0x1b4(%rsp), %eax
movl %eax, 0x1dc(%rsp)
jmp 0x83512
movl 0x1b4(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x1b4(%rsp)
jmp 0x8345c
jmp 0x83af3
leaq 0x180(%rsp), %rdi
callq 0x58dd0
leaq 0x180(%rsp), %rdi
callq 0x84110
movq 0xa8(%rsp), %rcx
movl 0x80(%rcx), %ecx
cmpq %rcx, %rax
jae 0x836bc
movq 0xa8(%rsp), %rax
movq %rax, %rdi
addq $0x88, %rdi
movl 0x104(%rax), %edx
decl %edx
xorl %esi, %esi
callq 0xa6fe0
movl %eax, 0x9c(%rsp)
jmp 0x83588
movl 0x9c(%rsp), %eax
movl %eax, 0x17c(%rsp)
movq 0x1d0(%rsp), %rdi
addq $0xb0, %rdi
leaq 0x17c(%rsp), %rsi
callq 0x5a300
movq %rax, 0x90(%rsp)
jmp 0x835bc
movq 0x90(%rsp), %rax
movq %rax, 0x160(%rsp)
movq 0x1d0(%rsp), %rdi
addq $0xb0, %rdi
callq 0x5a340
movq %rax, 0x158(%rsp)
leaq 0x160(%rsp), %rdi
leaq 0x158(%rsp), %rsi
callq 0x5dec0
movb %al, %cl
xorl %eax, %eax
testb $0x1, %cl
movb %al, 0x8f(%rsp)
jne 0x8360f
jmp 0x83644
movq 0xa8(%rsp), %rdi
movq 0x1d0(%rsp), %rsi
movl 0x17c(%rsp), %edx
callq 0x81b80
movb %al, 0x8e(%rsp)
jmp 0x83634
movb 0x8e(%rsp), %al
movb %al, 0x8f(%rsp)
jmp 0x83644
movb 0x8f(%rsp), %al
testb $0x1, %al
jne 0x83651
jmp 0x836b7
leaq 0x180(%rsp), %rdi
leaq 0x17c(%rsp), %rsi
callq 0x5e630
movb %dl, 0x7f(%rsp)
movq %rax, 0x80(%rsp)
jmp 0x83674
movb 0x7f(%rsp), %al
movq 0x80(%rsp), %rcx
movq %rcx, 0x148(%rsp)
movb %al, 0x150(%rsp)
jmp 0x836b7
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x170(%rsp)
movl %eax, 0x16c(%rsp)
leaq 0x180(%rsp), %rdi
callq 0x58de0
jmp 0x84103
jmp 0x8353a
leaq 0x180(%rsp), %rdi
callq 0x84120
movq %rax, 0x140(%rsp)
leaq 0x140(%rsp), %rdi
callq 0x84150
movl (%rax), %eax
movl %eax, 0x1dc(%rsp)
leaq 0x180(%rsp), %rax
movq %rax, 0x138(%rsp)
movq 0x138(%rsp), %rdi
callq 0x84120
movq %rax, 0x130(%rsp)
movq 0x138(%rsp), %rdi
callq 0x5a340
movq %rax, 0x128(%rsp)
leaq 0x130(%rsp), %rdi
leaq 0x128(%rsp), %rsi
callq 0x5a2e0
testb $0x1, %al
jne 0x8373f
jmp 0x837e8
leaq 0x130(%rsp), %rdi
callq 0x84150
movq %rax, 0x120(%rsp)
movq 0x1d0(%rsp), %rax
movq 0x10(%rax), %rax
movq 0x120(%rsp), %rcx
movl (%rcx), %ecx
shlq $0x6, %rcx
addq %rcx, %rax
movl 0xc(%rax), %eax
movq 0x1d0(%rsp), %rcx
movq 0x10(%rcx), %rcx
movl 0x1dc(%rsp), %edx
shlq $0x6, %rdx
addq %rdx, %rcx
cmpl 0xc(%rcx), %eax
jbe 0x837d4
movq 0xa8(%rsp), %rcx
movq 0x1d0(%rsp), %rax
movq 0x10(%rax), %rax
movq 0x120(%rsp), %rdx
movl (%rdx), %edx
shlq $0x6, %rdx
addq %rdx, %rax
movl 0x4(%rax), %eax
cmpl 0x108(%rcx), %eax
jne 0x837d4
movq 0x120(%rsp), %rax
movl (%rax), %eax
movl %eax, 0x1dc(%rsp)
jmp 0x837d6
leaq 0x130(%rsp), %rdi
callq 0x5eef0
jmp 0x83721
leaq 0x180(%rsp), %rdi
callq 0x58de0
jmp 0x83af3
movq 0xa8(%rsp), %rax
movq %rax, %rdi
addq $0x88, %rdi
movl 0x104(%rax), %edx
subl $0x1, %edx
xorl %esi, %esi
callq 0xa6fe0
movl %eax, 0x1dc(%rsp)
movl $0x0, 0x11c(%rsp)
movq 0xa8(%rsp), %rdi
movq 0x1d0(%rsp), %rsi
movl 0x1dc(%rsp), %edx
callq 0x81b80
movb %al, %cl
xorl %eax, %eax
testb $0x1, %cl
movb %al, 0x7e(%rsp)
jne 0x8387f
movq 0xa8(%rsp), %rcx
movl 0x11c(%rsp), %eax
movl %eax, %edx
addl $0x1, %edx
movl %edx, 0x11c(%rsp)
cmpl 0x104(%rcx), %eax
setb %al
movb %al, 0x7e(%rsp)
movb 0x7e(%rsp), %al
testb $0x1, %al
jne 0x83889
jmp 0x838b7
movq 0xa8(%rsp), %rax
movq %rax, %rdi
addq $0x88, %rdi
movl 0x104(%rax), %edx
subl $0x1, %edx
xorl %esi, %esi
callq 0xa6fe0
movl %eax, 0x1dc(%rsp)
jmp 0x8382e
jmp 0x83af3
movq 0xa8(%rsp), %rax
movq %rax, %rdi
addq $0x88, %rdi
movl 0x104(%rax), %edx
subl $0x1, %edx
xorl %esi, %esi
callq 0xa6fe0
movl %eax, 0x1dc(%rsp)
movl $0x0, 0x118(%rsp)
movq 0xa8(%rsp), %rcx
movq 0x1d0(%rsp), %rax
movq 0x10(%rax), %rax
movl 0x1dc(%rsp), %edx
shlq $0x6, %rdx
addq %rdx, %rax
movl 0x4(%rax), %eax
cmpl 0x108(%rcx), %eax
jb 0x83946
movq 0xa8(%rsp), %rdi
movq 0x1d0(%rsp), %rsi
movl 0x1dc(%rsp), %edx
callq 0x81b80
movb %al, %cl
xorl %eax, %eax
testb $0x1, %cl
movb %al, 0x7d(%rsp)
jne 0x8396e
movq 0xa8(%rsp), %rcx
movl 0x118(%rsp), %eax
movl %eax, %edx
addl $0x1, %edx
movl %edx, 0x118(%rsp)
cmpl 0x104(%rcx), %eax
setb %al
movb %al, 0x7d(%rsp)
movb 0x7d(%rsp), %al
testb $0x1, %al
jne 0x83978
jmp 0x839a6
movq 0xa8(%rsp), %rax
movq %rax, %rdi
addq $0x88, %rdi
movl 0x104(%rax), %edx
subl $0x1, %edx
xorl %esi, %esi
callq 0xa6fe0
movl %eax, 0x1dc(%rsp)
jmp 0x838f0
jmp 0x83af3
movq 0xa8(%rsp), %rax
movq %rax, %rdi
addq $0x88, %rdi
movl 0x104(%rax), %edx
subl $0x1, %edx
xorl %esi, %esi
callq 0xa6fe0
movl %eax, 0x1dc(%rsp)
movl $0x0, 0x114(%rsp)
movq 0xa8(%rsp), %rcx
movq 0x1d0(%rsp), %rax
movq 0x10(%rax), %rax
movl 0x1dc(%rsp), %edx
shlq $0x6, %rdx
addq %rdx, %rax
movl 0x4(%rax), %eax
cmpl 0x108(%rcx), %eax
jb 0x83a62
movq 0xa8(%rsp), %rcx
movq 0x1d0(%rsp), %rax
movq 0x10(%rax), %rax
movl 0x1dc(%rsp), %edx
shlq $0x6, %rdx
addq %rdx, %rax
movl 0xc(%rax), %eax
cmpl 0xf0(%rcx), %eax
jb 0x83a62
movq 0xa8(%rsp), %rdi
movq 0x1d0(%rsp), %rsi
movl 0x1dc(%rsp), %edx
callq 0x81b80
movb %al, %cl
xorl %eax, %eax
testb $0x1, %cl
movb %al, 0x7c(%rsp)
jne 0x83a8a
movq 0xa8(%rsp), %rcx
movl 0x114(%rsp), %eax
movl %eax, %edx
addl $0x1, %edx
movl %edx, 0x114(%rsp)
cmpl 0x104(%rcx), %eax
setb %al
movb %al, 0x7c(%rsp)
movb 0x7c(%rsp), %al
testb $0x1, %al
jne 0x83a94
jmp 0x83ac2
movq 0xa8(%rsp), %rax
movq %rax, %rdi
addq $0x88, %rdi
movl 0x104(%rax), %edx
subl $0x1, %edx
xorl %esi, %esi
callq 0xa6fe0
movl %eax, 0x1dc(%rsp)
jmp 0x839df
jmp 0x83af3
movq 0x1d0(%rsp), %rdi
addq $0x60, %rdi
callq 0x84170
movl (%rax), %eax
movl %eax, 0x1dc(%rsp)
movq 0x1d0(%rsp), %rdi
addq $0x60, %rdi
callq 0x84180
jmp 0x83af3
jmp 0x83af3
movq 0x1d0(%rsp), %rdi
addq $0xb0, %rdi
leaq 0x1dc(%rsp), %rsi
callq 0x5a300
movq %rax, 0x108(%rsp)
movq 0x1d0(%rsp), %rdi
addq $0xb0, %rdi
callq 0x5a340
movq %rax, 0x100(%rsp)
leaq 0x108(%rsp), %rdi
leaq 0x100(%rsp), %rsi
callq 0x5a2e0
testb $0x1, %al
jne 0x83b4e
jmp 0x83b53
jmp 0x840fb
movq 0x1e0(%rsp), %rsi
leaq 0xe8(%rsp), %rdi
callq 0x5e380
movl 0x1dc(%rsp), %eax
movl %eax, 0xf8(%rsp)
movq 0x1d0(%rsp), %rax
movq 0x10(%rax), %rax
movl 0x1dc(%rsp), %ecx
shlq $0x6, %rcx
addq %rcx, %rax
movq %rax, 0xe0(%rsp)
movq 0xe0(%rsp), %rax
cmpl $0x0, 0x4(%rax)
je 0x83bb4
movq 0xe0(%rsp), %rax
cmpl $0x0, 0xc(%rax)
jne 0x83bb9
jmp 0x840fb
movq 0xa8(%rsp), %rax
movq 0x38(%rax), %rdi
leaq 0xe8(%rsp), %rsi
callq 0x7cc90
movq 0x1d0(%rsp), %rdi
addq $0xb0, %rdi
leaq 0x1dc(%rsp), %rsi
callq 0x5e630
movq %rax, %rcx
movq 0xa8(%rsp), %rax
movq %rcx, 0xd0(%rsp)
movb %dl, 0xd8(%rsp)
movq 0x30(%rax), %rdi
movq (%rdi), %rax
leaq 0xe8(%rsp), %rsi
callq *0x98(%rax)
movq 0xa8(%rsp), %rax
movq 0x38(%rax), %rdi
leaq 0xe8(%rsp), %rsi
callq 0x7cc00
testb $0x1, %al
jne 0x83c3f
jmp 0x840f9
movq 0xa8(%rsp), %rax
leaq 0x57bce(%rip), %rcx     # 0xdb81c
movl (%rcx), %edx
incl %edx
movl %edx, (%rcx)
movq 0x40(%rax), %rdi
callq 0x59940
movl $0x98, %edi
callq 0x8450
movq %rax, %rdi
movq %rdi, %rax
movq %rax, 0x70(%rsp)
movq 0x1d0(%rsp), %rax
movq 0x10(%rax), %rax
movl 0x1dc(%rsp), %ecx
shlq $0x6, %rcx
movzwl 0x20(%rax,%rcx), %edx
movl $0x2, %esi
leaq 0xe8(%rsp), %rcx
callq 0x95e40
jmp 0x83ca2
movq 0x70(%rsp), %rax
movq %rax, 0xc8(%rsp)
movq 0xe0(%rsp), %rax
movl 0x4(%rax), %eax
movq 0xe0(%rsp), %rcx
subl 0xc(%rcx), %eax
cmpl $0x0, %eax
jbe 0x840ba
movq $0x0, 0xc0(%rsp)
movq $0x0, 0xb8(%rsp)
movl $0x0, 0xb4(%rsp)
movl 0xb4(%rsp), %eax
movq 0xe0(%rsp), %rcx
cmpl 0x4(%rcx), %eax
jae 0x840b8
movq 0xa8(%rsp), %rax
movq 0x38(%rax), %rdi
movq 0xe0(%rsp), %rsi
movl 0xb4(%rsp), %edx
callq 0x7cfa0
testb $0x1, %al
jne 0x83d32
jmp 0x840a0
movq 0xa8(%rsp), %rax
leaq 0x57edf(%rip), %rcx     # 0xdbc20
movl (%rcx), %edx
addl $0x1, %edx
leaq 0x57ed3(%rip), %rcx     # 0xdbc20
movl %edx, (%rcx)
movl 0xb4(%rsp), %ecx
movl %ecx, 0xfc(%rsp)
testb $0x1, 0x64(%rax)
je 0x83e94
movl $0xb0, %edi
callq 0x8450
movq %rax, %rcx
movq 0xa8(%rsp), %rax
movq %rcx, 0x58(%rsp)
movq 0xe0(%rsp), %rcx
movw 0x20(%rcx), %cx
movw %cx, 0x62(%rsp)
movl 0x10c(%rax), %ecx
shll $0x9, %ecx
movl %ecx, 0x64(%rsp)
movq 0x30(%rax), %rdi
movq (%rdi), %rax
movq 0x90(%rax), %rax
leaq 0xe8(%rsp), %rsi
callq *%rax
movq %rax, 0x68(%rsp)
jmp 0x83dbe
movq 0x68(%rsp), %r9
movl 0x64(%rsp), %ecx
movq 0x58(%rsp), %rdi
movw 0x62(%rsp), %ax
movq %rsp, %rdx
movq $-0x1, 0x20(%rdx)
movq $0x0, 0x18(%rdx)
movq $0x0, 0x10(%rdx)
movq $0x0, 0x8(%rdx)
movq $0x0, (%rdx)
movzwl %ax, %edx
movl $0x2, %esi
movq $-0x1, %r8
callq 0x96320
jmp 0x83e11
movq 0xa8(%rsp), %rax
movq 0x58(%rsp), %rcx
movq %rcx, 0xb8(%rsp)
movq 0xb8(%rsp), %rcx
movl $0x1, 0xa8(%rcx)
movq 0x40(%rax), %rdi
movq 0xb8(%rsp), %rsi
callq 0x59980
jmp 0x84084
movq 0x70(%rsp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x170(%rsp)
movl %eax, 0x16c(%rsp)
callq 0x8410
jmp 0x84103
movq 0x58(%rsp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x170(%rsp)
movl %eax, 0x16c(%rsp)
callq 0x8410
jmp 0x84103
movl $0xa0, %edi
callq 0x8450
movq %rax, %rcx
movq 0xa8(%rsp), %rax
movq %rcx, 0x40(%rsp)
movq 0xe0(%rsp), %rcx
movw 0x20(%rcx), %cx
movw %cx, 0x4a(%rsp)
movl 0x10c(%rax), %ecx
shll $0x9, %ecx
movl %ecx, 0x4c(%rsp)
movq 0x30(%rax), %rdi
movq (%rdi), %rax
movq 0x90(%rax), %rax
leaq 0xe8(%rsp), %rsi
callq *%rax
movq %rax, 0x50(%rsp)
jmp 0x83eeb
movq 0x50(%rsp), %r9
movl 0x4c(%rsp), %ecx
movq 0x40(%rsp), %rdi
movw 0x4a(%rsp), %ax
movq %rsp, %rdx
leaq 0xe8(%rsp), %rsi
movq %rsi, (%rdx)
movq $-0x1, 0x28(%rdx)
movq $0x0, 0x20(%rdx)
movq $0x0, 0x18(%rdx)
movq $0x0, 0x10(%rdx)
movq $0x0, 0x8(%rdx)
movzwl %ax, %edx
movl $0x2, %esi
movq $-0x1, %r8
callq 0x95fe0
jmp 0x83f4a
movq 0x40(%rsp), %rax
movq %rax, 0xc0(%rsp)
movl $0xb0, %edi
callq 0x8450
movq %rax, %rdi
movq 0xa8(%rsp), %rax
movq %rdi, %rcx
movq %rcx, 0x38(%rsp)
movq 0xe0(%rsp), %rcx
movzwl 0x20(%rcx), %edx
movl 0x10c(%rax), %ecx
shll $0x9, %ecx
movq 0xc0(%rsp), %rsi
movq %rsp, %rax
movq %rsi, 0x18(%rax)
leaq 0xe8(%rsp), %rsi
movq %rsi, (%rax)
movq $-0x1, 0x28(%rax)
movq $0x0, 0x20(%rax)
movq $0x0, 0x10(%rax)
movq $0x0, 0x8(%rax)
movl $0x2, %esi
movq $-0x1, %r9
movq %r9, %r8
callq 0x96240
jmp 0x83fd9
movq 0xa8(%rsp), %rax
movq 0x38(%rsp), %rcx
movq %rcx, 0xb8(%rsp)
movq 0xb8(%rsp), %rcx
movl $0x0, 0xa8(%rcx)
movq 0xc8(%rsp), %rdx
movq 0xb8(%rsp), %rcx
movq %rdx, 0x90(%rcx)
movq 0xb8(%rsp), %rdx
movq 0xc0(%rsp), %rcx
movq %rdx, 0x88(%rcx)
movq 0x40(%rax), %rdi
movq 0xc0(%rsp), %rsi
callq 0x59980
jmp 0x84084
movq 0x40(%rsp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x170(%rsp)
movl %eax, 0x16c(%rsp)
callq 0x8410
jmp 0x84103
movq 0x38(%rsp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, 0x170(%rsp)
movl %eax, 0x16c(%rsp)
callq 0x8410
jmp 0x84103
movq 0xc8(%rsp), %rdi
addq $0x80, %rdi
leaq 0xb8(%rsp), %rsi
callq 0x81ce0
jmp 0x840a2
movl 0xb4(%rsp), %eax
addl $0x1, %eax
movl %eax, 0xb4(%rsp)
jmp 0x83cf1
jmp 0x840ba
movq 0xa8(%rsp), %rax
movq 0xc8(%rsp), %rdx
movq 0xe0(%rsp), %rcx
movq %rdx, 0x28(%rcx)
movq 0x40(%rax), %rdi
movq 0xc8(%rsp), %rsi
callq 0x59980
movq 0xa8(%rsp), %rax
movq 0x40(%rax), %rdi
movq (%rdi), %rax
callq *0x30(%rax)
jmp 0x840fb
addq $0x1f8, %rsp            # imm = 0x1F8
retq
movq 0x170(%rsp), %rdi
callq 0x8770