Forum: Mikrocontroller und Digitale Elektronik Verständnisfrage zu uraltem Assembler Code


von Matthias H. (mhage)


Lesenswert?

Hallo zusammen,

ich beschäftige mich gerade mit dem Quellcode eines sehr alten Spiels 
von 1983?
Hintergrund ist, ich beabsichtige das in C(++) nachzuprogrammieren. Auf 
aktueller Hardware. Ich habe vor ca. 15 Jahren zum letzten mal Assembler 
programmiert. Kann also behaupten, meine Fähigkeiten sind mehr als 
eingerostet.
Ich verstehe zwar grob den Ablauf des Programmes, aber wenn es an die 
Feinheiten geht, hört es mitunter auf. Dabei sind es an verschiedenen 
Stellen immer die gleichen Fragezeichen...

Die Hardware ist ein TMS1400. Wusste bis vor kurzem nicht einmal, dass 
es sowas mal gab :-)
1
Instruction Func  OPCODE Bits  HEX  Inst
2
----------------  -----------  ---  ----
3
0011001111101000  00000000  00  MNEA - Memory not equal to Accumulator
4
0011010111010000  00000001  01  ALEM - Accumulator less than or equal to Memory
5
0010101111101001  00000010  02  YNEA - Y Register not equal to Accumulator
6
1011011111001100  00000011  03  XMA - Exchange Memory and Accumulator
7
0011001111011100  00000110  06  AMAAC - Add Memory to Accumulator -> Accumulator with Carry
8
0011011101011100  00000111  07  DMAN - Decrement memory -> Accumulator with Borrow
9
0001111111001100  00001000  08  TKA - Transfer K inputs to Accumulator
10
0001111111101000  00001110  0E  KNEZ - K inputs not equal to Zero
11
0011101111001010  00100000  20  TAY - Transfer Accumulator to Y Register
12
0011011111001100  00100001  21  TMA - Transfer Memory to Accumulator
13
0011011111001010  00100010  22  TMY - Transfer Memory to Y Register
14
0010111111001100  00100011  23  TYA - Transfer Y Register to Accumulator
15
1011111111001100  00100110  26  TAMZA - Transfer Accumulator to Memory and Zero Accumulator
16
1011111111001000  00100111  27  TAM - Transfer Accumulator to Memory
17
0011010111010100  00111100  3C  SAMAN - Subtract Accumulator from Memory -> Accumulator with Borrow
18
0011110111010100  00111101  3D  CPAIZ - Complement Accumulator and increment
19
0011011111010100  00111110  3E  IMAC - Increment Memory -> Accumulator
20
0011011111101000  00111111  3F  MNEZ - If Memory not equal to Zero, 1 -> Status
21
0010111101011010  00X00100  04/24  DYN, TAMDYN - Decrement Y Register with Borrow
22
0010111111010010  00X00101  05/25  IYC, TAMIYC - Increment Y Register with Carry
23
0001011110101000  001110XX  38-3B  TBIT1 - Test Memory Bit
24
0001111111001010  0100XXXX  4X  TCY  
25
0010111110101000  0101XXXX  5X  YNEC
26
0110111111000010  0110XXXX  6X  TCMIY  
27
1010111101010010  0010010X  24/25  TAMDYN, TAMIYC
28
0001101111010100  0111XXXX  7X  IAC, DAN, CLA, A1ACC, A2/3/.../13/14AAC  
29
0001111111010100  01111111  7F  CLA
30
31
All opcodes match standard TMS1000/TMS1100 opcodes--validated against PLA mask

Das hier ist ein Auszug der Befehle.
Jetzt geht es mir um die "Sound" Ausgabe. Hier mal die Funktion zum 
erstellen des Buffers für die Ausgabe.
1
PLYBAZR ldx   6         
2
  tcy   0         ; 6/0 - 6 5 6 7 6 5 6 7 9
3
  tcmiy 6         
4
  tcmiy 5         
5
  tcmiy 6         
6
  tcmiy 7         
7
  tcmiy 6         
8
  tcmiy 5         
9
  tcmiy 6         
10
  tcmiy 7         
11
  tcmiy 9         
12
  ldx   7         
13
  tcy   0         ; 7/0 - 3 15 3 10 3 15 3 10 7
14
  tcmiy 3         
15
  tcmiy 15        
16
  tcmiy 3         
17
  tcmiy 10        
18
  tcmiy 3         
19
  tcmiy 15        
