U23 2008/Gruppe3/Scheduler

From C4 Wiki
Jump to: navigation, search

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">

  1. define __SFR_OFFSET 0
  2. 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>