Commodore 64

Das Leben, das Universum und der ganze Rest.
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Commodore 64

Beitrag von DOSferatu »

Naja, byteweise. Indem man das Lowbyte der Adresse nach $FE und das Highbyte nach $FF schreibt. Einzeln halt. (Darf man die $FF überhaupt benutzen? Weiß grad nicht mehr.)
Zur Information betreffend "Speicher leeren". Nein, man muß den Speicher nicht "leeren". Es gibt keinen "leeren" Speicher. Speicher enthält immer Werte, aus Bits bestehend, die 0 oder 1 sein können. Es gibt keinen Bit-Zustand "leer".
Nach dem Einschalten hat der C64-Speicher einen zufälligen Zustand. Ältere Modelle haben da totales Chaos drin, neuere haben immer im Wechsel $00 und $FF gefüllt. Das liegt an der Hardware und ist konstruktionsbedingt.
Außerdem hält der C64 Speicher relativ lange. Selbst nach 30 Sekunden kann noch, wenn man Glück hat, der komplette Speicherinhalt erhalten sein nach dem nächsten Einschalten (abgesehen von den Speicherstellen natürlich, die er beim Einschalten initialisiert) und sogar nach einer Minute können noch Reste vorhanden sein.
Kleine Anmerkung: Die "Module" (also die Teile am Erweiterungs-Steckplatz aka "Expansion Port") blenden sich unter anderem im Bereich $8000 bis $9FFF ein und haben eine Kennung ab $8004, (CBM80). Davor stehen zwei Sprungvektoren für Hard-Reset und Soft-Reset. Das lustige ist, daß man das auch simulieren kann, indem man selbst in den Speicher ab $8004 den String CBM80 schreibt und davor die beiden Vektoren. Selbst wenn man den C64 aus- und wieder einschaltet, ist, aufgrund der "Langlebigkeit" des Speicherinhalts oft immer noch die Kennung erhalten und er versucht, zum Hard-Reset-Vektor zu springen. Da natürlich nicht der komplette Speicherinhalt erhalten wird, sondern sich langsam abbaut, kann das auch zu Abstürzen führen, wenn die Kennung immer noch vorhanden ist, die Vektoren aber nur noch zum Teil - oder die Vektoren zeigen auf eine Routine, die schon teilweise "verwest" ist... So kann man den C64 wunderbar "blockieren".....
Achja: Das Ganze funktioniert natürlich nicht bei eingesteckten Modulen, weil diese ja dann diese Funktion übernehmen.
Benutzeravatar
oDOSseus
LAN Manager
Beiträge: 239
Registriert: Di 10. Aug 2010, 15:21

Re: Commodore 64

Beitrag von oDOSseus »

Das es keinen "leeren" speicher gibt ist mir, als programmierer, schon klar. Ich meinte, dass der c-64-basic-interpreter nach dem sys-Aufruf vllt. weiter nach basic-befehlen ab seinem pointer sucht und ich das iwi unterbinden müsste, bzw. aufpassen sollte, da kein asm reinzuladen. verstehst du jetzt was ich meinte?

Das mit dem Byte-weise speichern war mir klar. nur nicht, wie ich das mache xD, kannst du einen beispiel-code geben? das wäre super =)
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Commodore 64

Beitrag von DOSferatu »

oDOSseus hat geschrieben:Das es keinen "leeren" speicher gibt ist mir, als programmierer, schon klar. Ich meinte, dass der c-64-basic-interpreter nach dem sys-Aufruf vllt. weiter nach basic-befehlen ab seinem pointer sucht und ich das iwi unterbinden müsste, bzw. aufpassen sollte, da kein asm reinzuladen. verstehst du jetzt was ich meinte?

Das mit dem Byte-weise speichern war mir klar. nur nicht, wie ich das mache xD, kannst du einen beispiel-code geben? das wäre super =)
Nö, der SYS-Aufruf startet ein "Assembler-Unterprogramm" ab der angegebenen Adresse. Vorher kann man übrigens Akku, X-Register, Y-Register und Statusregister nach Adresse 780, 781, 782 und 783 schreiben (mit Poke), denn da holt der SYS-Aufruf die dann her, bevor er das Unterprogramm startet. Ein RTS-Befehl (Asm) am Ende des Unterprogramms bewirkt einen Rücksprung zum BASIC-Interpreter.
Anmerkung, nicht wichtig...
Wenn man sowieso keinen BASIC-Interpreter braucht, sondern nur noch ASM ausführen will, sperrt man den IRQ (SEI) und "leert" den Stack (LDX,$FF und TXS) und dann setzt man die Interrupts neu usw.... So fangen meistens die Spiele an, die aus reinem Asm bestehen.

