Ich habe mich hier im Forum mal registriert um eines meiner aktuellen "kleinen" Projekte vorzustellen: Der Famicom Macro Assembler, kurz FMA.
Was ist FMA?
FMA stellt eine Alternative zu den bisherigen Super Nintendo Assemblern dar. Mein Hauptaugenmerk lag daran, den Quellcode ein wenig leserlicher und wiederverwendbarer zu gestalten und dabei trotzdem möglichst die volle Flexibilität von bisherigen Assembler beizubehalten.
Auch wenn FMA derzeit nur auf Super Nintendo Entwicklung ausgelegt ist, sollte es recht einfach sein auch andere Assembler, wie einen NES Assembler zu integrieren.
Was kann FMA was (die meisten) anderen Assembler nicht können?
[*] Speicher-Management geht größtenteils automatisch. Ich kann Variablen definieren, muss aber nicht definieren wo genau sie liegen, auch wenn ich es natürlich weiterhin konfigurieren kann.
[*] Speicher-Bereiche können "wiederverwendet" werden von anderen Code-Abschnitten (z.B. temporärer RAM)
[*] Module und Klassen können benutzt werden um den Quellcode zu strukturieren
[*] Macros und Klassen können auch für Code-Automatismen oder Konfigurationen benutzt werden
[*] Named-Parameter bei Macros für einfacher Verständliche Konfigurationen
[*] Ruby-ähnliche Syntax, welche mMn. eine gute Mischung war um auch die Standard-Assembler-Syntax zu integrieren
[*] Einfacher Zugriff auf Klassen-Member
[*] Sicherheits-Abfragen oder ähnliches können recht einfach integriert werden
[*] Fügt automatisch ein RTS ans Ende einer Funktion, falls der letzte Aufruf keine Instruktion war, die den PC verändert
[*] Sofern möglich, versucht der Assembler sich natürlich auch die Größen von Register A und X/Y zu merken
Ein paar (ungetestete) Beispiel-Code-Schnipsel
Variablen und Variablenbereiche registrieren
Code: Alles auswählen
; Registriert einen Namensraum im RAM (die Variablen müssen nicht hintereinander liegen)
; Der Namensraum liegt innerhalb des RAM Bereiches "SomeOtherScope"
scope :MyRamScope, in: SomeOtherScope
; Registriert einen Namensraum, dessen Inhalte in einem bestimmten Bereich liegen müssen
scope :QuickPage, in: SnesRAM, bank: $00, range: $0100..$1FFF
; Registriert einen Namensraum, dessen Inhalte wiederverwendet werden können
scope :TempRam, in: SnesRAM, bank: $00, range: $0000..$00FF
; Deklariert eine Variable
declare :map_width, in: MyRamScope, as: :uint16
; Deklariert eine Variable an einer bestimmten Address
declare :INIDISP, in: SnesSystemRAM, bank: $00, address: $2100
; Eine Klasse - hier wird die Reihenfolge der Variablen berücksichtigt
class MyClass
declare :variable, as: :uint16
declare :next, as: MyClass.pointer
end
; Registriert ein Array von MyClass
declare :my_classes, in: MyRamScope, as: MyClass, length: 10
Code: Alles auswählen
module CPU
;; Index register flag
FLAG_X = $10
;; Accumulator flag
FLAG_M = $20
;; Enables or disables flags
;;
;; @param A=nil number - defines the size of the A register (either 8 or 16)
;; @param XY=nil number - defines the size of the XY register (either 8 or 16)
macro set(A=nil, XY=nil)
_set = 0
_clear = 0
unless A.nil?
case A
when 8
_set |= CPU.FLAG_M
when 16
_clear |= CPU.FLAG_M
else
raise "Unexpected size for register A"
end
end
unless XY.nil?
case XY
when 8
_set |= CPU.FLAG_X
when 16
_clear |= CPU.FLAG_X
else
raise "Unexpected size for register XY"
end
end
SEP #_set if _set
REP #_clear if _clear
end
end
def my_code
; Set register A size to 8 bit
CPU.set A: 8
LDA #123
; Set register A size to 16 bit
CPU.set A: 16
LDA #12345
end
Code: Alles auswählen
macro with_bank(bank)
PHB
PEA #bank << 8
PLB
PLB
yield
PLB
end
def my_code
with_bank $7E do
LDA my_cool_variable
end
end
Code: Alles auswählen
class MyClass
declare :next, as: MyClass.pointer
declare :index, as: :uint16
end
declare :my_first_class, as: MyClass.pointer
def my_code
LDY #0
LDX #my_first_class
BZE _end_of_code
_loop:
TYA
; This code:
STA X[:index]
LDA X[:next]
; Is the same code as
STA MyClass.offset_of(:index), X
LDA MyClass.offset_of(:next), X
BZE _end_of_code
TAX
INY
BRA _loop
_end_of_code:
end
Derzeit ist FMA in zwei Versionen vorhanden:
Einer alten, sehr langsamen in JavaScript geschriebenen Version: Diese Version ist zwar schon "so gut", dass ich damit mein JRPG Projekt sehr weit entwickelt konnte, wird aber einerseits nicht weiter entwickelt und hat andererseits auch einige Bugs, mit denen man zwar leben kann, aber nicht sollte
Die neue Version, welche komplett neu in C++ geschrieben ist, ist derzeit noch WORK IN PROGRESS! Das bedeutet:
[*] Noch nicht alle Features der alten Version sind in der neuen integriert
[*] Noch nicht alle Instruktionen sind integriert
[*] Es gibt noch einige kritische Fehler die es noch zu entdecken gilt
[*] Es gibt keinerlei Dokumentationen, Tutorials, Hilfen oder sonst etwas.
[*] Ich habe noch nicht getestet, ob es unter Windows kompiliert, daher gibt es auch keine fertige .EXE Datei oder so
Weiterhin ist dazu zu sagen, dass die neue Version nicht 100% Abwärtskompatibel ist.
Ich würde daher NICHT empfehlen FMA jetzt schon zu benutzen
versteckter Inhalt:
Sonstiges
Obwohl das Projekt noch nicht nutzbar ist, wollte ich dieses Projekt schon einmal vorstellen
Sonst auf jeden Fall ein schönes Restwochende
