U23 2008/Gruppe3/Scheduler
So, ich habe schonmal mit dem scheduler angefangen. bis jetzt speichert der alle wichtigen daten des ersten threads in eine instanz (thread1_head) unserer Struktur. thread_number ist einfach ein int8 in dem die nummer des bis gerade gelaufenen threads gespeichert wird.
ist bis jetzt mein erster entwurf, deswegen gibts noch keine subs fuers kopieren der register.
ich frage mich, ob die schleife fuer das kopieren der register sinnvoll ist. wuerde man einfach fuer jedes register einen sts befehl schreiben, kaeme man auf 32 befehle und viel einfacheren code.
ausserdem ist mir aufgefallen, dass wir wahrscheinlich noch einen int in unserer strutur brauchen, in der wir die groesse des stacks speichern, ansonsten wissen wir ja nicht, wie gross der stack vor dem abspeichern war. oder gibts ne moeglichkeit, das im nachhinein rauszufinden?
naja, meldet euch mal mit verkuerzungen und verbesserungen und ideen ;)
hier ist der code der Scheduler.S: <source lang="asm">
- define __SFR_OFFSET 0
- include <avr/io.h>
.global scheduler_init, TIMER1_COMPA_vect .set sreg_offset,0 .set reg_offset,1 .set stack_offset,33 .set return_address_offset,133
scheduler_init:
/* initialize timer1, prescaler 1024, CTC */ ldi r16, _BV(CS12) | _BV(CS10) | _BV(WGM12) sts TCCR1B, r16
/* timeout after ~10ms=(1/20,000,000)*1024*200 */ ldi r16, hi8(200) sts OCR1AH, r16 ldi r16, lo8(200) sts OCR1AL, r16 /* enable timer1 COMPA interrupt */ ldi r16, _BV(OCIE1A) sts TIMSK1, r16
/* enable interrupts */ sei
TIMER1_COMPA_vect:
//zwei register fuer die bestimmung des threads frei machen push r16 push r17
lds r16, thread_number ldi r17, 1
cpse r16, r17 rjmp thread2
thread1: //statusregister sichern in r16, SREG sts thread1_swap+sreg_offset, r16
//working register sichern pop r17 pop r16
// ein paar register fuer pointer und kopieroperation frei machen push r31 push r30 push r29 push r28 push r27 ldi r31, 0 //adresse des ersten registers ins z register laden ldi r30, 0
ldi r29, hi8(thread1_swap+reg_offset) //adresse des swap ins y register laden ldi r28, lo8(thread1_swap+reg_offset) register_loop1: ld r27, Z+ //wert des aktuellen registers in r27 laden und den pointer inkrementieren st Y+, r27 //wert von r27 in den ram an die adresse des swaps schreiben und den pointer inkrementieren
CPSE r30, 26 //wenn wir bei register 26 angekommen sind aufhoeren rjmp register_loop1 pop r27 //jetzt muessen noch die register, die auf dem stack liegen in den swap pop r28 pop r29 pop r30 pop r31 sts thread1_swap+reg_offset+27, r27 sts thread1_swap+reg_offset+28, r28 sts thread1_swap+reg_offset+29, r29 sts thread1_swap+reg_offset+30, r30 sts thread1_swap+reg_offset+31, r31 //bevor wir jetzt den stack leeren, muessen wir noch die return adresse vom stack poppen ldi r28, lo8(thread1_swap+return_address_offset) //adresse des swap ins y register laden ldi r29, hi8(thread1_swap+return_address_offset) pop r17 //hi8 der return adresse laden pop r16 //lo8... sts Y+, r17 //zuerst hi8 schreiebn (in c haben wir ein int16, da kommt das highbyte zuerst sts Y, r16 ldi r24, lo8(RAMEND) //ramend laden (damit wir wissen, wo der stack angefangen hat) ldo r25, hi8(RAMEND) ldi r28, lo8(thread1_swap+stack_offset) //adresse des swap ins y register laden ldi r29, hi8(thread1_swap+stack_offset) stack_loop1: pop r16 st Y+, r16
lds r30, SPL //rausfinden, wo der stackpointer ist, lds r31, SPH //damit wir aufhoren koennen, wenn wir cp r30, r24 //am anfang des stacks angekommen sind cpc r31, r25 breq stack_loop1_done rjmp stack_loop1 stack_loop1_done: //alle daten des bis gerade ausgefuehrten threads sind gesichert, //sodass wir jetzt die umgebung des zweiten threads laden koennen reti
thread2: pop r17 pop r16
reti
</source>