'===========================================================================
' Subject: QB to NASM                         Date: 12-11-02 (  :  )       
'  Author: Nick Dark                          Code: QB,PDS                 
'  Origin: nrdark92@yahoo.co.uk             Packet: ASMCODE.ABC
'===========================================================================
DECLARE SUB badend (fromparse!)
DECLARE SUB conv (outfile$)
DECLARE FUNCTION TRIM$ (tin$)
DECLARE SUB parser (infile$)
'The idea of this program is to convert QB source code into nasm source code.
'I have spent a long time searching the net for a program that does this, but
'to no avail. So i have decided to write this.
'I am trying to keep as close to the Qbasic / QB syntax as possible, while
'adding what i consider to be usefull additions.
'I eventually hope to create a linux version as well.
'It is in the very early stages of development (currently only supporting
'5 functions fully.) The input function Now works for strings, and the
'internal alloc function has been deleted. This program is completely free
'for you to use as you wish, (but please acknowlege me). I however doubt it
'would be of much use to anyone in this state.
'The only other thing I ask is that if you modify it please send me a copy of
'the modified source code.
'If you have any questions comments etc. please email me: nrdark92@yahoo.co.uk
'Please send your modifications to that address as well.
'If  you would like an updated version, please email me at the abouve address
'and i will send you the latest version.

'More information can be found in the help sub.

TYPE var
name AS STRING * 20
END TYPE

DIM SHARED cvnum, vmax, linectr, outfile$

cvnum = 0
vmax = 20       'Max number of variables
printctr& = 0
inputctr& = 0
linectr = 0
pcomm = 0

DIM SHARED vars(cvnum) AS var
CLS

' INPUT "Input file: ", infile$
' INPUT "Output file: ", outfile$
infile$ = "c:\test.bas"
outfile$ = "c:\test.asm"
parser infile$
conv outfile$

SUB badend (fromparse)
                                        PRINT "Error on line:"; linectr
                                        CLOSE #1
                                        CLOSE #2
                                        CLOSE #3
                                        IF fromparse = 0 THEN
                                                KILL "~data.dat"
                                                KILL outfile$
                                        ELSE
                                                KILL "~parser.dat"
                                        END IF
                                        END

END SUB

SUB conv (outfile$)
OPEN "~parser.dat " FOR INPUT AS #1
OPEN "~data.dat" FOR OUTPUT AS #2
OPEN outfile$ FOR OUTPUT AS #3
OPEN "~bss.dat" FOR OUTPUT AS #4

PRINT #3, "[bits 16]"
PRINT #3, "org 0x100"
PRINT #3, "[section .text]"

DO UNTIL EOF(1)
LINE INPUT #1, source$
linectr = linectr + 1
source$ = source$ + " "
linectr% = 0
DO UNTIL linectr% = LEN(source$)
linectr% = linectr% + 1

midsrc$ = LEFT$(source$, linectr%)
IF LCASE$(source$) = "/asm " THEN noconv = 0
IF noconv = 1 THEN PRINT #3, source$: EXIT DO
IF ASC(MID$(source$, linectr%, 1)) = 32 THEN

REM ****************************************
REM ************ Between Here **************
REM ****************************************

