Knuppel in het "C" hoenderhok => Goto/Return

Frederick E. Terman

Honourable Member

Het verbieden van goto omdat je die ook op een slechte manier kunt gebruiken, lijkt me nogal bedillerig. Ik weet zeker dat er hier een storm zou opsteken als het bijvoorbeeld verboden zou worden messen in huis te hebben, of een auto te hebben die sneller kan dan de maximumsnelheid in je straat, omdat je ook met die dingen ongelukken zou kunnen maken.
Ook zonder goto moet je liever geen spaghetti maken.

Op een 65xx-cursus (die, nu ik eraan terugdenk, in Singapore was, maar dat terzijde) heb ik eens - met de zelfverzekerde eigenwijsheid die bij mijn leeftijd toen paste - een stiekeme goto gebruikt. Ik pushte het beoogde sprongadres op de stack en gaf daarna een return. Het effect: de program counter wordt geladen met het adres op de stack en de processor springt 'terug' naar de nieuwe plaats.
Dat leverde me a) een pluim op 'humoristisch en toch echt werkend', en b) een uitbrander, want 'ik hoefde gr**vr* heus niet te denken dat ik de eerste cursist was die dat bedacht'. :)

Keramisch, kalibratie, parasitair: woordenlijst.org

Afraden en verbieden zijn wel 2 verschillende zaken natuurlijk. ;)

Je kunt uit een opamp ook de maximale stroom halen, maar is het aan te raden, nee. Want... consequenties.

PE2BAS
bprosman

Golden Member

De processor:
De CPU heeft een aantal registers. Deze registers staan dus niet in de RAM, maar zijn extra stukjes geheugen in de CPU zelf. Zoon register is niets anders dan een stukje geheugen waar je een waarde in kan opslaan. Een van deze registers is de Program counter. De Program counter of PC, Onthoud bij welke instructie de processor is gebleven. Dit verwijst naar een adres in de ROM. Bij iedere clockcycle wordt: (Ook weer een paar details weg gelaten.)
- Een instructie uitgelezen uit de ROM, Welke instructie wordt uitgelezen wordt bepaald door de program counter.
- De instructie uitgevoerd.
- De program counter wordt opgehoogd, zodat hij verwijst naar de volgende instructie.

Dus hoe werkt de GOTO? Nou je maakt gewoon een instructie waarbij je de program counter aanpast waar je naartoe wilt springen. Dit wordt in ASM ook wel de jump instructie genoemd. Hiermee spring je eenmalig naar een specifieke plek in de code. Een probleempje, je kan nu niet meer terug waar we vandaan kwamen. Dit is wel nodig om de functies zoals we die in C kennen te kunnen implementeren. Om dit probleem te verhelpen is de stack uitgevonden.

De stack:
De stack is een stukje geheugen dat geclaimd wordt in de RAM. De stack kan je zien als een stapel met borden. Je mag alleen borden bovenop de stack zetten of borden van de stack afhalen. Je kan nooit de middelste of onderste pakken, dan vallen alle borden kapot. De CPU zet natuurlijk geen borden op de stack. Wat hij er wel opzet zijn waarden die in de registers staan. De ASM instructies die hierbij horen worden vaak PUSH en POP genoemd. Push om iets op de stack te zetten en pop om er iets af te halen. Dus hoe kan je nu springen naar en terug springen van een functie? Simpel, Wanneer je een jump doet, zet je de program counter op de stack. Als je weer terug wilt haal je deze waarde weer van de stack en schrijft dit weer naar de program counter.

Iedere keer dat je naar een functie springt onthoud hij dus waar je vandaan bent gekomen. Als je nu 3x achter elkaar springt, dan staan er dus 3 waardes op de stack. Zoals je begrijpt is de stack niet oneindig groot. Je kunt dus maar een beperkt aantal sprongen maken. Als je het te bont maakt dan krijg je een 'stack overflow' error. Letterlijk, de stack is overstroomt.

Omdat dit zo werkt ben ik het niet helemaal eens met de vergelijking van BFM, de "Chef" verlaat wel degelijk zijn plaats (PC word veranderd) en laat een marker (op stack) waar hij terug moet keren. Een single CPU heeft maat 1 "Chef" en geen assistenten.