Die Adresse nach $FE und $FF laden... Hm. Kommt drauf an, wie der C64-Assembler funktioniert. Ich kenne z.B. das Format <, um das LowByte und > um das Highbyte eines Wertes zu benutzen.
Nehmen wir an, der Wert wäre das Label TEXT. Dann kann man das z.B. so machen:

Code: Alles auswählen

LDA <TEXT
STA *$FE
LDA >TEXT
STA *$FF
Das * bedeutet, daß in die Zeropage geschrieben wird. Es kommt eben wirklich drauf an, wie der Assember funktioniert. Manche schreiben auch automatisch in die Zeropage.
Im Prinzip ist es Wurst. Es bestimmt nur den Opcode. Nämlich, ob der einen 16bit Parameter (z.B. $00FE) benutzt, oder einen 8bit-Parameter. (Es gibt extra Befehle, um in die Zeropage zu schreiben oder zu lesen.)

Ich habe mich schon sehr lange nicht mit C64 intensiv beschäftigt. Aber ich könnte mal eine Befehlsliste und Liste der Adressierungsarten erstellen. Aber ich weiß eben nicht, welchen Assembler Du benutzt. ICH habe mir nämlich damals meinen Assembler selbst geschrieben und der hatte einen teils etwas merkwürdigen Stil.
Benutzeravatar
oDOSseus
LAN Manager
Beiträge: 239
Registriert: Di 10. Aug 2010, 15:21

Re: Commodore 64

Beitrag von oDOSseus »

Hey funktioniert =)
muss aber #<TEXT und #>TEXT benutzen.
vielen Dank =)
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Commodore 64

Beitrag von DOSferatu »

Ja, stimmt natürlich. "immediate" wird mit # gemacht (also direkt aus dem Code).
elianda
DOS-Übermensch
Beiträge: 1150
Registriert: Mi 31. Jan 2007, 19:04
Wohnort: Halle
Kontaktdaten:

Re: Commodore 64

Beitrag von elianda »

DOSferatu hat geschrieben:
oDOSseus hat geschrieben: Wenn man sowieso keinen BASIC-Interpreter braucht, sondern nur noch ASM ausführen will, sperrt man den IRQ (SEI) und "leert" den Stack (LDX,$FF und TXS) und dann setzt man die Interrupts neu usw.... So fangen meistens die Spiele an, die aus reinem Asm bestehen.
Ich denke, Stack 'leeren' bringt gar nichts. Ob der Stack Pointer beim Programmstart auf $FF oder sonstwo anders steht, ist doch vollkommen irrelevant. Vielleicht ist das bei einigem Spielen so.

Ansonsten ist Assembler auf einem C64 etwas speziell, da die CPU sehr speicherorientiert arbeitet und damit Speicherzugriffe vergleichsweise schnell sind. Damit ist indirekt indizierte Adressierung im allgemeinen langsamer als selbstmodifizierender Code.
Die CPU hat 2 Pipeline Stufen, die man grob in Decode / Execute teilen kann. Daher braucht LDA #$00 nur 2 Zyklen und einfache Befehle ohne Operandenbyte wie NOP, SEI usw. auch jeweils 2.
Du kannst z.B. solche Sachen machen:
LDA #<START
STA ST+1
LDA #>START
STA ST+2
LDA #<ZIEL
STA ZI+1
LDA #>ZIEL
STA ZI+2
LDX #COUNT
START LDA $0000,X
ZIEL STA $0000,X
DEX
BPL START
INC START+2
INC ZIEL+2
LDA ZIEL+2
CMP #ENDE
BNE START