SELECT CASE LCASE$(RTRIM$(midsrc$))
                CASE "print"
                        PRINT #3, ";s"
                        PRINT #3, ";s " + source$
                        PRINT #3, ";s"

                                printctr& = printctr& + 1      'printctr gives the number on the end of the nasm pointer
                                tmp$ = RIGHT$(TRIM$(source$), LEN(source$) - linectr% - 1)
                                IF NOT tmp$ = CHR$(34) THEN
                                        IF RIGHT$(TRIM$(source$), 1) = ";" THEN
                                        REM IF TRIM$(RIGHT$((tmp$), 1)) = ";" THEN
                                                flag = 1
                                                IF LEN(tmp$) >= 2 THEN
                                                        tmp$ = LEFT$(tmp$, LEN(tmp$) - 2)
                                                ELSE
                                                        tmp$ = LEFT$(tmp$, LEN(tmp$) - 1)
                                                END IF
                                        ELSE
                                                flag = 0
                                                tmp$ = LEFT$(tmp$, LEN(tmp$) - 1)
                                        END IF
                                ELSE
                                        tmp$ = ""
                                END IF
                                PRINT #3, "mov ah,0x09"
                                printctr$ = STR$(printctr&)
                                PRINT #3, "mov dx,print" + RIGHT$(printctr$, LEN(printctr$) - 1)
                                PRINT #3, "int 0x21"
                        IF LEFT$(tmp$, 1) = CHR$(34) THEN
                                tmp$ = RIGHT$(tmp$, LEN(tmp$) - 1)
                        END IF

                        IF flag = 1 THEN
                                PRINT #2, "print" + TRIM$(printctr$) + "  db  " + "'" + tmp$ + "'" + ",'$'"
                        ELSE

                                PRINT #2, "print" + TRIM$(printctr$) + "  db  " + "'" + tmp$ + "'" + ",13,10,'$'"
                        END IF
                CASE "cls"
                        PRINT #3, ";s"
                        PRINT #3, ";s " + source$
                        PRINT #3, ";s"
                        PRINT #3, "push es"
                        PRINT #3, "mov ax,0b800h"
                        PRINT #3, "mov es, ax"
                        PRINT #3, "xor di,di"
                        PRINT #3, "mov ax,720h"
                        PRINT #3, "cld"
                        PRINT #3, "mov cx, 80 * 25 * 2"
                        PRINT #3, "shr cx, 1"
                        PRINT #3, "rep stosw"
                        PRINT #3, "mov ah, 2"
                        PRINT #3, "xor bx,bx"
                        PRINT #3, "xor dx,dx"
                        PRINT #3, "int 10h"
                        PRINT #3, "pop es"
                CASE "asm"
                                noconv = 1
                CASE "input"
                                PRINT #3, ";s"
                                PRINT #3, ";s " + source$
                                PRINT #3, ";s"
                                inputctr& = inputctr& + 1
                                var$ = TRIM$(RIGHT$(source$, LEN(source$) - 5))
                                PRINT #4, var$ + " " + "resb" + " 256"
                                PRINT #3, "mov si," + var$
                                PRINT #3, "inmain" + TRIM$(STR$(inputctr&)) + ":"
                                PRINT #3, "mov ah,0"
                                PRINT #3, "int 0x16"
                                PRINT #3, "cmp al,0"
                                PRINT #3, "je inmain" + TRIM$(STR$(inputctr&))
                                PRINT #3, "stosb"
                                PRINT #3, "mov ah,0x0e"
                                PRINT #3, "int 0x010"
                                PRINT #3, "cmp al,0x0d"
                                PRINT #3, "jne inmain" + TRIM$(STR$(inputctr&))

                CASE "pcomm"    ' if pcomm is in the source file then any
                                ' comments appear in the asm source as well.
                                ' pcomm can be toggled on and off by calling
                                ' it multiple times.
                        IF pcomm = 0 THEN pcomm = 1 ELSE pcomm = 0
                CASE "rem"
                        IF pcomm = 1 THEN
                                PRINT #3, ";"
                                PRINT #3, "; " + RIGHT$(source$, LEN(source$) - 3)
                                PRINT #3, ";"
                        END IF
                CASE "ap"
                        PRINT #3, ";i"
                        PRINT #3, ";i " + RIGHT$(source$, LEN(source$) - 2)
                        PRINT #3, ";i"
                        

                CASE "'"
                        IF pcomm = 1 THEN
                                PRINT #3, ";"
                                PRINT #3, "; " + RIGHT$(source$, LEN(source$) - 1)
                                PRINT #3, ";"
                        END IF

                CASE ELSE
                        SELECT CASE LCASE$(source$)
                                CASE "/asm "
                                CASE " "
                               CASE ELSE
                                        badend 0
                        END SELECT
END SELECT

REM ****************************************
REM ************ And Here ******************
REM ****************************************
REM ****** Is platform specific!! **********
REM ****************************************
EXIT DO

END IF