b) een uitbrander, want 'ik hoefde gr**vr* heus niet te denken dat ik de eerste cursist was die dat bedacht'.

In mijn laatste cursus alleen maar meewarige blikken toen ik vroeg waarom de "I D L E" (uitgesproken als IT-LE) taak zoveel tijd nam ;)

De jongere generatie loopt veel te vaak zijn PIC achterna.
Hoeben

Golden Member

Op 22 juni 2022 16:53:37 schreef bprosman:
[...]In mijn laatste cursus alleen maar meewarige blikken toen ik vroeg waarom de "I D L E" (uitgesproken als IT-LE) taak zoveel tijd nam ;)

Grappig. Zie hier dat bij mijn CPU met een flinke belasting dit toch veroorzaakt wordt door het IDLE proces.

Waarom Microsoft ooit een idle proces heeft verzonnen? Ze hadden veel beter in die procestijd niets kunnen doen...

Een OS heeft altijd minstens 1 proces aktief. Bij gebrek aan nuttige processen wordt dat het idle process. Als het goed is doet dat niet veel anders dan slapen.

...maar daarmee neemt het nog steeds meer cpu-tijd op dan alle andere processen want er zijn nauwelijks andere processen. Nix aan de hand, dus.
Het interpreteren van dit soort informatie is een heel aparte kunst, en af en toe is het heel contra-intuitief.

hoe beter de vraag geschreven, zoveel te meer kans op goed antwoord

Op een 65xx-cursus (die, nu ik eraan terugdenk, in Singapore was, maar dat terzijde) heb ik eens - met de zelfverzekerde eigenwijsheid die bij mijn leeftijd toen paste - een stiekeme goto gebruikt. Ik pushte het beoogde sprongadres op de stack en gaf daarna een return.

Dat heb ik ooit gezien als implementatie van wat in C een "case" is. tZou me niet verbazen als het in de Apple ][ monitor was. Of misschien zelfs in de Basic interpreter die er later werd bijgefrot, met indrukwekkende trucjes om tot de laatste byte te besparen.

hoe beter de vraag geschreven, zoveel te meer kans op goed antwoord
Hoeben

Golden Member

Die processor heeft hier ca 2 miljard instructies per seconde nodig om niets te doen.

De PDP8 of PDP11 had ook een taakje idle. Die besteedde er ook best veel tijd aan.
Af en toe zag je hearts langskomen en uiteraard andere taken.

Bezoek mijn neefjes' site: www.tinuselectronics.nl

Op 22 juni 2022 16:53:37 schreef bprosman:
In mijn laatste cursus alleen maar meewarige blikken toen ik vroeg waarom de "I D L E" (uitgesproken als IT-LE) taak zoveel tijd nam ;)

Ondertussen vraag ik me af waarom het idle-process 8K geheugen nodig heeft. Veel meer dan:

c code:


while (1);

hoeft het niet te zijn.

(Linux gebruikt (indien beschikbaar) de HLT of MWAIT instructie van de CPU om de CPU effectief stil te zetten (en power saving te laten werken) totdat er een interupt komt. Waarschijnlijk doet Windows ook zoiets, maar daar heb ik geen source van. Hoe dan ook, het is geen 8K aan code!)

@BFM hieronder: x86 systemen alloceren geheugen in 4K blokken.

Op 22 juni 2022 20:24:29 schreef Hoeben:
Die processor heeft hier ca 2 miljard instructies per seconde nodig om niets te doen.

Check: is er iets te doen?
zoja: doe het
zoniet: doe niets
check again
Dat kan men gemakkelijk enkele miljard keren per seconde uitvoeren, jawel. Echt nix aan de hand!

Ondertussen vraag ik me af waarom het idle-process 8K geheugen nodig heeft.

Omdat geheugen wordt gealloceerd in blokken van 8K, wie weet?

[Bericht gewijzigd door big_fat_mama op 22 juni 2022 21:03:30 (18%)]

hoe beter de vraag geschreven, zoveel te meer kans op goed antwoord

