flat assembler
Message board for the users of flat assembler.
![]() |
Author |
|
volkert 20 Jul 2017, 22:47
Hello everyone! This is my first post in this forum.
![]() Some months ago, I decided to take up the task of porting the source code of two useful DOS tools from TASM to FASM syntax. The main reasons being that I wanted to liberate the source code from their dependency on proprietary build tools, because I wanted to start dabbling with assembly programming in my spare time and because I was hoping it could provide a nice basis for a possible future Sound Blaster (and Tandy) emulator for computers with more modern audio devices, such as AC'97 and/or Intel HD Audio. I know you can pretty much emulate entire systems with DOSBox these days, but where's the fun in that? ![]() First a little background on my assembly skills: I've played with it in my late teens, and learned and practiced some assembly language at college, which is quite some time ago. I am an experienced Java developer by trade, but lately I've been drawn more to some wholesome old-fashioned low-level software engineering. Ah, the rose-tinted glasses of nostalgia. ![]() The source code in question was released by the author and with his permission, I published them on GitHub: https://github.com/volkertb/temu-vsb My first priority seemed getting rid of the dependency on TASM, which meant porting over the code from one assembly dialect to another. FASM appeared to be the obvious choice, considering its DOS support, its developer community and the available documentation. This porting step seemed a fun and interesting step to ease into assembly language at first, but quickly proved much more complicated than I had expected. Eventually, I started to realize that I may have bitten off more than I can chew. ![]() So far, I've only tried porting TEMU (the Tandy Emulator), since it appeared to be the simplest of the two. The first steps weren't too hard to figure out, thanks to some Googling:
At this point, I hit my first snag: Code: ..\386pdef.asm [7]: locals @@ error: illegal instruction. It took me quite a bit of Googling to figure this one out and I could hardly find any info on this, but I believe this directive meant something like "treat all labels prefixed with @@ as local labels". It would appear to be a redundant declaration, since "@@" is apparently the default prefix for local labels already. Please correct me if these assumptions are wrong! Anyway, assuming this, I removed this directive, although it left me worried about how different the default behavior with regards to local and non-local labels would be in FASM. Does FASM also automatically treat labels prefixed with "@@" as locals, and if not, how can I tell it to do so? Anyway, I decided to just try to continue fixing each error as I encountered it. The next issue I came up with was that struc blocks apparently have a different syntax in FASM than in TASM. The first struc the assembler encountered and choked on: Code: Desc386 struc SegLimit dw ? ; limit bits (0..15) Base0to15 dw ? ; base bits (0..15) Base16to23 db ? ; base bits (16..23) AccessRights db ? ; access rights byte Granularity db ? ; granularity & default op. size Base24to31 db ? ; base bits (24..31) Desc386 ends I changed it into this and that seemed to satisfy FASM: Code: struc Desc386 { SegLimit dw ? ; limit bits (0..15) Base0to15 dw ? ; base bits (0..15) Base16to23 db ? ; base bits (16..23) AccessRights db ? ; access rights byte Granularity db ? ; granularity & default op. size Base24to31 db ? ; base bits (24..31) } I then proceeded to similarly update the next struc that the assembler complained about: From this: Code: DT386 struc TableSize dw ? TableAddr dd ? DT386 ends ...to this: Code: struc DT386 { TableSize dw ? TableAddr dd ? } After the above fix, I ran into the next error: Code: ..\386pdata.asm [37]: DTload DT386 <> error: invalid macro arguments. So I tried removing the diamond ("<>") representing no arguments, and tried building again. I wasn't sure about this "fix" either, but I was eager to move on. And this brought me to the following error: Code: ..\386pdef.asm [75]: GDTdescr macro Name,DFlags,Granularity error: illegal instruction. It apparently pertained to the following macro definition: Code: GDTdescr macro Name,DFlags,Granularity Name: Desc386 <0FFFFh,,,DFlags,Granularity> @&Name = GDToffset GDToffset = GDToffset+8 endm ...Which I gave the curly-braces-treatment as well: Code: macro GDTdescr Name,DFlags,Granularity { Name: Desc386 <0FFFFh,,,DFlags,Granularity> @&Name = GDToffset GDToffset = GDToffset+8 } After applying the same fix to a subsequent macro the assembler complained about, the assembler ran into another struc that I had to rewrite: Code: ;************************ TSS structure definition *************************** TSSblk struc TSSlink dd ? TSSespP0 dd ? TSSssP0 dd ? TSSespP1 dd ? TSSssP1 dd ? TSSespP2 dd ? TSSssP2 dd ? TSScr3 dd ? TSSeip dd ? TSSeflags dd ? TSSeax dd ? TSSecx dd ? TSSedx dd ? TSSebx dd ? TSSesp dd ? TSSebp dd ? TSSesi dd ? TSSedi dd ? TSSes dd ? TSScs dd ? TSSss dd ? TSSds dd ? TSSfs dd ? TSSgs dd ? TSSldt dd ? dw ? TSSiomap dw ? TSSblk ends ... Which I modified like so: Code: ;************************ TSS structure definition *************************** struc TSSblk { TSSlink dd ? TSSespP0 dd ? TSSssP0 dd ? TSSespP1 dd ? TSSssP1 dd ? TSSespP2 dd ? TSSssP2 dd ? TSScr3 dd ? TSSeip dd ? TSSeflags dd ? TSSeax dd ? TSSecx dd ? TSSedx dd ? TSSebx dd ? TSSesp dd ? TSSebp dd ? TSSesi dd ? TSSedi dd ? TSSes dd ? TSScs dd ? TSSss dd ? TSSds dd ? TSSfs dd ? TSSgs dd ? TSSldt dd ? dw ? TSSiomap dw ? } After the above fix (or attempted fix?), I finally ran into an error that so far I have been unable to fix: Code: ..\386pdata.asm [38]: TaskSegment TSSblk <,,,,,,,,,,,,,,,,,,,,,,,,,,offset IOportMap-offset TaskSegment> error: invalid macro arguments. If I understand this syntax correctly, it's trying to equate TaskSegment to TSSblk, only with the last dw (TSSiomap?) set to that offset and the earlier arguments remaining empty (question marks?). But this is a struc, not a macro, right? Do strucs even have parameters like that? And I also remember reading somewhere that the "offset" keyword is not to be used in FASM and in most cases it just can be omitted. But at any rate, at this point I'm stuck. I've tried a lot of different things here, but nothing worked. Also, even after extensive searching, Google turned up nothing on any FASM-specific syntax regarding "parameterized strucs" or whatever this thing was supposed to be. Please tell me, am I even close to getting this to build correctly with FASM, or am I in way over my head here? I'm willing to learn, honestly! And also, it would be really cool to liberate this code from TASM, so that it can be built and further tinkered with using nothing but Free and open source tools instead. Thank you in advance for reading through this rather long summary of my current spare-time conundrum. I hope I'll encounter some knowledgeable and helpful people here whom I can learn from. ![]() |
|||
![]() |
|
Tomasz Grysztar 26 Jul 2017, 11:06
Shahada wrote: In FASM you have to do like this: Code: struc TSSblk TSSlink:?,TSSespP0:?,TSSssP0:?,TSSespP1:?,TSSssP1:?,TSSespP2:?,TSSssP2:?,TSScr3:?,TSSeip:?,TSSeflags:?,TSSeax:?,TSSecx:?,TSSedx:?,TSSebx:?,TSSesp:?,TSSebp:?,TSSesi:?,TSSedi:?,TSSes:?,TSScs:?,TSSss:?,TSSds:?,TSSfs:?,TSSgs:?,TSSldt:?,TSSiomap:? { .TSSlink dd TSSlink .TSSespP0 dd TSSespP0 .TSSssP0 dd TSSssP0 .TSSespP1 dd TSSespP1 .TSSssP1 dd TSSssP1 .TSSespP2 dd TSSespP2 .TSSssP2 dd TSSssP2 .TSScr3 dd TSScr3 .TSSeip dd TSSeip .TSSeflags dd TSSeflags .TSSeax dd TSSeax .TSSecx dd TSSecx .TSSedx dd TSSedx .TSSebx dd TSSebx .TSSesp dd TSSesp .TSSebp dd TSSebp .TSSesi dd TSSesi .TSSedi dd TSSedi .TSSes dd TSSes .TSScs dd TSScs .TSSss dd TSSss .TSSds dd TSSds .TSSfs dd TSSfs .TSSgs dd TSSgs .TSSldt dp TSSldt .TSSiomap dw TSSiomap } TaskSegment TSSblk ,,,,,,,,,,,,,,,,,,,,,,,,,IOportMap-TaskSegment Alternatively this could be simplified for this specific application: Code: struc TSSblk TSSiomap:? { .TSSlink dd ? .TSSespP0 dd ? .TSSssP0 dd ? .TSSespP1 dd ? .TSSssP1 dd ? .TSSespP2 dd ? .TSSssP2 dd ? .TSScr3 dd ? .TSSeip dd ? .TSSeflags dd ? .TSSeax dd ? .TSSecx dd ? .TSSedx dd ? .TSSebx dd ? .TSSesp dd ? .TSSebp dd ? .TSSesi dd ? .TSSedi dd ? .TSSes dd ? .TSScs dd ? .TSSss dd ? .TSSds dd ? .TSSfs dd ? .TSSgs dd ? .TSSldt dp ? .TSSiomap dw TSSiomap } TaskSegment TSSblk IOportMap-TaskSegment |
|||
![]() |
|
rugxulo 26 Jul 2017, 12:54
volkert wrote:
All of this is much harder than it sounds. But for tiny model code in .COM format, it's relatively easy to disassemble (which is actually extremely helpful when the sources are in some obsolete assembly dialect). Of course, I never fully understood TASM (structs? macros? blech!), so I'm not directly much help. But I sympathize, and I too have an interest in converting old code. I don't think jumping to FASM is a good first move. Maybe later, but not right now. (Lack of OMF/OBJ support is one omission, which AFAIK thankfully you don't rely on here.) TASM is not really easily available anymore, nor is it actively developed much, but it's still allegedly bundled with some Embarcadero Products. So it's not even freeware (except maybe as trialware/demo). For simplicity, at first, I would recommend using a clone or similar dialect, at least until you can replicate the original builds. So that would mean (freeware) LZASM, (OSI) OpenWatcom WASM (-zcm=tasm) or (OSI) JWasm (MASM v6) or similar. Once you get it assembling in such widely-available tools (even if not 100% GNU friendly), then it should be much easier to work with. (MASM v6 is well-documented in the online copy of The Art of Assembly.) Seriously, I'm all for Free Software, but some things are impossible. So it doesn't make sense to make things harder on yourself (at first) when everything else in the ecosystem isn't perfect either. "But OpenWatcom isn't Free". Well, it's OSI, and it's easily available everywhere, even with sources. FreeDOS relies heavily on it, and AFAIK no better DOS OS exists. So there's little point in struggling to surpass FreeDOS in freedom if you're exclusively relying on it anyways. Do you know what I mean? Yes, sure, be perfect, use 100% Free/libre tools, but if it's easier to just use freeware, that may be more pragmatic. (CuteMouse was converted from TASM to JWasm by Eric Auer. Yes, it's much harder than it sounds.) Apparently one can buy a used paperback copy of Mastering Turbo Assembler (2nd) (circa 1995 but 900+ pages!) from Amazon for $8 USD. If you're really interested in TASM dialect, that might be a worthy investment. Other references exist (e.g. PDFs):
|
|||
![]() |
|
rugxulo 26 Jul 2017, 13:00
Tomasz Grysztar wrote: Or use the "struct" macro which makes it all a bit easier. But the DOS version of FASM doesn't have this macro (missing "INCLUDE/MACRO/STRUCT.INC" file). |
|||
![]() |
|
Shahada 26 Jul 2017, 18:29
Tomasz Grysztar wrote: This does not initialize the variable values properly. The correct way to do it would be: Thanks for the correction, I meant like in your first example but after copy-paste I forgot to replace '?' with the argument names. |
|||
![]() |
|
ACP 29 Jul 2017, 09:11
As I've been using TASM and A86 back in 286 days exclusively (A86 only for small COM files, mostly TSR since on AT it was blazing quick comparing to TASM and MASM and did not required separate linker) I may add a bit of info here.
Paradigm assembler is TASM with changed copyright and few minor changes in the manual. Use original TASM manual for 5.x version (keep in mind that in original TASM 5 package DOS real mode TASM is 4.x, 5.0 refers to TASMX and TASM32 only) the one on the bitsavers site. The manual has mostly everything you need with maybe an exception of handling tlink in some cases. First assemble and link the code with TASM 5 package and see if you have correct executable files as output. Comparing original COM file with your output will be handy too. Next check if your code is using MASM or IDEAL mode. FASM syntax has been designed around IDEAL model ideas but is pretty far away from it by now. Most people used MASM mode actually, and very few went IDEAL path. Therefore porting to MASM first as in one of above suggestions makes little sense IMHO - especially considering some limited MASM 6.x compatibility in TASM. The (lack of) usage of IDEAL keyword will tell you more about changes you need to make. The LOCALS does not only apply to labels only but to other symbols as well. By default LOCALS prefix is @@ so no need to add it in TASM. @@ prefix tells TASM to treat label locally for current scope (PROC for example). Whan TASM is in MASM mode this works differently. As for mixing 16 and 32bit in COM/MZ EXE files in case of FASM this should pose not problems. I've done it in the past for some DPMI related code. There is an example of entering DPMI code using FASM - take a look at it to see how it is done if memory servers me well. Finally take a look at my blog: https://corexor.wordpress.com/ You will find some tidbits about TASM. Good luck with you project: you are facing a lot of fun. I know I had my dose when converting DesqView sdk to FASM. |
|||
![]() |
|
rugxulo 30 Jul 2017, 11:20
ACP wrote: As I've been using TASM and A86 back in 286 days exclusively (A86 only for small COM files, mostly TSR since on AT it was blazing quick comparing to TASM and MASM and did not required separate linker) I may add a bit of info here. A86 was fast because it was one-pass only (and only optimized backwards jumps without explicit override). I'm not majorly complaining here, but it required some workarounds to use. It hated the MASM/TASM style "mov ax,MyVar" (where FASM would be "mov ax,[MyVar]") unless declared before use. It only halfway barely supported old MASM v5 syntax. For laughs (although it omits A86), here's an old Dr. Dobbs article (with speed tests) from 1989 called MS-DOS Assemblers Compared. TASM was one-pass only until v2 (and even then it was optional). Even PSR Invaders forgot to use multi-pass in final release, thus it had inefficient encodings (roughly 9300 vs. 9194 bytes). Sure, one-pass is faster, but it's not preferred (if you can afford the slower assembling speed, and we've long ago jumped the shark there). Quote:
I think LZASM ("Ideal" only) is also just modified TASM as well (presumably licensed). Borland (or whoever) basically abandoned TASM after year 2000 (5.3) although Embarcadero allegedly has 5.4 nowadays, but it's probably very minor changes. Quote:
Yes, the last real-mode version was something like 4.1, but you can run WDOSX's STUBIT on (PE/Win32) TASM32.EXE (which I did from the freeware one included in 2007's Turbo C++ Explorer) to run natively in DOS. Not real mode but pmode, but at least it runs. Quote:
Yes, but comparing raw binary code output to a different assembler (e.g. FASM) requires ignoring the actual encoding bytes and only comparing the actual disassembled instructions. I whipped up a (very simple) script to do that with NASM / NDISASM and AWK. Quote:
Actually, Ideal was insanely popular in DOS circles. There's still tons of legacy code that no one converted to other assemblers. Unfortunately, Ideal isn't well-supported by any other assemblers, so it's annoying. Quote:
You should use LZASM (Ideal) for such code else just use MASM (or better, JWasm or HJWasm). My point was that MASM v6 is semantically similar, so it isn't impossible to convert from TASM to MASM. All the so-called advantages of Ideal mode (allegedly) went away with MASM v6, so there's no huge advantage to TASM anymore. Quote: The (lack of) usage of IDEAL keyword will tell you more about changes you need to make. Like I said, _Art of Assembly_ (online) explains MASM v6 syntax. Granted, it depends on what you're trying to do, and none of it is super easy. Quote:
MASM v6 by default (unlike v5) made all labels inside procs as local. This was for better scoping, but if your old code (e.g. PSR Invaders) doesn't like that, you can use "jwasm -Zm" to enable old v5 syntax or just put "option noscoped" at the top of the file. Or change the label's single colon at end into "::". Quote:
Right, it won't forbid any instructions (so be careful!). You may want to use revolution's old compatibility.inc macros for help with that, if you're unsure. Quote:
I'll probably forget (too much else on the brain), but it looks interesting. Quote:
There's still tons of old DOS code that was abandoned that should probably be converted to Free tools. But I guess few find it worthwhile, sadly. |
|||
![]() |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.