mal so ganz grob.
INC setzt nur Negativ und Zero Flag, daher ist das LDA / CMP notwendig,
weil CMP intern wie eine Subtraktion wirkt, die nur die Flags setzt. CMP setzt NZ und das Carry, wobei Zero gesetzt wird, wenn ZIEL+2 gleich ENDE ist (Subtraktion ergibt #0).
Diverse Retro-Computer vorhanden.
freecrac
DOS-Guru
Beiträge: 861
Registriert: Mi 21. Apr 2010, 11:44
Wohnort: Hamburg Horn

Re: Commodore 64

Beitrag von freecrac »

oDOSseus hat geschrieben:Das es keinen "leeren" speicher gibt ist mir, als programmierer, schon klar. Ich meinte, dass der c-64-basic-interpreter nach dem sys-Aufruf vllt. weiter nach basic-befehlen ab seinem pointer sucht und ich das iwi unterbinden müsste, bzw. aufpassen sollte, da kein asm reinzuladen. verstehst du jetzt was ich meinte?
Es gibt Spiele die nur aus einem einzigen SYS-Befehl im Basiclisting bestehen und unmittelbar nach dem Basic-Listing einen Opcode mitbringen der mitgeladen wird.
Nachdem das Spiel mit dem Rücksprung über RTS zurück zum Basic-Interpreter zurück springt versucht dieser noch weitere Basic-Befehle nach dem SYS-Befehl zu finden
und wenn dort keine Basicbefehle mehr vorhanden sind, dann erst wird das Programm beendet.

Ich verwende auch die Umschreibung vom Speicher leeren und meine damit natürlich den Speicher mit Nullen beschreiben/füllen.

Dirk
freecrac
DOS-Guru
Beiträge: 861
Registriert: Mi 21. Apr 2010, 11:44
Wohnort: Hamburg Horn

Re: Commodore 64

Beitrag von freecrac »

elianda hat geschrieben:
DOSferatu hat geschrieben:
oDOSseus hat geschrieben: Wenn man sowieso keinen BASIC-Interpreter braucht, sondern nur noch ASM ausführen will, sperrt man den IRQ (SEI) und "leert" den Stack (LDX,$FF und TXS) und dann setzt man die Interrupts neu usw.... So fangen meistens die Spiele an, die aus reinem Asm bestehen.
Ich denke, Stack 'leeren' bringt gar nichts. Ob der Stack Pointer beim Programmstart auf $FF oder sonstwo anders steht, ist doch vollkommen irrelevant. Vielleicht ist das bei einigem Spielen so.

Ansonsten ist Assembler auf einem C64 etwas speziell, da die CPU sehr speicherorientiert arbeitet und damit Speicherzugriffe vergleichsweise schnell sind. Damit ist indirekt indizierte Adressierung im allgemeinen langsamer als selbstmodifizierender Code.
Die CPU hat 2 Pipeline Stufen, die man grob in Decode / Execute teilen kann. Daher braucht LDA #$00 nur 2 Zyklen und einfache Befehle ohne Operandenbyte wie NOP, SEI usw. auch jeweils 2.
Du kannst z.B. solche Sachen machen:
LDA #<START
STA ST+1
LDA #>START
STA ST+2
LDA #<ZIEL
STA ZI+1
LDA #>ZIEL
STA ZI+2
LDX #COUNT
START LDA $0000,X
ZIEL STA $0000,X
DEX
BPL START
INC START+2
INC ZIEL+2
LDA ZIEL+2
CMP #ENDE
BNE START
Selbstmodifizierenden Code ist aber oft schwerer zu lesen.
mal so ganz grob.
INC setzt nur Negativ und Zero Flag, daher ist das LDA / CMP notwendig,
weil CMP intern wie eine Subtraktion wirkt, die nur die Flags setzt. CMP setzt NZ und das Carry, wobei Zero gesetzt wird, wenn ZIEL+2 gleich ENDE ist (Subtraktion ergibt #0).
Auch zu berücksichtigen: Bei LDA, LDX und LDY werden auch Negativ und Zero-Flag gesetzt/gelöscht. (Im Unterschied ein mov, lodsb oder stosb beim x86 verändert noch keine Flags.)

Dirk
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Commodore 64

Beitrag von DOSferatu »

Natürlich braucht man nicht den Stack zu leeren. Was ich meinte, war, daß Programme, die sowieso nicht vorhaben, zum Basic zurückzukehren (wie die meisten C64 Spiele), sich den Stack quasi "initialisieren", weil sie sowieso keine der Rücksprungadressen oder sonstigen von Basic aus geretten Werten mehr brauchen. Und ja, das sind z.B. die Spiele, die nur aus einem Basic Befehl SYS Adresse bestehen.
Und nochmal: sMan braucht den Speicher nicht "leeren", damit ein Rücksprung aus einem Assembler-Programm in Basic weitermacht oder es beendet. Der Basic-Interpreter weiß selbst, wann das Basic-Programm zu Ende ist, weil die Endadresse in zwei Zeropage-Adressen eingetragen wird:

Code: Alles auswählen

Adressen  Inhalt
43 44     Start des BASIC-Programms (normalerweise 1 und 8, also 2049 bzw $0801, siehe unten)
45 46     Ende des BASIC-Programms, Start der Variablen
47 48     Ende der Variablen, Start der Arrays
49 50     Ende der Arrays
51 52     Zeiger auf String-Grenze und gleichzeitig Ende des Basic-Speichers+1
          (normalerweise 0 und 160, also 40960 bzw $A000, siehe unten)
53 54     Hilfszeiger für Strings, enthält den Anfang der Zeichenketten
55 56     Zeiger auf BASIC-RAM Ende, normalerweise müßte das identisch mit den Stellen
          51 52 sein
Zum Start des Basic-Programms: Der Speicher wäre zwar von 2048 bis 40959 verfügbar. Aber man beginnt erst bei 2049. Grund ist, daß der BASIC-Interpreter vor dem Basic-Programm immer eine Null (0) erwartet, weiß nicht genau, wieso. Aber wenn da keine Null steht, spinnt der. Müßte mir den BASIC-Source mal angucken, um diesem Mysterium auf den Grund zu gehen.
Was die Variablen angeht: Alle Variablen werden mit 7 Bytes gespeichert, auch die, die weniger brauchen. Arrays werden als Folgen von einzelnen 5-Byte Folgen gespeichert (je nach Größe des Arrays).
Die ersten zwei Bytes von Arrays und Variablen sind die zwei Buchstaben oder Buchstabe+Zahl des Variablennamens. Dabei gelten die unteren 7 Bit. Die beiden oberen Bits entscheiden zwischen 4 Variablentypen: Real-Variable, Integer-Variabe, Array-Variable und String-Variable.

Real-Variablen werden in den folgenden 5 Bytes gespeichert, weiß grad nicht auswendig, wieviele Bits Mantisse und wieviele Exponent. Aber glaube 31 Mantisse, 8 Exponent und 1 Vorzeichen.

Integer-Variablen sind auf dem C64 irgendwie sinnlos, ich erkläre mal, wieso: Sie brauchen ebenfalls 5 Bytes - das liegt daran, daß die Variablen-Finder-Routine auf diese 7-Schritt-Suche aufgebaut ist und daher ALLE Variablen in 7er Schritten absuchen. Integer-Variablen benutzen von den folgenden 5 Bytes nur die ersten zwei Bytes und haben eine Reichweite von -32768 bis 32767. Die restlichen 3 Bytes sind unbenutzt. Da könnte man jetzt sagen: OK, aber dafür kann der mit Integer-Variablen schneller rechnen - aber das Gegenteil ist der Fall: Es gibt nur Berechnungs-Routinen für die Real-Variablen. Integers werden für die Berechnung in Real gewandelt, dann gerechnet und dann wieder, wenn Integer-Ergebnis gewünscht, in Integer zurück.

Der Variablentyp Array ist eine Variable, die in den ersten beiden der 5 Bytes die Adresse des Arrays speichert und in den restlichen glaub die Dimensionsgrößen der einzelnen Dimensionen (falls mehrdimensional). Das richtige Array ist dann ein Array of Datentyp oder so und liegt dann im Array-Bereich zwischen dem Bereich, der von 47+48 und 49+50 angegeben wird.

Der Variablentyp String ist eine Variable, die 3 der restlichen 5 Bytes benutzt. 2, um die Adresse im String-Bereich anzugeben, 1 um die Länge des Strings anzugeben. Strings werden "von hinten nach vorn" neu angelegt, zwar vorwärts, aber eben vom Speicherende aus angefangen. Daher gibt es auch eine Adresse in 53+54, die den Anfang der Strings anzeigt. Das heißt, zwischen der Adresse 49+50 (Ende der Arrays) und 53+54 (Anfang des Stringspeichers) ist der Bereich, der für Variablen noch frei ist. Die Variablen und die Strings bewegen sich also von beiden Seiten aufeinander zu. Strings ändern sich übrigens nicht einfach. Wird ein String geändert oder ein neuer String angelegt, wird einfach "vorn" an den Stringspeicher die neue Zeichenkette angefügt, ohne den alten String, der irgendwo dahinter liegt, zu entfernen. (Daher sollte man es vermeiden, zu dämlich bei der Programmierung mit Strings rumzumachen.)

Das sich ergebende Problem ist das: Variablen und Strings bewegen sich aufeinander zu. Irgendwann soll ein String angelegt werden, der nicht mehr in die kleine Lücke des noch freien Speichers paßt und dann erst wird "aufgeräumt", d.h. der nicht verwendete Zeichenketten-Müll wird entfernt und die Stringadressen neu angepaßt. (Also alles, was an Zeichenketten keine Referenz auf String-Variablen hat, wird rausgeschmissen.) Dieser Vorgang ist gemeinhin wirklich als "Garbage Collection" (Müllsammlung) bekannt.

Also:
Der Basic-Interpreter hört auf zu interpretieren, wenn er an die Adresse, die in den Speicherstellen 45+46 steht, angekommen ist, ODER, wenn er an einen END Befehl kommt (oder natürlich, wenn jemand die RUN/STOP Taste drückt...).

Das war vielleicht ein wenig mehr Erklärung als nötig. Aber damit weiß man nun auch, wie man sich z.B. den Speicher für Maschinencode, Sprites oder Zeichensätze "freimachen" kann, also den Speicher für BASIC verkleinern und den Speicher für Assembler-Programme oder Grafik- und Sounddaten vergrößern kann.
Die meisten Spiele auf dem C64, die in 100% Assembler geschrieben sind, haben entweder den Selbststarter-Trick (man lädt von Diskette mit LOAD"*",8,1 das erste Programm und das startet dann automatisch) oder sie bestehen aus einem einzigen SYS Adresse Befehl (also Adresse ist die Startadresse des Asm Programms im Speicher), der dann das Programm/Spiel startet. Das passiert nur, weil der C64 normalerweise gleich im BASIC-Interpreter startet - damit man eben aus dem Interpreter (der ja auch nur ein Assembler-Programm ist) auf sein eigenes Programm wechseln kann.

Bitte dabei nicht in DOS- und schon gar nicht in Windows- oder Linux-Kategorien denken!
Der C64 ist ein Single-Task-System. Es laufen sowieso nie mehrere Programme gleichzeitig darauf und viele Spiele haben überhaupt keine "Beenden" Option, sondern sind so gedacht, daß man einfach den C64 wieder aus- und einschaltet, um es zu beenden (oder, falls Modul vorhanden, einen Hard-Reset durchführt). Das heißt, bei größeren Programmen/Spielen in ASM ist gar nicht vorgesehen, daß die jemals "in das BASIC zurückgehen". Es gibt auch nicht "das BASIC". Man geht ja einfach nicht mehr in die Interpreter-Schleife zurück und somit versucht da auch nix, irgendwelche Speicherinhalte als BASIC-Programm zu interpretieren und auszuführen.
Benutzeravatar
oDOSseus
LAN Manager
Beiträge: 239
Registriert: Di 10. Aug 2010, 15:21

Re: Commodore 64

Beitrag von oDOSseus »

6502-Assembler macht, benutzt man die Kernal-Methoden, ja echt mega Spaß. aber wie kann man vernünftige Zufallszahlen zwischen 0 und 4 schaffen? Die Informationen im Internet sind dazu zwar massig, aber alle sagen, es ginge nicht. Wie macht ihr das bzw. wie habt ihr das gemacht?
elianda
DOS-Übermensch
Beiträge: 1150
Registriert: Mi 31. Jan 2007, 19:04
Wohnort: Halle
Kontaktdaten:

Re: Commodore 64

Beitrag von elianda »

Es gibt viele Ansatzpunkte:
a) einen der ueblichen Algorithmen zur Erzeugung von Zufallszahlen nachprogrammieren
b) die Kernal Routine nutzen
c) Ausgabe von Hardware Chips nutzen, wie SID Osc3 auslesen mit aktiven Rauschen, $D012, CIA Timer, Takte zwischen Tastendruecken messen usw.
Diverse Retro-Computer vorhanden.
Benutzeravatar
oDOSseus
LAN Manager
Beiträge: 239
Registriert: Di 10. Aug 2010, 15:21