Nou, die check (busy waiting, looping) doet hij dus tegenwoordig niet of nauwelijks meer, want dat is een energievreter. Ik heb me nooit precies in het power management van moderne OS'sen en processordrivers verdiept, maar ik neem aan dat men hoofdzakelijk timers en interrupts gebruikt.

EDIT: Wat blurp hierboven dus al zegt, onderstaand en bovenstaand had ik al geschreven voordat ik dat las, en de strekking komt overeen.

Op 22 juni 2022 19:38:57 schreef big_fat_mama:
...maar daarmee neemt het nog steeds meer cpu-tijd op dan alle andere processen want er zijn nauwelijks andere processen. Nix aan de hand, dus.
Het interpreteren van dit soort informatie is een heel aparte kunst, en af en toe is het heel contra-intuitief.

De idle tijd is vergelijkbaar met de tijdmeting op de trip-computer van een auto als je voor het stoplicht staat of op de koppeling filerijdt. De motor draait alleen stationair dus verbruikt niet of nauwelijks meer dan de basis, maar de klok loopt wel door. Vergelijk: de processor klokt terug en pauzeert cores. Duurt even lang als wanneer hij gedurende die tijd instructies zou uitvoeren, maar in alle moderne Windowsversies, in elk geval alle nazaten van Windows NT, doet hij zoveel mogelijk echt niks, in plaats van in lussen rond te lopen (met een goto >:)).

Overigens wel misleidend: de idle times van de cores worden bij elkaar opgeteld. Met een quad core kun je dus zomaar een half uur idle tijd hebben op een computer die pas 10 minuten aanstaat.

www.elba-elektro.nl | "The mind is a funny thing. Sometimes it needs a good whack on the side of the head to jar things loose."
PE9SMS

Golden Member

Op 21 juni 2022 14:47:31 schreef hardbass:
Dit idee van deze statemachines gebruik ik op veel plekken. Wat ook mooi is, je kan de state switches loggen. Op die manier zie je erg goed wat er in je programma gebeurt. Ik heb een los tooltje waarmee ik de states kan weergeven over tijd. (Een soort logic analyser beeld) Dit geeft enorm veel inzicht wanneer je states veranderen en hoelang je in een bepaalde state zit. Erg praktisch als je meerdere state machines krijgt en er gaat 'iets' niet zoals verwacht.

Dit klinkt interessant, kun je hier iets meer over vertellen hardbass? Over welke mcu gaat dit, hoe log je de states en wat is dat voor een visualisatie tooltje? Dank vast.

This signature is intentionally left blank.

(Ik heet wel geen hardbass maar toch: )

Ik had onder chibios een "menu" syteempje gemaakt.

code:


