Computer Architecture and Assembly Language Practical Session 7
Computer Architecture and Assembly Language Practical Session 7
Co-Routines • co-routines are assembly implementation of threads • each co-routine decides to which co-routine to pass a control (unlike “usual” threads) We would implement “silly” round robin scheduling algorithm: main() scheduler co-routine 1 scheduler co-routine 2 scheduler main()
Co-routine state • • co-routine is denoted by Coi (i is co-routine’s id) scheduler is also a co-routine suspends itself after some “time slice” co-routine resumes a scheduler • co-routine should save its current state before it suspends itself (in order to continue its execution later) • • • stack state registers flags stack pointer (ESP) instructions pointer (EIP)
Co-routine structure We define an array of co-routines’ structures: why SPi points to the end of stack ? to be able to use push and pop stack functionality with ESP Co-routine structure: STKSIZE equ 16*1024 STKi: resb STKSIZE ; (16 Kb) COi: Flags: SPi: ; pointer to co-routine function ; 0 if co-routine is not initialized, 1 otherwise ; pointer to the beginning of co-routine stack Funci dd Functioni dd 0 dd STKi + STKSIZE 0 co-routine structure SPi co-routine stack
STKSZ equ CODEP equ FLAGSP equ SPP equ section. rodata align 16 global numco: dd CORS: dd dd dd section. data align 16 CO 1: dd Flags 1: dd SP 1: dd CO 2: dd Flags 2: dd SP 2: dd CO 3: dd Flags 3: dd SP 3: dd section. bss align 16 CURR: resd SPT: resd SPMAIN: resd STK 1: resb STK 2: resb STK 3: resb 16*1024 0 4 8 ; co-routine stack size ; offset of pointer to co-routine function in co-routine structure ; offset of pointer to flags co-routine structure ; offset of pointer to co-routine stack in co-routine structure numco 3 CO 1 CO 2 CO 3 Function 1 0 STK 1+STKSZ Function 1 0 STK 2+STKSZ Function 2 0 STK 3+STKSZ 1 1 1 STKSZ Variables Declaration ; structure for first co-routine ; structure for second co-routine ; structure for third co-routine ; temporary stack pointer variable ; stack pointer of main
Run example – data declaration. bss . data CURR 3 SPT CO 1 SPMAIN CO 2 STK 1 CO 3 Function 1 0 numco CORS CO 1 Flags 1 SP 1 STK 2 Function 1 0 CO 2 Flags 2 SP 2 Function 2 0 CO 3 Flags 3 SP 3 STK 3 0 COUNTER 3 MAX_ITER
Co-routine initialization - save initial co-routine state init_co_from_c: … mov ebx, [ebp+8] mov ebx, [4*ebx + CORS] call co_init … ; get co-routine ID number ; get COi pointer co_init: … mov eax, [ebx+CODEP] … mov esp, [EBX+SPP] … push eax pushfd pushad mov [ebx+SPP], esp … Funci 0 co-routine structure main() { /* initialize co-routines*/ for(i = 0; i < numco; i++) init_co_from_c(i); /* start a scheduler co-routine*/ start_co_from_c (2); } ; get initial PC ; get initial SP ; push initial “return” address ; push flags ; push all other registers ; save new SPi value (after all the pushes) SPi co-routine stack
Co-routine initialization - save initial co-routine state init_co_from_c: … mov ebx, [ebp+8] mov ebx, [4*ebx + CORS] call co_init … ; get co-routine ID number ; get COi pointer co_init: … mov eax, [ebx+CODEP] … mov esp, [EBX+SPP] … push eax pushfd pushad mov [ebx+SPP], esp … Funci 0 co-routine structure main() { /* initialize co-routines*/ for(i = 0; i < numco; i++) init_co_from_c(i); /* start a scheduler co-routine*/ start_co_from_c (2); } ; get initial PC ; get initial SP ; push initial “return” address ; push flags ; push all other registers ; save new SPi value (after all the pushes) ESP SPi co-routine stack
Co-routine initialization - save initial co-routine state init_co_from_c: … mov ebx, [ebp+8] mov ebx, [4*ebx + CORS] call co_init … ; get co-routine ID number ; get COi pointer co_init: … mov eax, [ebx+CODEP] … mov esp, [EBX+SPP] … push eax pushfd pushad mov [ebx+SPP], esp … Funci 0 co-routine structure main() { /* initialize co-routines*/ for(i = 0; i < numco; i++) init_co_from_c(i); /* start a scheduler co-routine*/ start_co_from_c (2); } ; get initial PC ; get initial SP ; push initial “return” address ; push flags ; push all other registers ; save new SPi value (after all the pushes) ESP SPi Funci co-routine stack
Co-routine initialization - save initial co-routine state init_co_from_c: … mov ebx, [ebp+8] mov ebx, [4*ebx + CORS] call co_init … ; get co-routine ID number ; get COi pointer co_init: … mov eax, [ebx+CODEP] … mov esp, [EBX+SPP] … push eax pushfd pushad mov [ebx+SPP], esp … Funci 0 co-routine structure SPi main() { /* initialize co-routines*/ for(i = 0; i < numco; i++) init_co_from_c(i); /* start a scheduler co-routine*/ start_co_from_c (2); } ; get initial PC ; get initial SP ; push initial “return” address ; push flags ; push all other registers ; save new SPi value (after all the pushes) ESP EFlags co-routine stack Funci
Co-routine initialization - save initial co-routine state init_co_from_c: … mov ebx, [ebp+8] mov ebx, [4*ebx + CORS] call co_init … ; get co-routine ID number ; get COi pointer co_init: … mov eax, [ebx+CODEP] … mov esp, [EBX+SPP] … push eax pushfd pushad mov [ebx+SPP], esp … Funci 0 co-routine structure SPi main() { /* initialize co-routines*/ for(i = 0; i < numco; i++) init_co_from_c(i); /* start a scheduler co-routine*/ start_co_from_c (2); } ; get initial PC ; get initial SP ; push initial “return” address ; push flags ; push all other registers ; save new SPi value (after all the pushes) ESP registers EFlags co-routine stack Funci
Co-routine initialization - save initial co-routine state init_co_from_c: … mov ebx, [ebp+8] mov ebx, [4*ebx + CORS] call co_init … ; get co-routine ID number ; get COi pointer co_init: … mov eax, [ebx+CODEP] … mov esp, [EBX+SPP] … push eax pushfd pushad mov [ebx+SPP], esp … Funci 1 co-routine structure SPi main() { /* initialize co-routines*/ for(i = 0; i < numco; i++) init_co_from_c(i); /* start a scheduler co-routine*/ start_co_from_c (2); } ; get initial PC ; get initial SP ; push initial “return” address ; push flags ; push all other registers ; save new SPi value (after all the pushes) ESP registers EFlags co-routine stack Funci
After co-routine initialization. bss . data CURR 3 SPT CO 1 SPMAIN CO 2 STK 1 CO 3 Function 1 Registers 1 Flags STK 2 Function 1 1 Function 2 Flags 1 Function 1 Flags 1 CO 2 Flags 2 CO 3 Flags 3 SP 3 STK 3 Function 2 CO 1 SP 2 Registers Flags CORS SP 1 Function 1 Registers numco 0 COUNTER 3 MAX_ITER
Co-routine initialization section. text align 16 extern global init_co_from_c: push mov mov call pop ret co_init: pushad bts jc mov mov pushfd pushad mov init_done: popad ret printf init_co_from_c start_co_from_c end_co EBP, ESP EBX, [EBP+8] EBX, [EBX*4+CORS] co_init EBX EBP dword [EBX+FLAGSP], 0 init_done EAX, [EBX+CODEP] [SPT], ESP, [EBX+SPP] EBP, ESP EAX [EBX+SPP], ESP, [SPT] ; EBX contains a number of the co-routine to be initialized ; EBX contains a pointer to co-routine structure to be initialized ; test if already initialized ; get initial PC ; save original SP ; get initial SP ; also use as EBP ; push initial "return" address (initial PC) ; push flags ; push all other registers ; save new SP in structure ; restore original SP ‘bts’ instruction tests one bit of its first operand, whose index is given by the second operand, and stores the value of that bit in the carry flag (CF). In addition, ‘bts’ sets the bit to be 1.
Start co-routine scheduler We start scheduling by suspending main() and resuming a scheduler co-routine. start_co_from_c: push EBP mov EBP, ESP pushad mov [SPMAIN], ESP mov EBX, [EBP+8] mov EBX, [EBX*4 + CORS] jmp do_resume main() { /* initialize co-routines*/ for(i = 0; i < numco; i++) init_co_from_c(i); /* start a scheduler co-routine*/ start_co_from_c (2); } ; save ESP of main () ; gets ID number of a scheduler ; gets a pointer to a scheduler structure ; resume a scheduler co-routine We end scheduling by going back to main(). end_co: mov ESP, [SPMAIN] popad pop EBP ret ; restore state of main code
Scheduler co-routine function Scheduler function is started by main. scheduler_Function: pick up some thread ID i mov EBX, [CORS + i*4] call resume ‘call resume’ • save a state of the current co-routine • resume a state of the next co-routine (EBX should contain a pointer to it) ; resumes Coi pick up some other thread ID j mov EBX, [CORS + j*4] ; resumes Coj call resume … jmp end_co ; resume main EBX is pointer to co-init structure of the co-routine to be resumed. CURR holds a pointer to co-init structure of the current co-routine. resume: do_resume: pushfd pushad mov mov popad popfd ret ; save state of caller EDX, [CURR] [EDX+SPP], ESP, [EBX+SPP] [CURR], EBX • after ‘call resume’ return address (i. e. EIP) is pushed automatically into (co-routine) stack • we only have to save EFLAGS, ESP, and registers ; save current SP ; load SP for resumed co-routine ; restore resumed co-routine state ; "return" to resumed co-routine!
Function 2 This function used as code for co-routine 3 (scheduler) FMT 2: db "Function 2, co %lx, called by %lx, pass %ld", 10, 0 Function 2: push call add mov call dword 1 dword [CORS] ; indeed, called by main dword [CURR] dword FMT 2 printf ESP, 16 EBX, [CORS] ; resume CO 1 resume push call add dword 2 dword [CORS] dword [CURR] dword FMT 2 printf ESP, 16 mov EBX, [CORS+4] call resume push dword 3 push dword [CORS+4] push dword [CURR] push dword FMT 2 call printf add ESP, 16 mov EBX, [CORS] call resume push dword 4 push dword [CORS] push dword [CURR] push dword FMT 2 call printf add ESP, 16 mov EBX, [CORS+4] call resume jmp end_co ; resume main ; resume CO 2 ; resume CO 1 ; resume CO 2
Function 1 This function used as code for co-routines 1 and 2 FMT 1: db "Function 1, co %lx, called by %lx, pass %ld", 10, 0 Function 1: push call add mov call dword 1 dword [CORS+8] dword [CURR] dword FMT 1 printf ESP, 16 EBX, [CORS+8] resume push call add mov call dword 2 dword [CORS+8] dword [CURR] dword FMT 1 printf ESP, 16 EBX, [CORS+8] resume ; resume a scheduler
Run example – data declaration. bss . data CURR 3 SPT CO 1 SPMAIN CO 2 STK 1 CO 3 Function 1 0 numco CORS CO 1 Flags 1 SP 1 STK 2 Function 1 0 CO 2 Flags 2 SP 2 Function 2 0 CO 3 Flags 3 SP 3 STK 3 0 COUNTER 3 MAX_ITER
After co-routine initialization. bss . data CURR 3 SPT CO 1 SPMAIN CO 2 STK 1 CO 3 Function 1 Registers 1 Flags STK 2 Function 1 1 Function 2 Flags 1 Function 1 Flags 1 CO 2 Flags 2 CO 3 Flags 3 SP 3 STK 3 Function 2 CO 1 SP 2 Registers Flags CORS SP 1 Function 1 Registers numco 0 COUNTER 3 MAX_ITER
Resuming - right before. bss . data resume: CO 2 CURR 3 SPT CO 1 SPMAIN CO 2 STK 1 CO 3 Function 1 Registers 1 Flags numco CORS do_resume: CO 1 Flags 1 SP 1 Addr 1 STK 2 Function 1 1 CO 2 Flags 2 SP 2 ESP Function 2 ………. 1 ………. Flags Addr 3 Flags 3 SP 3 STK 3 Registers CO 3 0 COUNTER 3 MAX_ITER pushfd pushad mov mov popad popfd ret EDX, [CURR] [EDX+SPP], ESP, [EBX+SPP] [CURR], EBX
Resuming – resume is called. bss . data resume: CO 2 CURR 3 SPT CO 1 SPMAIN CO 2 STK 1 CO 3 Function 1 Registers 1 Flags STK 2 Function 1 1 Function 2 ………. 1 ………. Addr 3 CO 1 Flags 1 CO 2 Flags 2 CO 3 Flags 3 SP 3 STK 3 Flags do_resume: SP 2 Addr 2 Registers CORS SP 1 Addr 1 ESP numco 0 COUNTER 3 MAX_ITER pushfd pushad mov mov popad popfd ret EDX, [CURR] [EDX+SPP], ESP, [EBX+SPP] [CURR], EBX
Resuming – backup registers. bss . data resume: CO 2 CURR 3 SPT CO 1 SPMAIN CO 2 STK 1 CO 3 Function 1 Registers 1 Flags ESP STK 2 Flags Function 1 1 Function 2 ………. 1 ………. Addr 3 CO 1 Flags 1 CO 2 Flags 2 CO 3 Flags 3 SP 3 STK 3 Flags do_resume: SP 2 Addr 2 Registers CORS SP 1 Addr 1 Registers numco 0 COUNTER 3 MAX_ITER pushfd pushad mov mov popad popfd ret EDX, [CURR] [EDX+SPP], ESP, [EBX+SPP] [CURR], EBX
Resuming – backup stack pointer. bss . data resume: CO 2 CURR 3 SPT CO 1 SPMAIN CO 2 STK 1 CO 3 Function 1 Registers 1 Flags ESP STK 2 Flags Function 1 1 Function 2 ………. 1 ………. Addr 3 CO 1 Flags 1 CO 2 Flags 2 CO 3 Flags 3 SP 3 STK 3 Flags do_resume: SP 2 Addr 2 Registers CORS SP 1 Addr 1 Registers numco 0 COUNTER 3 MAX_ITER pushfd pushad mov mov popad popfd ret EDX, [CURR] [EDX+SPP], ESP, [EBX+SPP] [CURR], EBX
Resuming - load stack pointer of resume co-routine. bss . data resume: CO 2 CURR 3 SPT CO 1 SPMAIN CO 2 STK 1 CO 3 Function 1 Registers 1 Flags STK 2 Flags Function 1 1 Function 2 ………. 1 ………. Flags Addr 3 CO 1 Flags 1 CO 2 Flags 2 CO 3 Flags 3 SP 3 STK 3 Registers do_resume: SP 2 Addr 2 ESP CORS SP 1 Addr 1 Registers numco 0 COUNTER 3 MAX_ITER pushfd pushad mov mov popad popfd ret EDX, [CURR] [EDX+SPP], ESP, [EBX+SPP] [CURR], EBX
Resuming – set current co-routine variable. bss . data resume: CO 3 CURR 3 SPT CO 1 SPMAIN CO 2 STK 1 CO 3 Function 1 Registers 1 Flags STK 2 Flags Function 1 1 Function 2 ………. 1 ………. Flags Addr 3 CO 1 Flags 1 CO 2 Flags 2 CO 3 Flags 3 SP 3 STK 3 Registers do_resume: SP 2 Addr 2 ESP CORS SP 1 Addr 1 Registers numco 0 COUNTER 3 MAX_ITER pushfd pushad mov mov popad popfd ret EDX, [CURR] [EDX+SPP], ESP, [EBX+SPP] [CURR], EBX
Resuming – restore a state of loaded co-routine. bss . data resume: CO 3 CURR 3 SPT CO 1 SPMAIN CO 2 STK 1 CO 3 Function 1 Registers 1 Flags STK 2 Flags Function 1 1 Function 2 ………. 1 ………. CO 1 Flags 1 CO 2 Flags 2 CO 3 Flags 3 SP 3 STK 3 Addr 3 do_resume: SP 2 Addr 2 ESP CORS SP 1 Addr 1 Registers numco 0 COUNTER 3 MAX_ITER pushfd pushad mov mov popad popfd ret EDX, [CURR] [EDX+SPP], ESP, [EBX+SPP] [CURR], EBX
Resuming – go to execute the loaded co-routine. bss . data resume: CO 3 CURR 3 SPT CO 1 SPMAIN CO 2 STK 1 CO 3 Function 1 Registers 1 Flags STK 2 Flags do_resume: CO 1 Flags 1 Function 1 1 CO 2 Flags 2 SP 2 Addr 2 Function 2 ………. 1 ………. CO 3 Flags 3 SP 3 STK 3 ESP CORS SP 1 Addr 1 Registers numco 0 COUNTER 3 MAX_ITER pushfd pushad mov mov popad popfd ret EDX, [CURR] [EDX+SPP], ESP, [EBX+SPP] [CURR], EBX
- Slides: 28