20
  tcmiy 3         
21
  tcmiy 10        
22
  tcmiy 7         
23
  ldx   4         
24
  tcy   0         ; 4/0 - 10 5 5 5 5 5 5 5 5 5
25
  tcmiy 10        
26
SD29    tcmiy 5         
27
  ynec  9         
28
  br    SD29      
29
  ldx   5         
30
  tcy   0         ; 5/0 - 0 0 0 0 0 0 0 0 0 0
31
SD22    tcmiy 0         
32
  ynec  9         
33
  br    SD22      
34
  ldp   0         
35
  call  PLAYBUF   ; Play part 1 of tune

Hier die Funktion für die eigentliche Ausgabe
1
;********************************************************************************
2
; PLAYBUF - Play the sound buffer loaded in the Scratchpad RAM region
3
;
4
; 4/Y and 5/Y have array of durations
5
; 6/Y and 7/Y have array of pitches
6
; Notes are loaded in reverse order from Y=(0-10) down to 0
7
;
8
; Called with Y register pointing to first note plus 1
9
;********************************************************************************
10
PLAYBUF tya             ; Transfer the Y register to the Accumulator
11
  dan             ; Subtract one from Accumulator to point to beginning of sound buffer at 4/Y
12
  ldx   3         
13
  tcy   10        
14
SC0F    tam             ; Save updated pointer into sound buffer into 3/10
15
  tmy             
16
  ldx   4         ; Check 4/Y
17
  mnez            
18
  br    SC3A      ; There's a non-zero value here, play it
19
20
  ldx   5         ; Check 5/Y
21
  mnez            
22
  br    SC3A      ; There's a non-zero value here, play it
23
  ldp   1         
24
  br    LC5F      ; There's a zero here... play silence (a rest)
25
26
SC39    mnea            
27
  mnea            
28
  mnea            
29
  mnea            
30
SC1D    tam             
31
32
SC3A    tcy   10        ; Select the speaker R output
33
  setr            ; Toggle the speaker on
34
  mnea            
35
  mnea            
36
  mnea            
37
  mnea            
38
  ldx   3         
39
  rstr            ; Toggle the speaker off
40
  tmy             ; Restore the note we were playing
41
;
42
; Load delay from 6/Y and 7/Y into A and Y respectively and delay for that many loops
43
;
44
; This adjusts the time between toggles of the speaker
45
;
46
  ldx   6         
47
  tma             
48
  ldx   7         
49
  tmy             
50
SC1C    dyn             
51
  br    SC1C      
52
  dan             
53
  br    SC1C      
54
;
55
; Restore the note we're on into the Y register
56
;
57
  ldx   3         
58
  tcy   10        
59
  tmy             
60
;
61
; Loop based on 5/Y and 4/Y, playing the note repeatedly.  This gives duration.
62
;
63
  ldx   5         
64
  dman            
65
  br    SC39      
66
  tam             
67
  ldx   4         
68
  dman            
69
  br    SC1D      
70
;
71
; Note has been played for requested duration, decrement 3/10 and loop
72
;
73
LC08    ldx   3         
74
  tcy   10        
75
  dman            
76
  br    SC0F      
77
  retn 
78
79
80
;
81
; More of the PLAYBUF routine.  Code responsible for playing silence (a rest)
82
;
83
SC40    mnea            
84
  mnea            
85
  mnea            
86
  mnea            
87
SC4F    tam     
88
89
90
91
;
92
; Play a "zero" note - silence
93
;
94
LC5F    cla             
95
  dan             
96
SC7E    mnea            
97
  mnea            
98
  mnea            
99
  mnea            
100
  mnea            
101
  dan             
102
  br    SC7E      
103
  ldx   7         
104
  dman            
105
  br    SC40      
106
  tam             
107
  ldx   6         
108
  dman            
109
  br    SC4F      
110
  ldp   0         
111
  br    LC08      ; Go back and play the next note

So wie ich das verstehe ist es ja eine Art PWM. Was ich im Moment nicht 
weiß, wie ich die Zahlenwerte beim füllen des Buffers betrachten soll. 
Sind es millisekunden? In der Funktion für die Ausgabe leuchtet mir 
nicht ganz ein, wie durch die aufgerufenen Schleifen die genauen 
Verzögerungen erzeugt werden.
Der wiederholte Aufruf des Befehls "mnea" ist mir dabei auch 
unverständlich.