do_menu (struct menu * menu)
{
while (1) {
  wacht op toets
  if (toets_recht) volgende menu item. 
  if (toets_links) vorige menu item. 
  if (toets_up/back) return;
  if (toets_ok) voer actie uit van huidige item. 
}

de "actie" kan dus ook weer een do_menu zijn!

Nu met een andere processor... Geen chibios support -> Geen taken -> dit moet anders.

Dus nu een statemachine die in 1 state wacht op een toets ingedrukt wordt, dan de actie bij die toets uitvoert dan naar de state gaat waar ie wacht totdat de toets losgelaten is enz.

In iedere state doet ie snel wat ie moet doen (toets ingedrukt? Nee? return! ) zodat er daarnaast gescand kan worden of er nog andere dingen te doen zijn.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
PE9SMS

Golden Member

Ja, het principe van een statemachine is me duidelijk, gebruik dat zelf ook regelmatig. Maar specifiek dat loggen van de states, snap dat dat erg handig kan zijn met debuggen en dergelijke. Maar mijn fantasie komt even niet verder dan een watch op je state enum variabele zetten, maar of ik dat tegen tijd kan loggen in bijvoorbeeld Atmel Studio? Andere manier is de waarde van de state enum op een GPIO port zetten en dan met een logic analyser of scope kijken.

This signature is intentionally left blank.

Je kunt in die code eenvoudig een string naar een seriële poort of zo sturen om aan te geven dat je naar een andere state bent gegaan.

Ik heb die code nu pas bekeken, en ik heb er eigenlijk wel het e.e.a. op aan te merken; "Waitforstable" kijkt helemaal niet naar de stabiliteit, alleen voor een sample boven de grens. Zelfs als hij dat wel zou doen, pak je daarna een nieuw sample om op te slaan; die is dus niet perse stabiel.

Zelf zou ik in de functie "TakeSample" de variatie tussen de samples bepaald hebben (verschil tussen de hoogste en laagste, of de som van de absolute afwijking van het gemiddelde), dan kun je in één if statement bepalen of je boven de grenswaarde en onder de grens voor stabiliteit zit, en heb je die hele state machine niet nodig.

Waar het voorbeeld uiteindelijk om ging, hoe ziet zo'n state machine eruit, en op een centrale plaats de vorige en volgende state bijhouden, en de "OnEntry" vlag, is m.i. prima. Wat ik daar zelf meestal aan toevoeg, is het opslaan van de tijd van de laatste transitie; je kunt dan triviaal timeouts regelen (als je te lang in een bepaalde state zit), zonder overal losse timertjes voor te starten.

Een manager is iemand die denkt dat negen vrouwen in één maand een kind kunnen maken

Goeie opmerkingen Sparky, de weegschaal zelf is een work in progress dingetje. Je opmerkingen ga ik zeker meenemen al heb ik momenteel even andere projecten die voorrang vragen. Het idee om de tijd bij te houden van de laatste transitie is een mooie toevoeging voor de state machine. Die zal ik nog wel eens toevoegen wanneer ik er weer mee bezig ben.

De logging van de states en het weergeven daarvan is onderdeel van software die ik voor de zaak heb gemaakt. Samen met mijn collega's uiteraard. Die log wordt opgeslagen in flash en uiteindelijk doorgestuurd naar een MySQL database. Deze code kan ik uiteraard niet delen aangezien dit niet van mij is. :) De tool om de logs weer te geven is wel van mij privé, maar deze is aangepast zodat ik deze voor het werk kan gebruiken. Dus ook die kan ik niet zomaar vrij geven.

Ik was al begonnen om een nieuwe log viewer tool te maken. Gebaseerd op de basis van mijzelf. Mijn idee is om het opslaan van de logs en de MySQL achterwegen te laten en de logs direct te versturen via TCP of 232. Dan kan hij realtime de veranderingen weergeven. Dit is voor mijn hobby projecten ook meer dan genoeg. Hier ben ik toevallig vandaag nog mee bezig geweest. Het is allemaal nog niet af. Sterker nog, het is alleen nog maar een klein testje en nog te 'vers' om te delen, maar je kan de projecten hier volgen mocht je interesse hebben:

C# .NET-framework
https://github.com/vanBassum/TraceDebugger

ESP-IDF 4.4
https://github.com/vanBassum/TraceDebuggerESP

Wellicht overbodig, maar toch, Dit zijn hobbie projecten van mijzelf. Dus ik besteed geen aandacht aan zaken als documentatie. Als je dit wilt toepassen in je eigen project dan zal dit nog wel wat aandacht vereisen. Wellicht is het een idee om hier wat losse tools en bibliotheken omheen te maken. Dan kan iedereen het gebruiken. :)

PE2BAS

Op 24 juni 2022 13:40:39 schreef PE9SMS:
Ja, het principe van een statemachine is me duidelijk, gebruik dat zelf ook regelmatig. Maar specifiek dat loggen van de states, snap dat dat erg handig kan zijn met debuggen en dergelijke.

Oh! Ook dat doe ik wel eens.

Gewoon aan het begin van de grote state switcher:

code:


{ 
  static int oldstate;
  char *statenames[] = {"IDLE", "..." ... };

  if (oldstate != state) logprint (changed state from %s -> %s\n", 
    statenames[oldstate], statenames[state]);

  oldstate = state;
}
four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
KGE

Golden Member

Op 22 juni 2022 15:29:59 schreef blurp:
C kent ook functie-pointers (oftewel, adressen van instructies).
Daarmee kun je goto, computed goto, functie-tabellen en interupt vector tabellen maken.

En state machines dus.. Heb ze wel gebruikt in VGA code op een mbed LPC1768 e.a.