;--- simple masm source to html converter. Public Domain.
;--- it's a sample for a mixed-language application (uses CRT)

;--- Win32 binary:
;--- assemble: jwasm -coff masm2htm.asm
;--- link:     link /subsystem:console masm2htm.obj \msvc\lib\libc.lib

;--- Linux binary:
;--- assemble: jwasm -zcw -elf -Fo masm2htm.o masm2htm.asm
;--- link:     gcc -o masm2htm masm2htm.o

    .386
    .model FLAT, c
    option casemap:none

printf  proto c :ptr BYTE, :VARARG
fopen   proto c :ptr BYTE, :ptr BYTE
fclose  proto c :ptr
fseek   proto c :ptr, :DWORD, :DWORD
ftell   proto c :ptr
fread   proto c :ptr BYTE, :DWORD, :DWORD, :ptr
fwrite  proto c :ptr BYTE, :DWORD, :DWORD, :ptr
strcat  proto c :ptr BYTE, :ptr BYTE
strcpy  proto c :ptr BYTE, :ptr BYTE
strlen  proto c :ptr BYTE
_stricmp  proto c :ptr BYTE, :ptr BYTE
malloc  proto c :DWORD
free    proto c :ptr

if 1
externdef c errno:dword
else
;--- if errno is to be defined as a function call
__errno macro
__errno_location proto c  ;this is the gcc name
    call __errno_location
    mov eax,[eax]
    exitm <eax>
    endm
errno textequ <__errno()>
endif

lf  equ 10
cr  equ 13

SEEK_SET equ 0
SEEK_END equ 2
NULL     equ 0
EOL      equ <cr,lf>

CStr macro text
local xxx
    .const
xxx db text,0
    .code
    exitm <offset xxx>
    endm

@DD macro list:VARARG
    for directive, <list>
local xxx
    .const
xxx db @CatStr(!",directive,!"),0
    .data
    dd offset xxx
    endm
    endm

    .data

;--- array of directives which are to be displayed "bold"

dirtab label dword
    @DD <.186>,      <.286>,      <.286c>,      <.286p>
    @DD <.287>,      <.386>,      <.386c>,      <.386p>
    @DD <.387>,      <.486>,      <.486p>,      <.586>
    @DD <.586p>,     <.686>,      <.686p>,      <.8086>
    @DD <.8087>,     <.allocstack>, <.alpha>,   <.break>
    @DD <.code>,     <.const>,    <.continue>,  <.cref>
    @DD <.data>,     <.data?>,    <.dosseg>,    <.else>
    @DD <.elseif>,   <.endif>,    <.endprolog>, <.endw>
    @DD <.err>,      <.err1>,     <.err2>,      <.errb>
    @DD <.errdef>,   <.errdif>,   <.errdifi>,   <.erre>
    @DD <.erridn>,   <.erridni>,  <.errnb>,     <.errndef>
    @DD <.errnz>,    <.exit>,     <.fardata>,   <.fardata?>
    @DD <.if>,       <.k3d>,      <.lall>,      <.lfcond>
    @DD <.list>,     <.listall>,  <.listif>,    <.listmacro>
    @DD <.listmacroall>, <.mmx>,  <.model>,     <.no87>
    @DD <.nocref>,   <.nolist>,   <.nolistif>,  <.nolistmacro>
    @DD <.pushframe>,<.pushreg>,  <.radix>,     <.repeat>
    @DD <.safeseh>,  <.sall>,     <.savereg>,   <.savexmm128>
    @DD <.seq>,      <.setframe>, <.sfcond>,    <.stack>
    @DD <.startup>,  <.tfcond>,   <.until>,     <.untilcxz>
    @DD <.while>,    <.x64>,      <.x64p>,      <.xall>
    @DD <.xcref>,    <.xlist>,    <.xmm>
    @DD <alias>,     <align>,     <assume>,     <catstr>
    @DD <comm>,      <comment>,   <db>,         <dd>
    @DD <df>,        <dosseg>,    <dq>,         <dt>
    @DD <dw>,        <echo>,      <else>,       <elseif>
    @DD <elseif1>,   <elseif2>,   <elseifb>,    <elseifdef>
    @DD <elseifdif>, <elseifdifi>,<elseife>,    <elseifidn>
    @DD <elseifidni>,<elseifnb>,  <elseifndef>, <end>
    @DD <endif>,     <endm>,      <endp>,       <ends>
    @DD <equ>,       <even>,      <exitm>,      <extern>
    @DD <externdef>, <extrn>,     <for>,        <forc>
    @DD <goto>,      <group>,     <if>,         <if1>
    @DD <if2>,       <ifb>,       <ifdef>,      <ifdif>
    @DD <ifdifi>,    <ife>,       <ifidn>,      <ifidni>
    @DD <ifnb>,      <ifndef>,    <incbin>,     <include>
    @DD <includelib>,<instr>,     <invoke>,     <irp>
    @DD <irpc>,      <label>,     <local>,      <macro>
    @DD <name>,      <option>,    <org>,        <page>
    @DD <popcontext>,<proc>,      <proto>,      <public>
    @DD <purge>,     <pushcontext>,<record>,    <repeat>
    @DD <rept>,      <segment>,   <sizestr>,    <struc>
    @DD <struct>,    <substr>,    <subtitle>,   <subttl>
    @DD <textequ>,   <title>,     <typedef>,    <union>
    @DD <while>