Ich hoffe, einer der Assembler Profis kann mir etwas mehr Einblick in 
die Funktionsweise geben. Die Nachprogrammierung der beiden Funktionen 
artet schon in wildes Probieren aus, was bisher nicht zielführend war.


Gruß
Matze

von c-hater (Gast)


Lesenswert?

Matthias H. schrieb:

> ich beschäftige mich gerade mit dem Quellcode eines sehr alten Spiels
> von 1983?
> Hintergrund ist, ich beabsichtige das in C(++) nachzuprogrammieren. Auf
> aktueller Hardware.

Tja, wenn man irgendwas portieren will, muss man

1)die Quellsprache und die Zielsprache beherrschen.
2)ggf. das ursprüngliche Target und neue kennen.

Da bleibt dir noch viel Arbeit bei der persönlichen Aufschlauung zu 
leisten.

Wollen wir mal hoffen, dass dieses Spiel es wirklich wert ist...

von jens (Gast)


Lesenswert?

Hallo Matze,

Nur so eine Idee:

Das sind evtl. keine Zeitangaben in ms.
Sondern Zeitangaben, die in Relation zu der Zeit ist, die die mnea 
Befehle bzw Unterroutinen dauern?

Die Hardware läuft doch mit einer festen Frequenz. Und der mnea Befehl 
dauert dementsprechend.

Also kannst Du ausrechnen, wielange zB die SC3A Funktion den 
Lautsprecher anmacht.

Jens

von AtariST (Gast)


Lesenswert?

Dark Tower?

von Matthias H. (mhage)


Lesenswert?

@jens

Der Gedanke kam mir auch schon. Leider ist mir nicht Bekannt hoch der 
Takt ist.
Der mnea Befehl wird ja tatsächlich ähnlich wie nop verwendet....

Werde nochmal meine Unterlagen durchschauen ob ich etwas finde.


@AtariST

Völlig richtig! :-)
Tatsächlich am Code Fetzen erkannt?


Das füllen des Buffers leuchtet mir auch noch nicht so ganz ein. Für 
mich sieht das erstmal aus wie 2D Array. Würdet Ihr das auch so lesen?

Nachtrag:
Habe noch etwas gefunden:
1
26 OSC  pin 27 and 56pF cap to batt+ and 43K resistor to batt-
2
27 OSC  pin 26 and cap and resistor
3
28 Vss  9v battery positive

Muss mal schauen, wie ich mit den Angaben den Takt rausfinde...

: Bearbeitet durch User
von Matthias H. (mhage)


Angehängte Dateien:

Lesenswert?

Ich rechne wahrscheinlich wieder Unsinn.

f = 1 / (2  pi  R * C)

Komme auf 66 KHz.

Ist jetzt eine gute Frage wie viele Zyklen er für 1 Befehl braucht.

Bei 1/66000 wären das etwa 15us.
Gute Frage...


Nachtrag:

Habe noch etwas gefunden. Im Anhang ein Screenshot...

: Bearbeitet durch User
von Ein Kommentar (Gast)


Lesenswert?

Einfacher wäre, du extrahierst den Soundtrack aus einem der Youtube 
Videos.

Wenn du mit aktueller Hardware eine Linux-Platine meinst, kannst du die 
damalige Strategie eh nicht verwenden. Musst die Sound-Treiber nutzen.

von Matthias H. (mhage)


Lesenswert?

Ich meinte tatsächlich einen AVR.
Ich habe die Sounds als WAV Dateien. Habe aus der WAV mit einem 
Hilfstool ein Datenstream erzeugt. Der lässt sich auch mittels PWM auf 
dem AVR abspielen.

Macht aber natürlich Aufgrund der Datenmenge keinen Sinn.

Ich habe jetzt mal die Ausgabeprozedur Schritt für Schritt 
mitgeschrieben damit ich mal die Logik durchschaue. Dabei bin ich 
schnell drauf gekommen, dass das kein 2D Array ist, sondern ein 4D.

Wenn ich mir die Prozedur so anschaue , frage ich mich, wer sich sowas 
ausdenkt... Meine Schritt für Schritt Notizen werde ich morgen mal in 
Code wandeln und auf einem Arduino ausprobieren. Bin gespannt, ob da was 
gescheites bei rum kommt. Habe leise Zweifel.

von Stefan F. (Gast)


Lesenswert?

Matthias H. schrieb:
> Macht aber natürlich Aufgrund der Datenmenge keinen Sinn.

