X86 Assembler...

  • Hallo.


    Ich beschäftige mich seit 2-3 Jahren mit Assemblerprogrammierung am MOS 6502 und Intel 8080. Meine Programme sind natürlich nichts weltbewegendes, aber ich habe schon das ein oder andere Tool programmiert, das mir persönlich, bei meinen Projekten weitergeholfen hat. Ohne Assemblerkenntnisse hätte ich auch niemals meinen Imsai 8080 sauber zum Laufen gekriegt, versteht sich. Das ist ja noch "Computer pur".

    Um irgendwelche Spiele und Demos zu programmieren, fehlt mir zum einen die Zeit und zum anderen hab ich da auch überhaupt keine Ideen. Dazu muß man eine künstlerische und eine technische Ader haben. Ich würde mich eher als "technisch kalt" bezeichnen. Ohne Ideen für irgendwelche Grafiken, Spiele-Ideen oder dergleichen.. Natürlich hab ich am C64 schon mit den Rasterinterrupts gespielt und den ein oder anderen Lauftext unten, mit stehender Überschrift oben und SID-Sound Untermalung Code gebastelt. Aber das war es künstlerisch dann auch schon.

    Zwischendurch hab ich dann mal mit Z80 Assembler angefangen, aber da find ich irgendwie nicht hin. Ich habe da auch nur einen Vintage-Rechner, den Kaypro II, da. Und wenn ich da dringend was bräuchte, könnte ich das auch in 8080 Assembler tippen. Das ist ja schließlich kompatibel.

    Da ich auch Fan der Marke mit dem Nadelstreifenanzug und der Bügelfaltenhose bin, würde ich jetzt gerne mal was an meinem IBM 5160 machen, also im X86 Assembler. Bzw. auch für meinen IBM 5170 und den Cordata PPC...


    Die Kenntnisse beim 8080 machen es mir vielleicht ein bißchen leichter beim Erlernen des x86 Assemblers bzw. der Mnemonics. Allerdings ist das "Außenrum" natürlich anders.


    1. Da wäre der Speicher. Bisher hatte ich meine 64kb, also von 0000h bis FFFFh. Linear adressierbar. Beim IBM mit seinen 640kb RAM hab ich 16 Bit Register, aber hardwareseitig einen 20 Bit Adressbus (also bis 1 MB). Natürlich kann man mit 16 Bit keine 20 Bit linear adressieren. Deswegen gibt es die Segmente und in denen die Offsetadresse. 1 MB sind hardwareseitig adressierbar, 64kb sind über 16 Bit Register adressierbar. Jetzt könnte man meinen, man teilt einfach 1MB durch 16. Dann hat man 16 Segmente á 64 kb, was 1 MB ergäbe. Und ein Segment würde direkt nach dem anderen beginnen. Das würde normalem Bankswitching entsprechen, wie es bei anderen Rechnern der Fall ist. So "einfach" ist das aber beim 8088 nicht.


    (Ich will künftig jetzt 8088 schreiben anstatt x86, weil ab 80386 das Problem wegen der 32 Bit breiten Register nicht mehr besteht).


    Es ist nicht so einfach, weil in den Segmenten eine Überlappung sein kann. Ein neues Segment darf frühestens 16 Bytes nach dem letzten beginnen. Das würde ja bedeuten, dass Byte 17 von Segment 1 exakt die gleiche Speicherstelle ist wie Byte 00 von Segment 2. Oder sehe ich das falsch ? Vielleicht kann mir das jemand genauer erklären ?!


    2. Eine COM-file hat max 64kb, läuft also immer innerhalb eines Segmentes. Eine EXE-Datei kann im Prinzip den kompletten Speicher nutzen. Deswegen muß im Assembler (in meinem Fall MASM 5.0) allerdings mehr definiert werden, zB der Stack. Beim 6502 ist der Stack automatisch auf Page 1. Beim 8080 definiere ich den Stack über das Stackpointer Register und reserviere dazu genügend bytes am Ende des Programms. Rückwärts, versteht sich. Wo befindet sich der Stack einer COM-file, wenn er nicht explizit definiert wurde. Setzt diesen MASM automatisch am Ende des Programms, so wie ich es im 8080 Assembler manuell mache ?


    3. Ich schreibe ein helloworld-Programm mit dem MASM mit org 0100h. CP/M und DOS Programme beginne immer ab 0100h. Ich lade das Programm, es steht im Speicher. Wenn ich in den Debugger gehe und mit "-d 0100" den Dump anschauen will, steht da nichts. Wie finde ich raus, in welchem Segment das Programm steht ?


    4. "INT"-Anweisungen stehen für System-Calls (8080 Slang) oder Kernal-Routinen (Cevi Slang). Ich schiebe also die gewollten Daten in die dementsprechenden Register und führe die Int(errupt) Funktion aus. Der Name INTerrupt wahrscheinlich deswegen, weil er das Hauptprogramm kurz verlässt, es also unterbricht, diese "Unterbrechungsroutine" wird ausgeführt und dann springt er wieder zurück ins Hauptprogramm. Der XT hat weniger "Kernalfunktionen" als zB der AT oder der XT286. Man hat zB nicht die Funktion "Festplatte parken" beim XT. Dann gibt es neben den "Kernalroutinen" auch noch die INT-Routinen von MS-DOS. Dabei handelt es sich quasi um eine Erweiterung der "Kernalroutinen" eine Ebende höher..

    Die große Frage ist, wie greife ich zB auf die Funktionen des Festplattencontrollers zu ? Das XT Bios, oder genauer gesagt die Firmware im ROM, hat keine Funktion bzw. keinen INT, um einen Seek in Richtung Festplatte zu schicken. Ich müsste diesen Seek direkt am Festplattencontroller aufrufen, um zB die heads zum parken in die landing zone zu fahren. Damit könnt ich mir ein kleines Festplattenparkprogramm, 100% angepasst auf meine Platte, schreiben.. Wie geht man da vor ?


    5. Vom 8080 kenne ich die Portadressierung, vom 6502 den Weg der Speicheradressierung.. Wenn ich beim zB 8080 eine "1" aus der Seriellen Schnittstelle COM1 an Port 22h ausgeben will, dann schreibe ich einfach:


    Code
        org 0100h        ; Programmstart 0100h
        mvi a,31h        ; 31h (Ascii-Code für Ziffer "1") in den Akkumulator
        out 22h          ; Akku-Inhalt an Port 22h ausgeben
        ret              ; Return from Subroutine  


    Wie man das beim 8088 über eine "Kernalroutine" macht, das weiß ich. Direkt über einen Port hab ich nicht probiert. Aber was hat das mit diesen IRQs (interrupt requests) zu tun ? Warum liegen die COM-Schnittstellen zB auf IRQ3 und IRQ4 ?



    Vielleicht kann mir jemand helfen ?!



    Vielen Dank !!!!


    Gruß Jan

  • Warum liegen die COM-Schnittstellen zB auf IRQ3 und IRQ4 ?

    Das wurde einfach mal so als Standard definiert. Beim XT gibts ja nur 8 IRQs, und es gibt ja kein IRQ-Sharing (wie bei EISA, MCA oder PCI), also hat man das einfach mal festgelegt.


    Zum Parken der Festplatte, siehe Anhang, das musst du nicht selbst schreiben, und es muss auch nicht an die Geometrie einer bestimmten Platte angepasst werden.


    Deine Annahmen zur Segment-Überlappung sind richtig, du siehst in beiden Segmenten mit einem gewissen Offset das Selbe, soweit sich die Segmente überlappen.


    Den Rest weiß ich nicht mehr, weil ich damals 808x Assembler gehasst habe, wie die Pest. Wenn du mal 68000 Assembler verwöhnt bist, ist das so...

  • Es geht mir ja nicht nur um das Parken der Platte. Unterm Strich gehts mir ja eher darum, wie so etwas programmiert wird. Hintergründe erblicken :)

    Trotzdem danke für das Tool !


    Ja, das war natürlich der riesen Vorteil des 68000ers.... eine vollends lineare Adressierung. Vielleicht komme ich irgendwann auch mal dazu.. Aber momentan will ich mal in die Welt des 8088 schnuppern...



    Vielen Dank !


    Gruß Jan

  • zu 1)


    Ja, Adresse 0000:000F ist das selbe wie 0001:0000



    zu 2)


    COM kann auch mehr adressieren.

    Es ist halt ein vereinfachtes Speichermodell damit Entwickler die vom Z80 kommen sich einfacher tun.



    zu 3)


    Kann mich nicht mehr an Details erinnern.


    Der Clou an Segmenten ist aber, dass man sich um absolute Adressen nicht mehr kümmern muss.

    Beim 80286 real mode ist das ja noch direkt abgebildet.

    Aber der Gedanke dahinter ist da schon Virtualisierung.


    Das OS soll sich darum kümmern, wo das Ding im Speicher wirklich ist.

    Weder der Coder noch das Programm selbst sollen das wissen und auch nicht manipulieren können.

    So hat man Schutz vor Programme, gegeneinander und auch zum OS.



    zu 4)


    Ja leider, das INT Modell ist zeitfressend und hat viele andere Nachteile.

    Aber es ist halt easy in der Anwendung, Register und INT dann macht es halt was das OS her gibt.



    zu 5)


    Ist da nicht anders, IO Adressen kann man zugreifen im REAL Mode, im protected Mode ist das normalerweise geschützt und Ring 0 vorbehalten.


    IO Adressen direkt zuzugreifen ist Unfug.

    So macht man sich Hardware abhängig und die Hardware Kompatiblitäts Abhängig.

    Ab 8086 war man bedacht diese Dinge aus der Applikations programmierung Stück für Stück raus zu nehmen.

  • Adressierung:

    Du hast 4 Stück 16 Bit Segmentregister. Diese werden um 4 Bit nach links verschoben zum verwendeten 16 Bit Adressregister (z.B. HL) dazuaddiert.


    Code
    0000HHHHHHHHLLLLLLLL
    +
    SSSSSSSSSSSSSSSS0000
    =
    Adresse im 1 MByte Adressraum

    12 Bit überlappen sich dann natürlich. Erhöhst Du das Segemntregister um 1 ist es dasselbe wie HL um 16 zu erhöhen.


    I/O passiert genauso wie beim 8080 mit IN und OUT Befehlen, allerdings mit 16 Bit Adressen, deswegen

    0x3f8 für COM1 und 0x2f8 für COM2. Diese Adressen sind willkürlich gewählt aber "Standard".


    Die Interrupts sind ebenso willkürlich vergeben, bei den meisten alten Karten kannst Du das auch anders jumpern.

    Bedenke das damals[tm] die wenigsten Treiber Interrupt Sharing unterstützt haben...


    Der INT Befehl macht dasselbe wie ein Hardware Interrupt.


    Übrigens der 386 und 486 starten alle in dem 8088/8086 Modus mit 16 Bit Segment Registern und 4 Bit Verschiebung. Solange Du unter DOS arbeitest, sollte da kein Unterschied zu spüren sein.

  • Hallo Jan,


    vielleicht hilft es Dir, Dir darüber klar zu werden, daß es Software Interrupts und Hardware Interrupts gibt.


    Deine INT Anweisung löst einen Software Interrupt aus. System Calls per Software Interrupt statt per "normalem" Unterprogramm-Aufruf zu machen, ist (vereinfacht) erstmal nur eine Konvention, die der Ersteller der System Calls vorgibt und an die Du Dich als Verwender der System Calls halten mußt.


    Diese IRQ3/4 der COM Schnittstelle stehen im Gegensatz dazu für Hardware Interrupts. Da gibt es an der CPU tatsächlich physische Anschlüsse, die wenn sie von externer Hardware wie einer seriellen Schnittstelle auf einen bestimmten Pegel gezogen werden, einen Interrupt auslösen.


    Software Interrupts sind synchron. Hardware Interrupts sind asynchron, können also jederzeit zwischen die Ausführung zweier CPU Instruktionen eingefügt werden. Das stellt natürlich sehr speziellen Anforderungen an der Code, auf den Hardware Interrupt reagieren soll. Da muß "alles" so hinterlassen, wie er es vorgefunden hat. Das kennst Du ja von Raster-Interrupt Handlern auf dem C64.


    Dieses IRQ3/4 ist also konzeptionell das gleiche wie ein Raster-Interrupt beim C64. Der C64 hatte halt nur zwei solche Leitungen, nämlich IRQ und NMI, während neuere CPUs mehr Leitungen haben, die man nummeriert.


    Gruß,

    Oliver

  • Ja, das war natürlich der riesen Vorteil des 68000ers.... eine vollends lineare Adressierung. Vielleicht komme ich irgendwann auch mal dazu.. Aber momentan will ich mal in die Welt des 8088 schnuppern...

    Naja, der Vorteil ist auch ein Nachteil.

    Undenkbar heutzutage, viel zu gefährlich.


    So lange du eine Kiste hast, wo nur dein Programm läuft, ist das alles okay.

    Sobald aber mehrere Programme laufen, die sich nicht kennen oder deren Ursprung unklar ist ...


    Ab dem 64 Bit Pentium hast du ja wieder deinen 32 Bit Adressraum linear.

    Und zwar exklusiv für dein Programm.

    Jedes laufende Programm hat dies ... ;)

  • Das Zauberwort heißt "Sandbox".


    Moderne CPU's bieten alle dieses Konzept.

    Die Hardware bietet viele getrennte Adressräume, die geschützt sind und konfigurierbar.

    So kann ich für eine beliebige Software eine virtuelle Umgebung schaffen genau für ihre Bedürfnisse.

    Und ich kann die Rechte der Software beliebig von außen steuern: RAM Größe, IO Zugriff, OS Zugriff.

  • Naja, ab dem 68020 gibts eine MMU, die ebenso Prozesse voneinander und vom Kernel trennen kann. Immerhin läuft ab dem 68020 UNIX drauf.

  • Also hat man das vor allem gemacht, um Programme kompatibel zu machen, egal wie die Ausbaustufe des Rechners war ?! Das war ja bei CP/M ein riesen Problem. Im Grunde musste man die Equals anpassen im Listing und das Programm individuell auf das eigene System konfigurieren und assemblieren ?! Sehe ich das richtig ?

    Und mit dieser Segmentgeschichte ist es also egal, ob 64kb oder 640kb Hauptspeicher insgesamt da sind, das OS sucht den passenden Speicherbereich. Vorrausgesetzt das Programm passt in den Speicher, versteht sich. Dennoch verstehe ich den Aufbau (noch) nicht.

    Wie arbeitet ein assmbliertes Programm mit Sprüngen (zB bei Schleifen oder Subroutines), wenn die Adressen nicht 100% klar sind ?



    Gruß Jan

  • Naja, ab dem 68020 gibts eine MMU, die ebenso Prozesse voneinander und vom Kernel trennen kann. Immerhin läuft ab dem 68020 UNIX drauf.

    Klar, jede moderne CPU macht das.

    Ist heute Standard.

    Ohne geht heute nicht mehr.

  • Da gibt es an der CPU tatsächlich physische Anschlüsse,

    Nein, nicht ganz. Die von ISA-kommenden IRQs sind nicht direkt an der CPU, sondern am Interrupt-Controller-Chip. XTs haben dazu den 8259 Chip. Der merkt sich die IRQ-Nummer vom ISA-Bus, und meldet an seinem IRQ-Ausgang an die CPU, dass es einen IRQ gab. DIe CPU muss sofort darauf reagieren und das Register im 8259 auslesen, um zu wissen, welcher IRQ es letztlich war. Bei ATs sind zwei Stück davon "kaskadiert", das heißt der Ausgang des AT-8259 hängt an IRQ2 des XT-8259. IRQ2 auf dem ISA-Bus ist dann beim AT IRQ9. Wenn also im AT die CPU vom XT-8259 ein IRQ bekommt, und festgestellt wird, dass es IRQ2 war, bedeutet es, dass es ein AT-IRQ, also IRQ 9-14 ist, und muss dann nochmal im AT-8259 nachsehen, um die eigentliche IRQ-Quelle zu finden. Das ist ziemlich umständlich, aber es bleibt halt halbwegs kompatibel zum XT. Man hätte es auch über einen neuen Interrupt-Controller, der direkt 16 IRQ-Eingänge hat, lösen können, das wäre aber halt evtl. nicht ganz so kompatibel geworden.

  • Danke für die Korrektur. Jetzt wo ich es lese, erinnere ich mich daran, daß ich das alles sogar seinerzeit zur Ansteuerung einer Laborhardware selbst so gemacht habe. Na ja, ich werde wohl zu alt für diesen Sch... ;-))

  • Wie arbeitet ein assmbliertes Programm mit Sprüngen (zB bei Schleifen oder Subroutines), wenn die Adressen nicht 100% klar sind ?

    Die sind relativ innerhalb des Programm-Segments. Also auch wenn du einen absoluten Sprung mit einer 16-Bit-Adresse machst, erfolgt die weiterhin innerhalb deines Programmsegments. Größere Sprünge als 16 Bit gibts nicht innerhalb eines Segments. Du musst dir das so vorstellen, dass dieses Programm genau wie auf einem 8-Bit-Prozessor läuft, du kommst aus dem Segment nicht raus, auch nicht mit relativen Sprüngen am Segmentanfang rückwärts, oder am Segmentende vorwärts. Das verhält sich genau wie ein 8-Bit-Prozessor mit 64kB RAM. Das ist zumindestens der einfache Fall. Es gibt auch Sprünge von Programm-Segment zu Programm-Segment, aber in diese Sphären habe ich mich damals nie getraut, ätzend, sowas habe ich dann lieber einen Compiler machen lassen... Das ist quasi der Sprung von einer .com zu einer .exe Datei...


    Übrigens, das deutet das Wort "Programm-Segment" schon an, wenn du mit Daten arbeiten willst, musst du vom Betriebssystem auch ein Datensegment anfordern. Das kann auch wiederum bis zu 64 kB groß sein, und erklärt z.B., warum bei diversen einfachen DOS-Textverarbeitungsprogrammen oder Editoren die geschriebenen Briefe nicht länger als 64 kB sein könnnen...

  • .COM Dateien: alles (Code und Daten) in einem Segment, feste Ladeadresse (normalerweise 0x100). Ideal um CP/M Programme zu portieren. In der Regel nur relative Sprünge im Segment (als "near" bezeichnet, brauchen sie nur den Offset relativ zum Segment). War damals sicher ein wichtiges Argument für die segmentierte Architektur - Programme wie Turbo-Pascal oder Wordstar konnten so sehr schnell von CP/M-80 auf den PC portiert werden.


    .EXE Dateien: beliebig viele (Code und Daten) Segmente, variable Ladeadresse, Code wird vom Lader verschoben ("relocated") und Sprunganweisungen werden vom Lader an die tatsächliche Lage im Adressraum angepasst. Damit ist man viel flexibler in der Speicherorganisation. Dazu enthält eine .EXE Datei immer eine Tabelle mit den umzurechnenden Adressen; eine .COM Datei hat so etwas nicht. Sprünge können "near" (Offset, brauchen dann keine Umrechnung) oder "far" (Segment:Offset, brauchen Umrechnung) sein.


    Die meisten 80xx und MS-DOS/Assembler Bücher beginnen mit einer ausführliche Beschreibung dieser Details.

  • Also hat man das vor allem gemacht, um Programme kompatibel zu machen, egal wie die Ausbaustufe des Rechners war ?!

    Und aus Performancegründen, denn innerhalb eines Segmentes hast du ja nur 16-Bit-Adressierung. Der Code wird kürzer und schneller.

    Dafür war es ein wenig komplizierter.


    Im Gegensatz z.B. zum 68000 mit seinem linearen Adressraum. Da hat man eben 32-Bit-Adressen. Obwohl es da wohl auch relative 16-Bit-Adressierung gab, aber das ist doch nicht das selbe.

    • i-Telex 7822222 dege d

    • technikum29 in Kelkheim bei Frankfurt

    • Marburger Stammtisch

    Douglas Adams: "Everything, that is invented and exists at the time of your birth, is natural. Everything that is invented until you´re 35 is interesting, exciting and you can possibly make a career in it. Everything that is invented after you´re 35 is against the law of nature. Apply this list to movies, rock music, word processors and mobile phones to work out how old you are."

  • Dieses IRQ3/4 ist also konzeptionell das gleiche wie ein Raster-Interrupt beim C64. Der C64 hatte halt nur zwei solche Leitungen, nämlich IRQ und NMI, während neuere CPUs mehr Leitungen haben, die man nummeriert.


    Ist das wirklich so - oder doch eher nur eine IRQ Leitung und dazu ein "Code" aus z.B. vier Bit, der sagt welche Karte da jetzt am anderen Ende der Leitung verantwortlich war ?


    Hat sich erledigt, wurde oben gut beantwortet. Man sollte doch erst zu Ende lesen ...


    Kompliziert geht beim PC aber anscheinend doch immer. Viel umständlicher geht es ja kaum noch. Und langsam ist es wahrscheinlich auch noch.

    -- 1982 gab es keinen Raspberry Pi , aber Pi und Raspberries

    Einmal editiert, zuletzt von ThoralfAsmussen ()

  • Vielen Dank nochmal für die Antworten. So langsam wird mir einiges klarer. Ich bleibe am Ball. Das mit den Hardware-Interrupts ist auch sehr interessant, da hab ich mich mal durch die Schaltpläne geschlagen.


    Im Grunde muß ich mir ja über die Stelle der Segmente keine Gedanken machen, das macht ja der Assembler ?! Aber, wenn er eh das Programm immer an einer anderen Stelle ablegt, dann wäre ja debugging schwierig, oder verstehe ich das falsch ? Und warum brauche ich dann den org Befehl, wenn es keine Rolle spielt ? COM-Files beginnen doch normal immer bei 0100h, aber in welchem Segment ?! :)


    Ich hab mal zum Probieren ein kleines "Hello VZEKC" Programm geschrieben...



    In dem Code ist kein Stack definiert, was man bei einer COM-file anscheinend nicht zwingend braucht. Als Texteditor habe ich den Nortoncommander benutzt. Anschließend mit MASM assembliert und mit dem Linker aus der Object-Datei eine EXE-Datei erstellt. Dabei gab es natürlich den Fehler, dass der Stack fehlt. Aus der EXE-Datei habe ich dann mit exe2bin eine COM-File erstellt. Und diese funktioniert ! :)


    Momentan bin ich ein Buch am lesen "Mapping the IBM PC and PCjr", das macht einen ganz guten Eindruck. Außerdem sammele ich Informationen, mit welchen Routinen bzw. Interrupts ich am Besten was erledigen kann..



    Gruß Jan

  • Hat evtl jemand Interesse Assembler für eine neue 32 Bit RISC CPU zu lernen?


    Kollege entwickelt da gerade was.

    Interessant wäre es auf jeden Fall. Aber ich persönlich möchte mich eher auf Intel 8080, 8086 und MOS 6502 konzentrieren. Mein Ziel ist es, an allen Geräten in meinem Bestand was auf Prozessorebene machen zu können...

    Evtl. wäre es besser, wenn du dafür einen eigenen Thread aufmachst. Evtl. interessieren sich Leute dafür, die sich nicht für den x86 interessieren und in Folge dessen diesen Thread erst gar nicht öffnen ?!


    Gruß Jan

  • Nachdem ich jetzt das COM-Port Problem bei meinem Cordata PPC gelöst hatte, wollte ich mir mit Microsoft MacroAssembler 5.0 ein paar Zeilen Code schreiben, um die Com-Ports zu konfigurieren und anschließend das ganze mit einer schicken Rückmeldung quittieren. Ich weiß, dass das auch mit dem MODE Befehl geht. Aber es geht mir ja um das Lernen des Assemblers. Wichtig dabei war mir, dieses Mal den Stack zu definieren und das Programm somit als lauffähige EXE Datei zu erstellen. Außerdem würde ich gerne den Text bzw. die spätere Rückmeldung in das Datensegment auslagern. Für das Extrasegment hab ich keine Verwendung. Hier mal der Code:



    Das Programm funktioniert, also die COM-Ports werden sauber konfiguriert. Aber anstatt nur "Das ist ein Text..." kommen da erst schätzungsweise 7-8 Zeilen Datenmüll und erst dann der gewünschte Text.


    Wie rufe ich aus dem Code Segment gezielt meine Bytes im Datensegment auf, um sauber nur meinen Text dar zu stellen ?



    Vielen Dank !


    Gruß Jan

  • Initialisiert ist es ja. Der Text erscheint ja, allerdings davor Datenmüll.. Ich habe festgestellt, dass es schwierig ist, im Netz Beispiel Sources im Bereich x86 Vintage computing zu finden. Wenn man nach x86 codes sucht, ist das sehr breit gefächert, da es zum einen viele verschiedene Assembler gibt und zum anderen die Technik ja quasi noch aktuell ist..

    Das macht es schwieriger, als zB beim C64 oder so... Man muß sich quasi durch alte Bücher durcharbeiten.. Ich muß nachher nochmal ein bißchen blättern... :)



    Gruß Jan

  • Ich haette jetzt spekuliert das ds irgendwo in ein Segment davor zeigt, der Muell den du siehst dein Code ist, und irgendwann ist er durch das Code Segment durch und landet im Datensegment, wo dann der richtige Text steht. D.h, der Datenmuell den du siehst ist dein Programmcode. Ich habe hier grad kein MASM, sonst koennte ich das mal schnell testen...

  • Hmm,


    muss man das ds register noch initialisieren, oder reicht das 'assume ds:data'? Kenne mich mit dos assembler nicht so wirklich aus, habe aber schon sowas wie 'mov ax,@foo; mov ds,ax' gesehen.

    Nee das "assume" erzeugt keinen Code und ändert letztlich nix an dem Inhalt deines Segment Register!


    Es ist ein Pseudo Code, um dem Assembler mitzuteilen wie dein Segment liegt.

  • Mal eine kleine Frage: Warum hast Du das nicht im passenden Forum Assembler gepostet ?

    Ja, gute Frage. Wenn möglich, bitte verschieben ! Danke !


    Ich bin auf der Arbeit und kann jetzt nicht mehr nach schauen. Aber ich habe mir überlegt, das „end start“ direkt nach dem Code Segment zu setzen und nicht erst ganz am Ende. Evtl ist das ja des Rätsels Lösung ?!


    Gruss Jan

  • TINY und EXE pasen nicht zusammen. TINY ist für COM Dateien gedacht.


    Und wie dxl schrieb, muss man das DS Register noch auf des gewünschte Datensegment setzten (EXE Dateien könne ja auch viele davon haben.


    Hier nochmal ein kleinen Gerüst für eine EXE Datei, in xx.asm abspeichern.


    Assemblieren und linken:

    masm xx;

    link xx;


    Starten:

    xx


    Auch mal mit Umleitung:

    xx > xx.txt


    Die 1 cent Zusatzfrage: Warum der Unterschied in der Ausgabe bei biden Ausführungen?



  • Hallo.


    Vielen Dank für die Antworten ! Ich hab mir das alles natürlich durchgelesen und es mit meiner Literatur "zurück verfolgt"...


    Hier mal der funktionierende Code:



    Den Source hab ich natürlich assembliert mit MASM und dann mit dem Linker eine EXE-file draus gemacht. Comset.exe hat 847 bytes.


    Ich musste zuerst das directive ".model" von tiny auf small setzen. "Tiny" bedeutet, dass sich alles innerhalb eines Segments abspielt. Bei small werden die Segmente jeweils in einem 64kb Bereich geführt. Sehr wahrscheinlich ist deswegen eine EXE-file mit dem gleichen Funktionsumfang wie eine COM-file immer größer.


    Dann wurde ja weiter oben schon von dxl geschrieben, dass das Datensegment nicht richtig initialisiert ist. Genau so war es. Ich dachte, mit assume sei das erledigt. Dem war es nicht so. Wie Martin bereits sagte, es kann ja mehrere Datensegmente in dem Programm geben.

    Dem zu Folge muß ein Pointer auf das jeweilige, benötigte Datensegment gesetzt werden. Das geschieht über das DS Register.


    Über die Funktion 09 im Interrupt 21h möchte ich den Textstring darstellen. Offset bedeutet, dass er bei der Abfrage vom Anfang des Datensegments zu dem angegebenen Label mit dem dementsprechenden Text springt, bzw. diesen darstellt.


    Sinniger ist dieses winzige Programm natürlich als COM-file. Das wird es letzten Endes auch werden. Aber es geht ja um das Lernen und den Spaß am Vintage-computing... :)


    Gruß Jan