enddirtab label dword

startstring label byte
    db "<!DOCTYPE HTML PUBLIC ",22h,"-//W3C//DTD HTML 3.2 FINAL//EN",22h,">",EOL
    db "<HTML>",EOL
    db "<HEAD>",EOL
    db "<TITLE>"
    db 0
startstring2 label byte
    db "</TITLE>",EOL
    db "</HEAD>",EOL
    db "<BODY>",EOL
    db "<TABLE BORDER=0 CELLSPACING=4 CELLPADDING=4 WIDTH=",22h,"100%",22h,">",EOL
    db "<TR BGCOLOR=#E0E0E0><TD>",EOL
    db "<pre>",EOL
    db 0
endstring label byte
    db "</pre>",EOL
    db "</TD></TR>",EOL
    db "</TABLE>",EOL
    db "</BODY>",EOL
    db "</HTML>",EOL
    db 0

fgcolorgray label byte
    db "<font color=#808080>"
    db 0

fgcolorrest label byte
    db "</font>"
    db 0

    .code

is_valid_id_first_char proc stdcall
    cmp al,'.'
    jz yes
    cmp al,'$'
    jz yes
    cmp al,'?'
    jz yes
    cmp al,'@'
    jz yes
    cmp al,'_'
    jz yes
    cmp al,'A'
    jb no
    cmp al,'Z'
    jbe yes
    cmp al,'a'
    jb no
    cmp al,'z'
    jbe yes
no:
    stc
    ret
yes:
    clc
    ret
is_valid_id_first_char endp

is_valid_id_char proc stdcall
    cmp al,'$'
    jz yes
    cmp al,'?'
    jz yes
    cmp al,'@'
    jz yes
    cmp al,'_'
    jz yes
    cmp al,'0'
    jb no
    cmp al,'9'
    jbe yes
    cmp al,'A'
    jb no
    cmp al,'Z'
    jbe yes
    cmp al,'a'
    jb no
    cmp al,'z'
    jbe yes
no:
    stc
    ret
yes:
    clc
    ret
is_valid_id_char endp

scandirectives proc stdcall uses esi edi ebx ecx pdir:ptr BYTE
    mov esi, offset dirtab
    mov edi, pdir
    .while (esi < offset enddirtab )
        lodsd
        push esi
        mov esi, eax
        invoke _stricmp, esi, edi
        and eax, eax
        jz found
        pop esi
    .endw
    clc
    ret
found:
    mov eax, esi
    pop esi
    stc
    ret
scandirectives endp

;--- convert masm text found in buffer
;--- 1. put comments in gray fgcolor
;--- 2. write directives in bold.
;--- 3. convert '<', '>' and '&' to '&lt;', '&gt;' and '&amp;'

convertbuffer proc uses ebx esi edi fname:ptr BYTE, buffer:ptr BYTE, size_:DWORD, psize:ptr DWORD

local outb:dword
local startdir:dword
local firstdir:byte
local lastchar:byte
local inquotes:byte
local inangles:byte
local incomment:byte
local cnt:word
local startline:dword

    mov eax, size_
    shl eax, 2          ;use size*4 for output buffer size
    invoke malloc, eax
    .if ( eax == 0 )
        invoke printf, CStr(<"out of memory",lf>)
        mov ecx, psize
        xor eax, eax
        mov [ecx], eax
        ret
    .endif
    mov ebx, eax
    mov edi, eax
    mov startline, edi
    mov startdir, 0
    mov firstdir, 0
    mov inquotes, 0
    mov inangles, 0
    mov incomment, 0
    mov esi, offset startstring
    .while ( byte ptr [esi] )
        movsb
    .endw
    mov esi, fname
    .while ( byte ptr [esi] )
        movsb
    .endw
    mov esi, offset startstring2
    .while ( byte ptr [esi] )
        movsb
    .endw
    mov esi, buffer
    mov ecx, size_
    .while (ecx)
        lodsb
        .if ( incomment == 0 && inquotes == 0 && inangles == 0 )
            .if ( startdir == 0 )
                .if ( firstdir == 0 )
                    call is_valid_id_first_char
                    jc @F
                    mov startdir, edi