Für Audio Ausgabe eignen sich diese angeblich übertrieben schnellen 32 
Bit Controller mit DMA und DAC viel besser. Computer aus 8/16 Bit Zeiten 
hatten für die Audio Ausgabe eigene Chipsätze.

von H. H. (Gast)


Lesenswert?

Matthias H. schrieb:
> f = 1 / (2  pi  R * C)

Nicht 2*Pi, sondern 2*ln(2).

von Christoph db1uq K. (christoph_kessler)


Lesenswert?

Hier gibt es ein Wiki zur TMS1000-Familie, mit Handbüchern aus den 
Siebzigern:
https://en.wikichip.org/wiki/ti/tms1000

das Datenblatt
http://blog.kevtris.org/blogfiles/TMS1000/tms1400info.pdf

Anscheinend kann man TMS1000-Programme im Emulator MAME laufen lassen
https://github.com/mamedev/mame/blob/master/src/devices/cpu/tms1000/tms1k_base.cpp

Aus der Zeit habe ich nur ein "Speak&Spell von Texas Instruments, aber 
da scheint noch eine 4-Bit CPU drin zu stecken, die Hauptsache ist der 
Sprachgenerator.
https://en.wikipedia.org/wiki/Speak_%26_Spell_(toy)

: Bearbeitet durch User
von Matthias H. (mhage)


Lesenswert?

H. H. schrieb:
> Matthias H. schrieb:
>> f = 1 / (2  pi  R * C)
>
> Nicht 2*Pi, sondern 2*ln(2).

Hallo,

ich kapier deine Formel nicht ganz...
Kannst du mir das kurz erläutern? Vielleicht mit einer Beispiel 
Rechnung?



Ansonsten ging meine Vermutung, dass der mnea Befehl ähnlich wie nop
genutzt wird nicht auf. Die Verwendung bzw. die Logik der Verwendung 
dieses Befehls bleibt ein Rätzel.
Genau wie der zeitliche Ablauf. Ich sehe zwar, wann und wie welcher Wert 
runtergezählt wird, aber eine Tonfolge entsteht bei mir nicht dabei.

Um genau zu sein, ich müsste nur diese Zeilen verstehen, dann wäre alles 
klar!
1
SC39 mnea            
2
  mnea            
3
  mnea            
4
  mnea            
5
SC1D tam

Der Befehl bedeutet "memory not equal accumulator" Wenn die Bedingung 
wahr wäre, wird das Status bit auf 1 gesetzt. Da aber innerhalb dieser 6 
Zeilen keine Veränderung eintritt, und nachfolgend keine Auswertung des 
Status bits erfolgt, z.B. durch bedingten Sprung, verstehe ich die 
Bedeutung nicht.

Daher kam die Vermutung, dass damit nur Zeit geschunden wird. Ich habe 
im Programm mal probehalber 35us pro Befehl angenommen. Kam damit nicht 
weiter.
Irgendwo übersehe ich etwas.

Langsam glaube ich, diese Soundausgabe ist der schwierigste Teil im 
Projekt...

Vielen Dank für die verlinkten Unterlagen!

: Bearbeitet durch User
von H. H. (Gast)


Lesenswert?

Matthias H. schrieb:
> H. H. schrieb:
>> Matthias H. schrieb:
>>> f = 1 / (2  pi  R * C)
>>
>> Nicht 2*Pi, sondern 2*ln(2).
>
> Hallo,
>
> ich kapier deine Formel nicht ganz...
> Kannst du mir das kurz erläutern? Vielleicht mit einer Beispiel
> Rechnung?

Siehe Datenblatt des NE555 als AMV.

von Cheesus (Gast)


Lesenswert?

Dark Tower gabs auch für den C64:
https://www.myabandonware.com/game/dark-tower-4se

In einem C64-Emulator kann man das laufen lassen und analysieren.

Die Mnemonics für den 6502 dürften geläufiger sein.

von Matthias H. (mhage)


Lesenswert?

Das C64 Game ist etwas anderes...
Ich spreche hiervon:

https://www.spiele4us.de/spiel/atlantis-das-imperium-kehrt-zurueck/

Die Funktionsweise, die ich nachprogrammieren möchte, könnte man hier 
testen:

https://www.emuparadise.me/M.A.M.E._-_Multiple_Arcade_Machine_Emulator_ROMs/Dark_Tower_(Milton_Bradley)/180037


Ist allerdings ohne die Bilder des Turms und des Spielbretts nicht 
wirklich Spektakulär.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.