Re: Commodore 64

Beitrag von oDOSseus »

zu a) hast du da einen im Angebot?
zu b) Welche Kernal-routine meinst du?
zu c) Hast du da ne Quelle im Angebot?
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Commodore 64

Beitrag von DOSferatu »

oDOSseus hat geschrieben:zu b) Welche Kernal-routine meinst du?
Naja, das C64-BASIC hat ja eine Funktion RND(). Die könnte man benutzen.
y=RND(x) liefert für y IMMER eine Zahl zwischen einschließlich 0 und ausschließlich 1, also 0<=y<1.
Für x gelten dabei diese Regeln:
Ist x eine negative Zahl, so wird immer der gleiche Zufallswert zurückgegeben. Das kann auch benutzt werden, um RND auf einen Startwert zu initialisieren - dann liefert RND mit x>0 nämlich immer die gleiche Zahlenfolge.
Ist x=0, so wird, laut Dokumentation eine völlig zufällige Zahl geliefert, aus irgendwelchen Chips des C64. Weiß nicht mehr, welchen die da nehmen, da müßt ich mir den Code angucken.
Ist x>0 (also positiv und nicht 0), so wird eine Zufallszahl zurückgegeben, die aus der internen vorhergehenden Zahl berechnet wird (und die interne Zahl ändert sich dabei). Dieses x kann in diesem beliebig sein und hat keinen Einfluß auf den zurückgelieferten Wert.