LOOP
LOOP
CLOSE #2
CLOSE #4

PRINT #3, ";s"
PRINT #3, ";s DOS Quit Prog Code"
PRINT #3, ";s"
PRINT #3, "mov ah,0x4c"
PRINT #3, "mov al,0x00"
PRINT #3, "int 0x21"

OPEN "~data.dat" FOR INPUT AS #2
PRINT #3, ""
PRINT #3, "[section .data]"
DO UNTIL EOF(2)
LINE INPUT #2, in$
PRINT #3, in$
LOOP

CLOSE #1
CLOSE #2

OPEN "~bss.dat" FOR INPUT AS #2
PRINT #3, ""
PRINT #3, "[section .bss]"
DO UNTIL EOF(2)
LINE INPUT #2, in$
PRINT #3, in$
LOOP
CLOSE #2
CLOSE #3

KILL "~data.dat"
KILL "~bss.dat"

END SUB

SUB help
'This sub is not designed to be run. If you wish to convert it into a
'callable / interactive resource of some kind feal free to do so.
'I will only elaborate on any differences from the standard QB commands,
'as standard statements are well documented in QB's online help.
'Note: Variables are usupported at the moment.

'The currently supported commands are:  (they are NOT case sensitive)
'PRINT useing print on its own, currently dosn't work
'      to get a blank line, you must use print ""
'CLS
'ASM  /ASM
'       Any code inbetween these two markers, will assumed to be nasm assembly
'       source code and passed straight through to the output file completely
'       unchanged and unchecked.
'INPUT
'       Now works for string variables. (string variables=255 bytes
'       currently)
'PCOMM  (pass comments)
'       Any comments between two PCOMM statements will appear in the assembly
'       source.
'REM
'       using a ' instead of REM will also work
'       Currently, all remarks must be on a separate line. :-(


'To assemble the output file eg. test.asm, use:
'       nasm -fbin test.asm -o test.com
' if all goes well, then nasm will create test.com which you can run.
'
END SUB

SUB parser (infile$)
OPEN infile$ FOR INPUT AS #1
OPEN "~parser.dat" FOR OUTPUT AS #2
DO UNTIL EOF(1)
LINE INPUT #1, source$
linectr% = 0
midsrc$ = ""
DO UNTIL linectr% = LEN(source$)
linectr% = linectr% + 1
midsrc$ = LEFT$(source$, linectr%)
lastspeech = 0
SELECT CASE LCASE$(midsrc$)
        CASE "input"
                prin$ = ""
                param$ = RIGHT$(TRIM$(source$), LEN(source$) - 5)
                FOR i = 1 TO LEN(param$)
                        IF MID$(param$, i, 1) = CHR$(34) THEN
                                lastspeech = i
                                IF prin = 0 THEN
                                        prin = 1
                                ELSE
                                        prin = 0
                                END IF
                        END IF
                        IF prin = 1 AND NOT MID$(param$, i, 1) = CHR$(34) THEN prin$ = prin$ + MID$(param$, i, 1)
                NEXT
                var$ = TRIM$(RIGHT$(param$, lastspeech - 1))
                IF LEN(prin$) > 0 THEN
                        PRINT #2, "print " + CHR$(34) + CHR$(34)
                        PRINT #2, "print " + CHR$(34) + prin$ + CHR$(34)
                        PRINT #2, "ap this and the command after this form the input command"
                        PRINT #2, "input " + var$
                ELSE
                        PRINT #2, "input " + var$
                END IF
        CASE "print"
                PRINT #2, source$
        CASE "cls"
                PRINT #2, source$
        CASE "rem"
                PRINT #2, source$
        CASE "'"
                PRINT #2, source$
        CASE "asm"
                PRINT #2, source$
        CASE "/asm"
                PRINT #2, source$
        CASE "pcomm"
                PRINT #2, source$
        CASE ELSE
                 'badend 1
END SELECT
LOOP
LOOP
CLOSE #1
CLOSE #2
END SUB

FUNCTION TRIM$ (tin$)
TRIM$ = LTRIM$(RTRIM$(tin$))
END FUNCTION