@@:
                .endif
            .else
                call is_valid_id_char
                .if (CARRY?)
                    push eax
                    mov byte ptr [edi], 0
                    invoke scandirectives, startdir
                    .if (CARRY?)  ;found?
                        mov firstdir,1
                        mov edi, startdir
                        push esi
                        mov esi, eax
                        mov ax,"b<"
                        stosw
                        mov al,'>'
                        stosb
                        .while ( byte ptr [esi] )
                            movsb
                        .endw
                        mov eax,">b/<"
                        stosd
                        pop esi
                    .endif
                    pop eax
                    mov startdir, 0
                .endif
            .endif
        .endif
        .if ( al == '<' )
            mov eax, ";tl&"
            stosd
            mov al,'<'
        .elseif ( al == '>' )
            mov eax, ";tg&"
            stosd
            mov al,'>'
        .elseif ( al == '&' )
            mov eax, "pma&"
            stosd
            mov al, ";"
            stosb
            mov al,'&'
        .else
            ;.if ( incomment != 0 && ( al == cr || al == lf ))
            .if ( ( al == cr || al == lf ) && incomment != 0 )
                push esi
                mov esi, offset fgcolorrest
                .while ( byte ptr [esi] )
                    movsb
                .endw
                pop esi
            .endif
            stosb
        .endif
        .if ( al == lf || al == cr )
            mov startline, edi
            mov startdir, 0
            mov firstdir, 0
            mov inquotes, 0
            mov inangles, 0
            mov incomment, 0
        .elseif ( al == ';' && incomment == 0 )
            .if ( inquotes == 0 && inangles == 0 )
                mov incomment, 1
                push esi
                mov esi, offset fgcolorgray
                .while ( byte ptr [esi] )
                    movsb
                .endw
                pop esi
            .endif
        .elseif ( al == '<' && incomment == 0 && lastchar != '!' )
            inc inangles
        .elseif ( al == '>' && incomment == 0 && lastchar != '!' && inangles )
            dec inangles
        .elseif ( al == '"' && incomment == 0 && lastchar != '!' )
            .if ( inquotes == al )
                mov inquotes, 0
            .else
                mov inquotes, al
            .endif
        .elseif ( al == "'" && incomment == 0 && lastchar != '!' )
            .if ( inquotes == al )
                mov inquotes, 0
            .else
                mov inquotes, al
            .endif
        .endif
        mov lastchar, al
        dec ecx
    .endw
    mov esi, offset endstring
    .while ( byte ptr [esi] )
        movsb
    .endw
    mov eax, ebx
    sub edi, eax
    mov ecx, psize
    mov [ecx], edi
    ret
    align 4
convertbuffer endp

main proc c argc:dword, argv:ptr ptr

local filename:dword
local filesize:dword
local buffer:dword
local outbuf:dword
local outbsize:dword
local fname[260]:byte

    .if ( argc < 2 )
        invoke printf, CStr(<"masm2htm v1.0, Public Domain.",lf>)
        invoke printf, CStr(<"masm2htm is a masm to html converter.",lf>)
        invoke printf, CStr(<"usage: masm2htm input_file [output_file]",lf>)
        mov eax,1
        ret
    .endif
    mov ebx,argv
    mov ebx,[ebx+1*4]
    invoke fopen, ebx, CStr("rb")
    .if ( eax )
        mov filename, ebx
        mov ebx, eax
        invoke fseek, ebx, 0, SEEK_END
        invoke ftell, ebx
        mov filesize, eax
        invoke fseek, ebx, 0, SEEK_SET
        mov eax, filesize
        inc eax
        invoke malloc, eax
        .if ( eax == 0 ) 
            invoke printf, CStr(<"out of memory",lf>)
            invoke fclose, ebx
            mov eax,1
            ret
        .endif
        mov buffer, eax
        invoke fread, buffer, 1, filesize, ebx
        push eax
        invoke fclose, ebx
        pop eax
        .if ( eax != filesize )
            invoke printf, CStr(<"read error [%u]",lf>), errno
            mov eax,1
            ret
        .endif
        mov edx, buffer
        mov byte ptr [edx+eax],0
        invoke convertbuffer, filename, buffer, filesize, addr outbsize
        push eax
        invoke free, buffer
        pop eax
        .if ( eax )
            mov outbuf, eax
            mov edx, argv
            mov ebx, [edx+1*4]
            .if ( argc == 2 )
                invoke strlen, ebx
                add eax, ebx
                .while (eax != ebx && \
                        byte ptr [eax-1] != ':' && \
                        byte ptr [eax-1] != '\' && \
                        byte ptr [eax-1] != '/')
                    dec eax
                .endw
                lea ebx, fname
                invoke strcpy, ebx, eax
                invoke strcat, ebx, CStr(".txt")
            .else
                mov ebx, argv
                mov ebx, [ebx+2*4]
            .endif
            invoke fopen, ebx, CStr("wb")
            .if ( eax )
                mov ebx, eax
                invoke fwrite, outbuf, 1, outbsize, ebx
                .if ( eax != outbsize )
                    invoke printf, CStr(<"write error [%u]",lf>), errno
                .endif
                invoke fclose, ebx
                invoke printf, CStr(<"Done. %u bytes written",lf>), outbsize
            .else
                invoke printf, CStr(<"open('%s') failed [%u]",lf>), ebx, errno
            .endif
            invoke free, outbuf
        .endif
    .else
        invoke printf, CStr(<"open('%s') failed [%u]",lf>), ebx, errno
    .endif
    xor eax,eax
    ret
    align 4

main endp

    end