Auf die Sache mit x=0 will man sich wohl nicht verlassen, weiß nicht, ob es da irgendwelche Beschränkungen oder Häufungen von Werten aufgrund der Arbeitsweise der verwendeten Chips gibt. Meist benutzt man RND() mit einem positiven Wert, also z.B. RND(1).
Das Problem ist, daß der interne Startwert für RND (man nennt sowas übrigens oft den RANDOM SEED - Zufalls-Samen) nach dem Starten/Booten des C64 immer derselbe ist, so daß man mit RND(positive Zahl) anfangs immer dieselbe Zahlenfolge bekommen würde.
Um dies zu verhindern, initialisieren BASIC-Programmierer dies auf dem C64 gerne mit einer zufälligen negativen Zahl. Und es gibt eine Zahl, die anfangs relativ zufällig ist, nämlich die "Variable" TI (die intern eigentlich eine Funktion ist). TI liefert die aktuelle Zeit seit dem Einschalten zurück, in 60tel Sekunden (Anmerkung: TI$ liefert übrigens die Zeit seit Einschalten im Format HHMMSS zurück.)
Das heißt, der BASIC-Programmerer initialisiert die Zufallsfunktion einfach mit
X=RND(-TI)
Man kann natürlich auch die Ports der CIA1 oder CIA2 auslesen, die ticken ja auch die ganze Zeit, haben einen internen Timer.
Wenn man in Assembler nun eine Zufallsfunktion haben will, kann man entweder die Zufallsfunktion aus dem Kernal nachbauen - oder man benutzt sie einfach, indem man Anfangs z.B. die Werte aus einer der CIAs ausliest und an die Adresse des Random-Seeds kopiert. Und dann ruft man immer wieder die Random-Funktion auf.
Aber, wie gesagt, RND liefert Werte 0<=Wert<1. Um z.B. ganzzahlige Werte von 0 bis 99 zu erhalten, kann man das tun: X=INT(RND(1)*100)
Um ganzzahlige Werte zwischen einschließlich 50 und 80 zu erhalten: X=INT(RND(1)*31)+50
Ich hoffe, ich konnte ein wenig weiterhelfen. Bei Bedarf suche ich noch die Einsprungsadresse der RND-Funktion und die Speicheradresse Random-Seed Wertes raus.
Benutzeravatar
oDOSseus
LAN Manager
Beiträge: 239
Registriert: Di 10. Aug 2010, 15:21

Re: Commodore 64

Beitrag von oDOSseus »

Vielen Dank?

Wenn ich dich richtig verstanden habe, wäre es also klug, diesen teil des Programms vorab in Basic zu Regeln.
Ich könnte mir vorstellen, zwischen "RUN" und dem "RND(-TI)" einfach auf einen Tastendruck zu warten. Der wird ja auch nicht immer zum selben Zeitpunkt sein.

Wie is das mit a) und b)?
DOSferatu
DOS-Übermensch
Beiträge: 1220
Registriert: Di 25. Sep 2007, 12:05
Kontaktdaten:

Re: Commodore 64

Beitrag von DOSferatu »

oDOSseus hat geschrieben:Wenn ich dich richtig verstanden habe, wäre es also klug, diesen teil des Programms vorab in Basic zu Regeln.
Nö - muß nicht sein. Wie gesagt, das geht auch in Assembler. Die ganzen Subroutinen von Basic und Kernal sind ja auch in Assembler.
oDOSseus hat geschrieben:Ich könnte mir vorstellen, zwischen "RUN" und dem "RND(-TI)" einfach auf einen Tastendruck zu warten. Der wird ja auch nicht immer zum selben Zeitpunkt sein.
TI (die interne Zeit) fängt ja schon beim Start des C64 an zu rennen und hält nicht an. Sie ist nicht sehr genau, weil bestimmte Prozesse den Interrupt mal kurz ausschalten - das macht aber in dem Fall nichts. Nach Start des C64 muß man ja das Programm erst laden, schon alleine das macht ja nicht jeder zum gleichen Zeitpunkt - die Eingabe von RUN auch nicht.
Ein Problem ergibt sich vielleicht höchstens mit C64-Emulatoren, die die Option haben, ein Programm gleich automatisch zu laden und zu starten, also quasi automatisch ausführen: RESET - Programm in Speicher legen, starten. Denn dann wäre TI wohl wirklich immer gleich und hier könnte ein Tastendruck helfen. Wenn es aber z.B. ein Spiel ist, hat das Ding ja meistens ein Titelbild und Auswahloptionen oder sowas und dann kann man Start drücken - und bis dahin ist eine zufällige Zeit vergangen.
Zu den Quellen: Ich such das mal irgendwann raus. Die beiden CIAs haben jeweils 16 Adressen (Bytes). CIA1 ab $DC00 und CIA2 ab $DD00. Ich weiß jetzt grad nicht auswendig, wo die Ticker-Bytes bei denen liegen.
Der Soundchip (SID) hat als eine der Wellen "Rauschen". Dieses Rauschen wird intern mit einer 23bit-Zahl erzeugt, die sich wirkllich erst nach den 8388608 durchläufen wiederholt und zufällige Werte durchläuft (hab da mal ne sehr genaue Doku gefunden, wie der SID funktioniert, glaub sogar von Bob Yannes persönlich).
In einem der letzten 3 SID-Register (nur die letzten 3 der 29 SID-Register sind "lesbar") kann man die aktuelle "Wellenposition" (0-255) der Stimme 3 auslesen. Also kann man: Stimme 3 auf Rauschen stellen, losrauschen lassen und den Wert da auslesen. Dabei kommt einem zugute, daß man Stimme 3 auf "stumm" schalten kann. (die anderen beiden der 3 lesbaren SID-Register geben übrigens die Werte der analogen Eingänge (Pins) der Joystick-Ports wieder (im SID is ein A/D-wandler) - damit fragt man die DrehPaddles ab). Der SID wird ab Adresse $D400 eingespiegelt.
Man kann auch, wie von Elianda erwähnt, $D012 abfragen. In $D012 steht die Nummer der aktuellen Rasterzeile (bzw deren untere 8 Bits), d.h. die Bildschirmzeile, die gerade generiert wird. (das neunte Bit der Rasterzeile ist das oberste Bit von $D011)... Mann, was ich noch alles weiß...
Wenn man $D012 also nicht gerade abfragt, wenn man sich in einer Rasterzeilen-Interrupt-Routine befindet, erhält man da auch zufällige Werte zwischen 0 und 255. Das hat nur einen kleinen Nachteil:
Beim PAL gehen die Rasterzeilen von 0 bis 311, beim NTSC gibt es 2 Versionen, eine mit 262, eine mit 263 Zeilen. Das bedeutet, daß bei einem Bildschirmdurchlauf natürlich die unteren 8 Bit für die Werte ab 256 doppelt vorkommen, so daß bei der PAL-Version die Werte 0 bis 55 doppelt so oft an $D012 stehen werden wie die Werte 56 bis 255 (und bei der NTSC-Version die Werte 0 bis 6 oder 0 bis 7 doppelt so oft an $D012 stehen werden wie 7/8 bis 255). Das bedeutet, daß deren Wahrscheinlichkeit höher wäre und man somit zwar zufällige Zahlen bekäme, aber nicht völlig gleichverteilte.

Ich hoffe, das war jetzt nicht wieder zu viel Information.
Antworten