MBimport

Description of the page content

Tool to import MasterBASIC source code (in standard text files) into the SimCoupe emulator.

Tags:

MBim stands for MasterBASIC improved. It's both a toolkit to write SAM Coupé's MasterBASIC programs with the Vim editor and an improved format for MasterBASIC source code, with C-style line and block comments, hash line meta-comments, empty lines, splitted lines, indentation with tabs or spaces, and no line numbes.

Unfortunately. no method of importing the source code into the SimCoupe emulator has worked fine yet (as of 2011-09). The problem is the SAM BASIC command KEYIN is buggy, and MasterBASIC didn't fixed it. The import process fails sooner or later. If you are lucky, little programs can be imported into the interpreter, that's all.

The last alternative I see is to write an utility (I'd choose Forth or SBASIC for the task) to convert MasterBASIC source code into an actual MasterBASIC file in a MGT disk image. The MGT disk format is well documented, but I'm afraid the internal format used by SAM BASIC and MasterBASIC is not, and some reverse enginnering will be needed.

The MBim format

Since the tool is useless, there's no point explaining the new format. The BBim format (page in Spanish) is very similar to what MBim was planned to be. (beside the labels, that already exist in MasterBASIC).

Source code

MBim2MB

This Vim program converts a source code written in MasterBASIC to a format that can be imported by one or more versions of MBimport.

The original Two new files from the MasterBASIC source code:

The source code can be written in the MBim format.

" mbim2mb.vim

" MBim2MB (version A-02-201406260020)

" This file is part of the MBim tools.

" Copyright (C) 2011,2012,2013,2014 Marcos Cruz (programandala.net)

" This Vim program provides some functions needed to
" convert MBim source code into an actual MasterBASIC
" program ready to be imported into the 
" SimCoupe emulator using the MBimport utility.

" ------------------------------
" History

" 2011-08-07: First version, called "vim2mb.vim".

" 2011-08-10: Added: "set noignorecase" and lowercase UDG chars.

" 2011-08-12: Fixed: "set fileencoding=latin1" was needed to make sure the
" source file is not UTF-8.

" 2011-09-01: Many changes: Renamed to "mbim2mb.vim".  Improved: Two files are
" created from the original "myprogram.mbim": "myprogram.mbim.mb" and
" "myprogram.mbim.mb.mgt". Formerly, the MGT was created right from the
" original code, ready to be imported by "MBimport 2". Now the intermediate MB
" file can be added into a real MGT disk image with the "SAM Diskimage
" manager" by Edwin Blink, and then imported by "MBimport 3".

" 2012-11-16: "silent!" added to the substitutions.

" 2012-11-17: Messages during the process. Usage message at the start. UDG and
" block chars are translated with nr2char() insted of hardcoded char codes.
" Support for embedded ASCII codes (in BASin format). "silent" added to the
" file operations. Fixed: "nul" to "/dev/null"

" 2012-11-18: The fake MGT image is compressed with zip or gzip.
"
" 2012-11-19: The lines can be numbered. This way the new versions of MBimport
" can work faster. The MB file can be deleted; is not needed in the last
" versions of MBimport. Line comments now need a trailing space (this avoids
" the '//' in 'http://' to be considered the start of a comment, and in other
" cases as well).
"
" 2012-11-20: Implemented: '#firstline'.
"
" 2012-12-05: Improved: now '#firstline 0' means no line number (MBimport 5
" imports source with line numbers, what is faster; the previous versions add
" the line numbers during the import proccess). The minimum line number is
" 100, to protect MBimport 5.

" 2012-12-06: Improved: in MBimChars(), the status of 'ignorecase' is
" preserved.  Fixed: 'normal w"lyw' had to be 'normal ww"lyw'. New: '#vim' and
" '#include' (copied from BBim2BB). New: '#keep_mb_file' and '#compression'.

" 2012-12-08: The minimum line number now is 10, because MBimport 5 has only
" seven lines.

" 2012-12-25: '#firstline' changed to '#first_line_number'.

" 2013-01-20: Block comments regexp improved.

" 2014-06-19: Revision. New to-do.

" 2014-06-26: Version A-02. No ISO chars translated anymore. The
" 'delete_MB_file' variable is renamed to 'remove_MB_file', and its default
" value is changed: now the file is keep unless '#remove_mb_file' is used;
" '#keep_mb_file' doesn't work any more. The default first line is 1, not 0
" (what caused the lines were not numbered by default).
" Fix: tow 'silent wq' are changed to 'silent w' and 'silent bw'; previously
" the buffers of the .mb and .mb.mgt files were not actually closed, and
" the next run failed.

" ------------------------------
" XXX TODO

" 2014-07-03: Fix:
" The following deffn with splitted lines removes some lines above it:
"   deffn f$(e)\
"   // comment
"   ="x"
" But this way it is properly rendered:
"   deffn f$(e)=\
"   // comment
"   "x"

" Fix?: This line removes al the following splitted lines:
"    // print i:\ // comment

" Change: #fake_mgt to force the creation of the mgt file. The default
" behaviour will be not to create it, because it will be unnecessary with the
" next version of SimCoupe.

" Improve: Option to translate labels to line numbers? This may prevent
" problems with KEYIN during the import process, and also would make the code
" faster.

" Fix: remove empty lines at the top of the source (after removing the
" comments); test this with the program 'mkusage5'.

" New: #remove_labels, #udg_off, #blocks_1_off, #blocks_2_off

" ----------------------------------------------
" Source cleaning

function! MBimClean()

  " Clean off all MBim stuff.

  " Remove comments:
  silent! %s@^\s*#.*$@@ " metacomments
  silent! %s@^\s*\/\/\s\+.*$@@ " line comments at the start 
  silent! %s@\s\+\/\/\s\+.*$@@ " line comments at the end 
  silent! %s@^\s*\/\*\_.\{-}\*\/@@ " block comments

  " Remove the empty lines:
  silent! %s/^\s\+//
  silent! %s/\n\n\+/\r/g

  silent! %s.^\s*..g " Remove indentation
  silent! %s/\s\+$//g " Remove ending blanks

  silent! %s.\\\n.. " Join the splitted lines 

  echo 'Source code cleaned.'

endfunction

" ----------------------------------------------
" Metacommands 

function! MBimGetConfig()

  " Search the source for '#keep_mb_file', '#compression zip' and
  " '#compression gzip'.  They can be anywhere in the source but always at the
  " start of a line (with optional indentation).  The values of
  " 's:remove_MB_file' and 's:zip_compression' are set accordingly.

  " Delete the MB file?
  " The MB file is needed only by old versions of MBimport.
  let s:remove_MB_file=0 " Default
  call cursor(1,1) " Go to the top of the file.
  let s:remove_MB_file=search('^\s*#remove_mb_file\>','Wc')
  if s:remove_MB_file
    echo 'The MB file will be removed.'
  else
    echo 'The MB file will be kept.'
  endif

  " Compress the fake MGT file with zip instead of gzip?
  let s:zip_compression=0 " Default
  call cursor(1,1) " Go to the top of the file.
  if search('^\s*#compression\s\+g\?zip\>','Wc')
    " Store the compression format into register 'l':
    normal ww"lyw
    " And then into the config variable:
    let s:zip_compression=(getreg('l',1)=='zip')
  endif
  if s:zip_compression
    echo 'The MGT file will be zipped.'
  else
    echo 'The MGT file will be gzipped.'
  endif

endfunction

function! MBimVim()

  " Execute all #vim metacommands.
  " Syntax:
  " #vim Any-Vim-Ex-Command

  call cursor(1,1) " Go to the top of the file.
  let l:vimCommands=0 " Counter
  while search('^\s*#vim\s','Wc')
    let l:vimCommands += 1
    let l:vimCommandLine = line('.')
    let l:vimCommand=matchstr(getline(l:vimCommandLine),'\S\+.*',4)
    " echo l:vimCommand
    execute 'silent! '.l:vimCommand
    call cursor(l:vimCommandLine,1) " Return to the command line.
    call setline('.','') " Blank the line.
  endwhile

  if l:vimCommands==0
    echo 'No Vim command found.'
  elseif l:vimCommands==1
    echo 'One Vim command executed.'
  else
    echo l:vimCommands 'Vim commands executed.'
  endif

endfunction

function! MBimInclude()

  " Execute all #include commands in the source.
  " Syntax:
  " #include file-name
  " Warning: nested including is possible, but no recursion check is made!

  call cursor(1,1) " Go to the top of the file.
  let l:includedFiles=0 " Counter
  while search('^\s*#include\s','Wc')
    let l:includedFiles += 1
    let l:fileName=matchstr(getline('.'),'\S\+.*',8)
    call setline('.','') " Blank the line.
    " ----------- xxx debug check
    "echo '#include ' l:fileName
    "echo 'getcwd()=' getcwd()
    "echo 'Modifications:'
    "echo ':~' fnamemodify(l:fileName,':~')
    "echo ':p' fnamemodify(l:fileName,':p')
    " -----------
    execute "silent! r ".l:fileName
  endwhile

  if l:includedFiles==0
    echo 'No file included.'
  elseif l:includedFiles==1
    echo 'One file included.'
  else
    echo l:includedFiles 'files included.'
  endif

endfunction

" ----------------------------------------------
" Renum

function! MBimGetFirstLine()

  " Store into s:firstLine the first line number
  " to be used by the final MasterBASIC program.
  " The command #first_line_number can be used to set
  " the desired line number. Only the first occurence
  " of #first_line_number will be used; it can be anywhere
  " in the source but always at the start of a line
  " (with optional indentation).

  " Default value of the first line number
  " (if it's 0, the lines will not be renumbered):
  let s:firstLine=1 " Default

  call cursor(1,1) " Go to the top of the file.
  if search('^\s*#first_line_number\s\+[0-9]\+\>','Wc')
    " Store the number into register 'l':
    normal ww"lyw
    " And then into the variable:
    let s:firstLine=getreg('l',1)
  endif
  if s:firstLine
    " Make sure the first line does not clash with MBimport 5:
    let s:firstLine=max([s:firstLine,10])
    echo 'First line number: '.s:firstLine
  else
    echo 'No line numbers'
  endif

endfunction

function! MBimRenum()

  " Call the the nl program (part of the Debian coreutils package):
  execute "silent! %!nl --body-numbering=t --number-format=rn --number-width=6 --number-separator=' ' --starting-line-number=".s:firstLine." --line-increment=1"

  " In older versions of coreutils,
  " -v sets the first line number, and -i sets the line increment.
  " (the long option for -v doesn't work, though the manual mentions it).
  " Modern versions of nl uses the clearer options
  " --first-line and --line-increment, see:
  " http://www.gnu.org/software/coreutils/manual/coreutils.html#nl-invocation

  " Remove spaces before line numbers
  " (nl has no option to remove them):
  silent! %substitute/^\s*//e

  " Remove line numbers from import-time commands
  silent! %substitute/^[0-9]\{1,5}\s://e

  echo 'Line numbers added.'

endfunction

" ----------------------------------------------
" Character translation

" Translations are provided for block graphs and ZX Spectrum UDGs, using the
" BASin notation.
"
" Other ad hoc translations must be done in the program source, using the #vim
" command. Example:
"
"   // Convert every A acute char in the source to SAM Coupé's char 133:
"   #vim %s/Á/\=nr2char(133)/g

function! MBimBlockGraphs()

  " Translate BASin format ZX Spectrum block graphics notation
  " (SAM Coupé chars 128-143)

  silent! %s/\\  /\=nr2char(128)/ge
  silent! %s/\\ '/\=nr2char(129)/ge
  silent! %s/\\' /\=nr2char(130)/ge
  silent! %s/\\''/\=nr2char(131)/ge
  silent! %s/\\ \./\=nr2char(132)/ge
  silent! %s/\\ :/\=nr2char(133)/ge
  silent! %s/\\'\./\=nr2char(134)/ge
  silent! %s/\\':/\=nr2char(135)/ge
  silent! %s/\\\. /\=nr2char(136)/ge
  silent! %s/\\\.'/\=nr2char(137)/ge
  silent! %s/\\: /\=nr2char(138)/ge
  silent! %s/\\:'/\=nr2char(139)/ge
  silent! %s/\\\.\./\=nr2char(140)/ge
  silent! %s/\\\.:/\=nr2char(141)/ge
  silent! %s/\\:\./\=nr2char(142)/ge
  silent! %s/\\::/\=nr2char(143)/ge

endfunction

function! MBimUDG()

  " Translate BASin format ZX Spectrum UDG notation
  " (SAM Coupé chars 144-164)

  silent! %s/\\[Aa]/\=nr2char(144)/ge
  silent! %s/\\[Bb]/\=nr2char(145)/ge
  silent! %s/\\[Cc]/\=nr2char(146)/ge
  silent! %s/\\[Dd]/\=nr2char(147)/ge
  silent! %s/\\[Ee]/\=nr2char(148)/ge
  silent! %s/\\[Ff]/\=nr2char(149)/ge
  silent! %s/\\[Gg]/\=nr2char(150)/ge
  silent! %s/\\[Hh]/\=nr2char(151)/ge
  silent! %s/\\[Ii]/\=nr2char(152)/ge
  silent! %s/\\[Jj]/\=nr2char(153)/ge
  silent! %s/\\[Kk]/\=nr2char(154)/ge
  silent! %s/\\[Ll]/\=nr2char(155)/ge
  silent! %s/\\[Mm]/\=nr2char(156)/ge
  silent! %s/\\[Nn]/\=nr2char(157)/ge
  silent! %s/\\[Oo]/\=nr2char(158)/ge
  silent! %s/\\[Pp]/\=nr2char(159)/ge
  silent! %s/\\[Qq]/\=nr2char(160)/ge
  silent! %s/\\[Rr]/\=nr2char(161)/ge
  silent! %s/\\[Ss]/\=nr2char(162)/ge
  silent! %s/\\[Tt]/\=nr2char(163)/ge
  silent! %s/\\[Uu]/\=nr2char(164)/ge

endfunction

function! MBimChars()

  set fileencoding=latin1
  set fileformat=mac " Force CR (char 13) as end of line
  let l:ignoreCaseBackup=&ignorecase
  set noignorecase

  call MBimUDG()
  call MBimBlockGraphs()

  " Embedded ASCII codes (BASin format):
  silent! %s/\\#\(\d\+\)/\=nr2char(submatch(1))/g

  echo 'Special chars translated.'

  if l:ignoreCaseBackup
    set ignorecase
  else
    set noignorecase
  endif

endfunction

" ----------------------------------------------
" Fake MGT disk image

function! MBimMGTfile()

  " Create a fake MGT disk image from the
  " current MB file.

  silent write " Write the current MB file
  split " Split the window
  silent write! %.mgt " Save a copy with the MGT extension added
  echo 'Fake MGT file created.'

  silent edit ++bin %.mgt " Open it in binary mode
  set noendofline " Don't put an EOL at the end of the file whew saving it
  set fileencoding=latin1

  " Convert it to a fake MGT disk image,
  " just making it 819200 bytes long:
  silent %!dd bs=819200 conv=sync 2>> /dev/null

  " Somehow, 'dd' changes all
  " carriage returns (decimal 13) to
  " line feeds (decimal 10), what makes useless
  " the command 'set fileformat=mac' used to
  " force the carriage return needed by the SAM Coupé
  " as end of line. That's why "tr" is needed now:

  " Translate line feed chars (decimal 10)
  " into carriage returns chars (decimal 13):
  silent! %!tr "\12" "\15"

  let mgtfile=expand("%")
  silent w " Write the MGT file
  silent bw " Close the MGT file
  echo 'Fake MGT file saved and closed.'

  " Make a compressed copy of the fake disk image
  " (no check is made; the compression program must be installed):
  if s:zip_compression
    " silent execute '!zip -9' shellescape(mgtfile.'.zip') shellescape(mgtfile)
    silent execute '!zip -9' shellescape(mgtfile.'.zip') shellescape(mgtfile)
    silent call delete(mgtfile)
    echo 'Fake MGT file compressed with zip.' mgtfile
  else
    silent call delete(mgtfile.'.gz')
    silent execute '!gzip' shellescape(mgtfile)
    echo 'Fake MGT file compressed with gzip.'
  endif

endfunction

" ----------------------------------------------
" MB file

function! MBimMBfile()

  " Create a copy of the current MBim file
  " with the ".mb" extension added
  " and open it for editing.

  silent write " Write the current MBim file
  split " Split the window
  silent write! %.mb " Save a copy with the MB extension added
  silent edit %.mb " Open it for editing
  set fileencoding=latin1

  echo 'MB file created.'

endfunction

" ----------------------------------------------
" Main

function! MBim2MB()

  call MBimGetFirstLine()
  call MBimGetConfig()
  call MBimMBfile()
  call MBimInclude()
  call MBimVim()
  call MBimClean()
  if s:firstLine
    call MBimRenum()
  endif
  call MBimChars()
  call MBimMGTfile()
  let mbfile=expand("%")
  silent w " Save the MB file
  silent bw " Close the MB file
  echo 'MB file saved and closed.'
  if s:remove_MB_file
    silent echo delete(mbfile)
    echo 'MB file removed.'
  endif

  echo 'Done!'

endfunction

" Shortkey ',mb' in normal mode:
nmap <silent> ,mb :call MBim2MB()<CR>

echo "MBim2MB loaded."
echo "Activate it with the keys ',mb' (comma, M and B), in normal mode, on your MBim source."


MBim Vim syntax file

The following MBim Vim syntax file is an extension of the MasterBASIC syntax Vim file.

" mbim.vim
" Vim syntax file
" Language:    MBim (MasterBASIC improved) for the SAM Coupé computer
" Author:      Marcos Cruz (programandala.net)
" License:     Vim license (GPL compatible)
" URL:         http://programandala.net/es.programa.mbim_vim_syntax_file.html
" Updated:     2015-02-26

" This syntax file is not a complete implementation yet.

" ----------------------------------------------

" History:

" 2011-09-01: First version, based on SBim by the same author.
" 2012-09-03: Moved the editing preferences to a filetype plugin.
" 2012-11-19: Modified: Line comments (with //) need a trailing space; if they
" are not at the start of a line, also a leading space is needed.
" 2012-12-06: New: xxx, todo, fixme.
" 2014-06-19: New: PreProc commands:
"   '#first_line_number', '#compression', '#vim', '#include', '#keep_mb_file'.
" 2014-08-02: New: 'mbimLineEmptyComment'.
" 2015-02-26: Vim license.

" ----------------------------------------------

" MBim is an improved format for SAM Coupé's MasterBASIC source code.

" Features:

" Tabs can be used for indentation. All indentation is removed.
" Line comments with # (it must be the first non-empty char in the line).
" Line comments with // (anywhere in the line, even at the end of splitted lines; any spaces or tabs before the mark are removed too).
" Block comments with /* */ (the opening mark must be the first non-empty char in the line).
" Empty lines.
" Lines splitted with \.

" ----------------------------------------------

" This syntax file simply loads the standard
" MasterBASIC syntax file, adds the highlights for the new
" comments and sets the tabs and text folding preferences.

" The Vim2BAS tool provides programs to conver 
" the MBim source code into an actual MasterBASIC
" program for the SAM Coupé.

" ----------------------------------------------

:runtime! syntax/masterbasic.vim " Include the standard MasterBASIC syntax file

" XXX TODO just add '#' to iskeyword:
if version > 600
  setlocal iskeyword=48-57,65-90,97-122,_,$,%,#
else
  set iskeyword=48-57,65-90,97-122,_,$,%,#
endif

syn keyword mbimPreProc #first_line_number
syn keyword mbimPreProc #compression
syn keyword mbimPreProc #vim
syn keyword mbimPreProc #include
syn keyword mbimPreProc #keep_mb_file

" Line numbers are not used:
syntax clear masterbasicLineNumber

syn keyword mbimTodo contained todo fixme xxx

syn region mbimMetaComment start="^\s*#[# ]*" end="$" contains=mbimTodo
syn region mbimCommentedOut start="^\s*#[^# ]" end="$" contains=mbimTodo
syn region mbimLineStartComment start="^\s*//\s" end="$" contains=mbimTodo
syn region mbimLineEndComment start="\s\+//\s" end="$" contains=mbimTodo
syn match mbimLineEmptyComment "^\s*//\s*$"
syn region mbimBlockComment start="^\s*/\*" end="\*/" contains=mbimTodo

" XXX OLD ?
syn match mbimSplittedLine "|\s*\(//.*\)*$" contains=mbimLineComment

hi def link mbimMetaComment Comment
"hi def link mbimCommentedOut Ignore
hi def link mbimCommentedOut Comment
hi def link mbimLineStartComment Comment
hi def link mbimLineEndComment Comment
hi def link mbimBlockComment Comment
hi def link mbimLineEmptyComment Comment
hi def link mbimSplittedLine Ignore
hi def link mbimPreProc PreProc
hi def link mbimTodo Todo

let b:current_syntax = "mbim"


MBimport 1

The first version uses the option provided by SimCoupe to import host system files into the SAM Coupé's memory.

After many tries, I decided to low the RAMTOP to 32768 and use the whole memory page. That means the maximum length of the source code is 16 KiB.

First, the procedure v2f ("Vim to file") reads the memory region, extracts the source code lines, edit and join them if needed an prints them out into a serial file in the current disk. Second, the procedure f2s ("file to SAM") reads every line of the serial file and adds it into the current program using KEYIN.

It's possible to import programs longer than 16 KiB, dividing them into parts. The parts will be joined in the second step of the process.

   10 REM MBimport 1 (Version A-20110907)
      (C) 2011 Marcos Cruz (programandala.net)
      License programandala.net/license
   15 MODE 1
   20 OPEN BLOCKS 1
   25 DEF FN nn$(n)=CHR$ (n MOD 256)+CHR$ INT (n/256)
   30 DEF FN min(a,b)=a*(a<b)+b*(b<a)
   35 DEF FN max(a,b)=a*(a>b)+b*(b>a)
   40 DEF PROC addExtensionTo REF file$
   45   LOCAL drive$
        LET drive$=""
   50   IF LEN file$>4
   55     IF EQU(file$(1),"d") AND file$(3)=":"
   60       LET drive$=file$(2),drive$=file$( TO 3) AND (drive$>="1" AND drive$<="9")
   65     END IF
   70   END IF
   75   IF LEN drive$ THEN LET file$=file$(4 TO )
   80   LET file$=drive$+TRUNC$ ((file$+"      ")(1 TO 6))+".mb"
   85 END PROC
   90 DEF PROC about
        CLS #
        PRINT PEN 5;"MBimport 1"'"Copyright (C) 2011 Marcos Cruz"'"(programandala.net)"''
      END PROC
   95 DEF PROC v2fs DATA
        REM "From Vim to files"
  100   LOCAL file$,file
  105   DO WHILE ITEM
          IF ITEM=2
            READ file
            LET file$=STR$ file
          ELSE READ file$
          END IF
          v2f file$
        LOOP
  110 END PROC
  115 DEF PROC v2f file$
        REM "From Vim to file"
  120   DEFAULT file$="tmp"
  125   LOCAL line$,start,end,eol$,eol,stream,clear$
        LET start=RAMTOP,eol$=CHR$ 10,eol=LEN eol$,stream=4
        addExtensionTo file$
  130   LET clear$=CHR$ &21+FN nn$(32768)+CHR$ &11+FN nn$(32769)+CHR$ &01+FN nn$(16383)+CHR$ &36+CHR$ 0+CHR$ &ED+CHR$ &B0+CHR$ &C9
        REM machine code to clear 16 KiB at 32768
  135   about
  140   IF start<>32768 THEN PRINT "Error: RAMTOP<>32768"'"Press any key to do "; BRIGHT 1;"CLEAR 32768"; BRIGHT 0'"and then try again."
          PAUSE
          CLEAR 32768
          STOP
        ELSE CALL LENGTH(0,clear$)
  145   PRINT "The file "; BRIGHT 1;file$; BRIGHT 0;" will be"'"created with the content of the"'"memory (address range from"'"32768 to 49151)."''"Now please import the host"'"system file into the BASIC"'"address 32768, using the"'"SimCoupe's data import 
option"'"(PC key F4)."''"After importing the file, press any key to continue."
  150   PAUSE
  155   CLOSE #stream
        IF FSTAT(file$,1)
          PRINT "erased"
          ERASE file$
        END IF
        OPEN #stream;file$ OUT
  160   PRINT "Copying the lines from memory"'"into the temporary file..."
  165   DO
  170     LET end=LOCN(start, 49151,eol$,ABS )
  175   EXIT IF NOT end
  180     PRINT #stream;MEM$(start TO end-1)
          LET start=end+eol
  185   LOOP
  190   CLOSE #stream
  195   PRINT "Done!"
  200 END PROC
  205 DEF PROC fs2s DATA
        REM "From files to SAM"
  210   LOCAL file$,file
  215   DO WHILE ITEM
          IF ITEM=2
            READ file
            LET file$=STR$ file
          ELSE READ file$
          END IF
          f2s file$
        LOOP
  220 END PROC
  225 DEF PROC f2s file$,lineN
        REM "From file to SAM"
  230   DEFAULT file$="tmp",lineN=lastLineByMBimport+1
  235   LOCAL srcLine$,srcLineL,line$,stream,tabChars$,tail$
        LET line$="",stream=4,tabChars$=CHR$ 9+CHR$ 32
        addExtensionTo file$
  240   about
  245   IF RAMTOP<81919 THEN PRINT "Error: RAMTOP<81919"'"Press any key to do "; BRIGHT 1;"CLEAR 81919"; BRIGHT 0'"and then try again."
          PAUSE
          CLEAR 81919
          STOP
  250   SCROLL CLEAR
  255   CLOSE #stream
        OPEN #stream,file$ RND
  260   DO WHILE NOT EOF stream
  265     REM LET srcLine$=INP$(#stream,0)
          rem fails with lines longer than 255
  270     INPUT #stream; LINE srcLine$
  275     REM PRINT "raw srcLine$="; PEN 5;srcLine$
          rem debug!!!
  280     FOR first=1 TO LEN srcLine$
            IF NOT INSTR(tabChars$,srcLine$(first)) THEN EXIT FOR
  285     NEXT first
          IF first>1 THEN LET srcLine$=srcLine$(first TO )
  290     REM PRINT "srcLine$="; PEN 2;srcLine$
          PRINT "line$="; PEN 6;line$
  295     LET srcLineL=LEN srcLine$
  300     IF srcLineL
            PRINT PEN 6;srcLine$
            IF srcLine$(srcLineL)=CHR$ 92
              LET tail$=srcLine$( TO srcLineL-1)
              JOIN TO line$,tail$
            ELSE
              keyinLine line$+srcLine$
              LET line$=""
            END IF
          END IF
  305   LOOP
        IF LEN line$ THEN keyinLine line$
  310   keyinLine "label lastLineByMBimport"
  315   SCROLL RESTORE
  320   CLOSE #stream
  325 END PROC
  330 DEF PROC keyinLine l$
  335   LOCAL color
        LET color=4
  340   ON ERROR GO TO syntaxError
        KEYIN STR$ lineN+l$
        ON ERROR STOP
        GO TO printLine
  345   LABEL syntaxError
  350   ON ERROR STOP
        KEYIN STR$ lineN+"REM MIS"+"TAKE "+l$
        LET color=2
  355   LABEL printLine
        PRINT lineN; PEN color;l$
  360   LET lineN=lineN+1
  365 END PROC
  370 DEF PROC si
        REM Show input (for debugging)
  375   LOCAL pointer,first,last
        LET pointer=PTR stream,first=FN max(pointer-16,0),last=FN min(pointer+16,LENGTH#stream)
  380   FOR pos=first TO last
          POINT#stream,pos
          LET char$=INP$(#stream,1),color=7-5*(pos=pointer)
          PRINT PEN color;SHIFT$(char$,4);"(";CODE char$;")";
        NEXT pos
  385   POINT#stream,pointer
  390 END PROC
  395 DEF PROC sr
        SCROLL RESTORE
      END PROC
  400 DEF PROC c
        REM Clear after an error
  405   sr
        CLOSE *
        CLEAR
        STOP
      END PROC
  410 DEF PROC cat drive
        DEFAULT drive=DSTAT(*,8)
        DIR "d"+STR$ (drive)+":*.mb"
      END PROC
  415 DEF PROC d
        REM Delete the imported code
  420   DELETE 1e3 TO
      END PROC
  425 DEF PROC dMBimport
        REM Delete MBimport
  430   DELETE TO VAL "999"
      END PROC
  435 DEF PROC r
        REM Renum MBimport
  440   RENUM TO endOFMBimport LINE 10 STEP 5
      END PROC
  445 DEF PROC s
        REM Save MBimport 1
  450   d
        ERASE "MBimport1~"
        RENAME "MBimport1","MBimport1~"
        SAVE "MBimport1"
      END PROC
  455 LABEL endOfMBimport
  999 LABEL lastLineByMBimport
      REM This line must be 999
      -------------------------

MBimport 2

The second version is faster and easier to use. It allows to import programs bigger than 16 KiB in a single step. The source code is extracted from the sectors of a fake MGT disk image. That means the maximum length of the source code is 800 KiB, the raw capacity of an MGT disk.

The procedure d2s ("disk to SAM") does all the work.

To increase the speed, this version doesn't join the splitted lines or remove indentation. Those tasks must be done by the host system.

   10 REM MBimport 2 (version A-20110907)
      (C) 2011 Marcos Cruz (programandala.net)
      License programandala.net/license
   15 MODE 1
   20 DEF PROC about
        CLS #
        PRINT PEN 5;"MBimport 2"'"Copyright (C) 2011 Marcos Cruz"'"(programandala.net)"''
      END PROC
   25 DEF PROC import drive,lineN
        REM "From disk image to SAM"
   30   DEFAULT drive=2,lineN=endOfMBimport+1
   35   LOCAL srcLine$,buffer,bufferEnd,cr$,endOfSource
   40   LET cr$=CHR$ 13,endOfSource=0
   45   LOCAL sectorLen,track,sector,sliceStart,CRPos,lastTrack
   50   LET sectorLen=512,track=0,sector=1,CRPos=0,lastTrack=79 BOR 128
   55   IF RAMTOP<81919 THEN PRINT "Error: RAMTOP<81919"'"Press any key to do "; BRIGHT 1;"CLEAR 81919"; BRIGHT 0'"and then try again."
          PAUSE
          CLEAR 81919
          STOP
   60   LET buffer=RAMTOP,bufferEnd=buffer+sectorLen-1
   65   SCROLL CLEAR
   70   loadSector
   75   DO
   80     diskTo srcLine$
   85   EXIT IF endOfSource
   90     PRINT AT 0,0;lineN
   95     KEYIN STR$ lineN+srcLine$
  100     LET lineN=lineN+1
  105   LOOP
  110   keyinLine "label lastLineByMBimport"
  115   SCROLL RESTORE
  120 END PROC
  125 DEF PROC diskTo REF diskLine$
  130   LET diskLine$=""
  135   IF NOT PEEK (sliceStart) THEN debug "Zero found"
          LET endOfSource=1
          EXIT PROC
  140   DO
  145     LET crPos=LOCN(sliceStart,bufferEnd,cr$,ABS )
  150     debug "crPos="+STR$ crPos+(" (+"+STR$ (crPos-buffer)+")") AND crPos
  155     IF crPos
  160       LET diskLine$=diskLine$+MEM$(sliceStart TO crPos-1)
  165       IF crPos=bufferEnd THEN feedBuffer
            ELSE LET sliceStart=crPos+1
  170     ELSE
  175       LET diskLine$=diskLine$+MEM$(sliceStart TO bufferEnd)
            feedBuffer
  180     END IF
  185   LOOP UNTIL crPos
  190 END PROC
  195 DEF PROC feedBuffer
  200   debug "feedBuffer (current sector is "+STR$ sector+"/"+STR$ track+")"
  205   nextSector
        loadSector
  210 END PROC
  215 DEF PROC loadSector d,t,s
  220   DEFAULT d=drive,t=track,s=sector
  225   debug "loadSector ("+STR$ sector+"/"+STR$ track+")"
  230   READ AT d,t,s,buffer,1
  235   LET sliceStart=buffer
  240 END PROC
  245 DEF PROC nextSector
  250   debug "nextSector (current is "+STR$ sector+")"
  255   IF sector<10 THEN LET sector=sector+1
        ELSE nextTrack
  260 END PROC
  265 DEF PROC nextTrack
  270   debug "nextTrack (current is "+STR$ track+")"
  275   IF track=lastTrack THEN wipeBuffer
          LET endOfSource=1
          EXIT PROC
  280   IF FN side0(track)
  285     DoSide1 track
  290   ELSE doSide0 track
          LET track=track+1
  295   END IF
  300   LET sector=1
  305 END PROC
  310 DEF FN side0(t)=t<80
  315 DEF PROC doSide1 REF track
  320   debug "doSide1"
  325   LET track=track+128
  330 END PROC
  335 DEF PROC doSide0 REF track
  340   debug "doSide0"
  345   LET track=track-128
  350 END PROC
  355 DEF PROC debug message$,color
  360   DEFAULT color=2
  365   REM PRINT PEN color;message$
        REM PAUSE 
  370 END PROC
  375 DEF PROC keyinLine l$
  380   LOCAL color
        LET color=4
  385   ON ERROR GO TO syntaxError
        KEYIN STR$ lineN+l$
        ON ERROR STOP
        GO TO printLine
  390   LABEL syntaxError
  395   ON ERROR STOP
        KEYIN STR$ lineN+"REM MIS"+"TAKE "+l$
        LET color=2
  400   LABEL printLine
        PRINT lineN; PEN color;l$
  405   LET lineN=lineN+1
  410 END PROC
  415 DEF PROC sr
        SCROLL RESTORE
      END PROC
  420 DEF PROC c
        REM Clear after an error
  425   sr
        CLOSE *
        CLEAR
        STOP
      END PROC
  430 DEF PROC d
        REM Delete the imported code
  435   DELETE endOfMBimport+1 TO
      END PROC
  440 DEF PROC dMBimport
        REM Delete MBimport
  445   DELETE TO endOfMBimport
      END PROC
  450 DEF PROC r
        REM Renum MBimport
  455   RENUM TO endOFMBimport-1 LINE 10 STEP 5
      END PROC
  460 DEF PROC s
        REM Save MBimport
  465   d
        ERASE "MBimport2~"
        RENAME "MBimport2","MBimport2~"
        SAVE "MBimport2"
      END PROC
  999 LABEL endOfMBimport
      REM The first line of the imported code will be one more than this one.
      --------------------------



MBimport 3

The third version is the simplest method: It reads the source code from a serial file, line after line, from a real MGT disk image.

The source code has to be copied into the MGT disk image using the program SAM Diskimage Manager, (written by Edwin Blink in 2000). It's a program for Windows 95/95, but it works fine on GNU/Linux with Wine.

The source code will be added by SAM Diskimage Manager as a SAM CODE file; but MasterBASIC can open it like an OPENTYPE file. Unlike with normal OPENTYPE files, this way the first nine bytes must be skipped (they are the file header): OPEN #4,"filename" IN:let useless$=INP$(#4,9).

    1 REM MBimport 3 (Version A-20110907)
      (C) 2011 Marcos Cruz (programandala.net)
      License programandala.net/license
    2 MODE 1
      OPEN BLOCKS 1
    3 DEF PROC about
        CLS #
        PRINT PEN 5;"MBimport 3"'"Copyright (C) 2011 Marcos Cruz"'"(programandala.net)"''
      END PROC
    4 DEF PROC import file$,lineN
    5   DEFAULT lineN=lastLineByMBimport+1
    6   LOCAL line$,stream
        LET line$="",stream=4
    7   about
    8   SCROLL CLEAR
        ON ERROR STOP
    9   CLOSE #stream
        OPEN #stream,file$ IN
        LET line$=INP$(#stream,9)
   10   DO WHILE NOT EOF stream
   11     INPUT #stream; LINE line$
   12     REM LET line$=STR$ lineN+" "+line$
          PRINT line$
          KEYIN line$
          REM Alternative 1, no error checking
   13      KeyinLine line$
          REM Alternative 2, with error checking
   14     LET lineN=lineN+1
   15   LOOP
   16   KEYIN "label lastLineByMBimport"
   17   CLOSE #stream
   18   SCROLL RESTORE
   19   DEF PROC keyinLine l$
   20     ON ERROR GO TO syntaxError
          KEYIN STR$ lineN+l$
          ON ERROR STOP
          EXIT PROC
   21     LABEL syntaxError
          ON ERROR STOP
          PRINT lineN; PEN 2;l$
          KEYIN STR$ lineN+"REM MIS"+"TAKE "+l$
   22   END PROC
   23   DEF PROC sr
          SCROLL RESTORE
        END PROC
   24   DEF PROC c
          REM Clear after an error
   25     sr
          CLOSE *
          CLEAR
          STOP
        END PROC
   26   DEF PROC d
          REM Delete the imported code
   27     DELETE lastLineByMBimport+1 TO
        END PROC
   28   DEF PROC dMBimport
          REM Delete MBimport
   29     DELETE TO VAL "99"
        END PROC
   30   DEF PROC r
          REM Renum MBimport
   31     RENUM TO endOFMBimport LINE 1 STEP 1
        END PROC
   32   DEF PROC s
          REM Save MBimport 3
   33     d
          ERASE "MBimport3~"
          RENAME "MBimport3","MBimport3~"
          SAVE "MBimport3"
        END PROC
   34   LABEL endOfMBimport
   99   LABEL lastLineByMBimport
        REM This line must be 99

MBimport 4

The fourth version is a modified copy of the second version. It was created in 2012-11 in order to check the influence of label in the keyin's errors. Every #!label#! was removed from the program. Also the debug code was removed, what makes the program a bit faster.

    1 REM MBimport 4 (version A-20121208)
      (C) 2012 Marcos Cruz (programandala.net)
      License programandala.net/license
    2 REM Change log
      2012-11-15, first version, copy of MBimport 2 without labels
      2012-11-16, debug checks removed.
      2012-11-20, instructions,error report.
      2012-12-06, better instructions; mode 3.
      2012-12-08, better instructions; better final error message.
    3 screenConfig
      about
      STOP
    4 DEF PROC screenConfig scrMode
        DEFAULT scrMode=3
    5   MODE scrMode
        IF scrMode=1
          LET black=0,green=4,red=2,white=7
        ELSE
          LET black=0,green=1,red=2,white=3
          PALETTE black,0
          PALETTE green,68
          PALETTE red,2
          PALETTE white,15
        END IF
    6   PAPER black
        PEN white
        BORDER black
        CLS
      END PROC
    7 DEF PROC about
        PRINT "MBimport 4"'"Copyright (C) 2012 Marcos Cruz (programandala.net)"''"Usage:"
    8   PRINT '"1) Make sure there's enough memory for your program to be imported."'"   Use FREE, FPAGES, OPEN and CLEAR if needed."
    9   PRINT '"2) Insert into drive 2 the fake MGT disk image created by MBim2MB."
   10   PRINT '"3) Type the command 'IMPORT'."'"   If the disk image is in drive 1, use 'IMPORT 1' instead."
   11   PRINT '"If the import process fails with the error message ""Not understood"" and the source"'"line is right (it's in 'srcLine$'), simply try CONTINUE. It's a KEYIN's bug."
   12   PRINT '"For more details see:"'"<http://programandala.net/en.program.mbim>"
   13 END PROC
   14 DEF PROC import drive,lineN
        DEFAULT drive=2,lineN=MBimport4Top+1
   15   LOCAL srcLine$,buffer,bufferEnd,cr$,endOfSource,sectorLen,track,sector,sliceStart,CRPos,lastTrack,errors
   16   LET cr$=CHR$ 13,endOfSource=0,sectorLen=512,track=0,sector=1,CRPos=0,lastTrack=79 BOR 128,buffer=RAMTOP,bufferEnd=buffer+sectorLen-1,errors=0
   17   CLS
        SCROLL CLEAR
        loadSector
   18   DO
          diskTo srcLine$
        EXIT IF endOfSource
          keyinLine srcLine$
        LOOP
   19   done
        SCROLL RESTORE
   20 END PROC
   21 DEF PROC done
   22   PRINT '"Done!"
   23   errorReport
   24   PRINT '"Press the space bar to delete"'"MBimport and renumber your"'"program, or any other key to"'"stop and do it manually"'"(DELETE TO ";MBimport4Top;")."
   25   GET key$
        IF key$=" " THEN KEYIN "delete to mbimport4top:renum:scroll restore:list:stop"
        ELSE STOP
   26 END PROC
   27 DEF PROC errorReport
   28   IF NOT errors THEN EXIT PROC
   29   PRINT ' PAPER red; BRIGHT 1;"Warning:"
   30   IF errors=1
          PRINT "There was one syntax error.";
        ELSE IF errors>1
          PRINT "There were ";errors;" syntax errors.";
        END IF
   31   PRINT " 'REM MISTAKE' has been prefixed to the wrong line";"s" AND (errors>1);"."'
   32 END PROC
   33 DEF PROC diskTo REF diskLine$
   34   LET diskLine$=""
   35   IF NOT PEEK (sliceStart) THEN
          LET endOfSource=1
          EXIT PROC
   36   DO
   37     LET crPos=LOCN(sliceStart,bufferEnd,cr$,ABS )
   38     IF crPos
   39       LET diskLine$=diskLine$+MEM$(sliceStart TO crPos-1)
   40       IF crPos=bufferEnd THEN feedBuffer
            ELSE LET sliceStart=crPos+1
   41     ELSE
   42       LET diskLine$=diskLine$+MEM$(sliceStart TO bufferEnd)
            feedBuffer
   43     END IF
   44   LOOP UNTIL crPos
   45 END PROC
   46 DEF PROC feedBuffer
        nextSector
        loadSector
      END PROC
   47 DEF PROC loadSector d,t,s
        DEFAULT d=drive,t=track,s=sector
        READ AT d,t,s,buffer,1
        LET sliceStart=buffer
      END PROC
   48 DEF PROC nextSector
        IF sector<10 THEN LET sector=sector+1
        ELSE nextTrack
   49 END PROC
   50 DEF PROC nextTrack
   51   IF track=lastTrack THEN wipeBuffer
          LET endOfSource=1
          EXIT PROC
   52   IF FN side0(track)
          DoSide1 track
        ELSE doSide0 track
          LET track=track+1
        END IF
   53   LET sector=1
   54 END PROC
   55 DEF FN side0(t)=t<80
   56 DEF PROC doSide1 REF track
        LET track=track+128
      END PROC
   57 DEF PROC doSide0 REF track
        LET track=track-128
      END PROC
   58 DEF PROC debug message$,color
   59   DEFAULT color=red
   60   PRINT PEN color;message$
   61   PAUSE
   62 END PROC
   63 DEF PROC keyinLine l$
   64   LOCAL color
        LET color=green
   65   ON ERROR GO TO syntaxError
        KEYIN STR$ lineN+" "+l$
        ON ERROR STOP
        GO TO printLine
   66   LABEL syntaxError
        ON ERROR STOP
        LET errors=errors+1
        KEYIN STR$ lineN+"REM MISTAKE "+l$
        LET color=red
   67   LABEL printLine
        PRINT lineN-MBimport4Top; PEN color;l$
   68   LET lineN=lineN+1
   69 END PROC
   70 DEF PROC d4
        DELETE TO MBimport4Top
      END PROC
   71 DEF PROC r4
        RENUM TO MBimport4Top LINE 1 STEP 1
      END PROC
   72 DEF PROC s4
        r4
        ERASE "MBimport4~"
        RENAME "MBimport4","MBimport4~"
        SAVE "MBimport4" LINE 1
      END PROC
   73 LABEL MBimport4Top
      REM The first line number of the imported code will be one more than this one.
      --------------------------



MBimport 5

    1 REM MBimport 5 (version A-02-201212081830)
      (C) 2012 Marcos Cruz (programandala.net)
      License programandala.net/license
    2 MODE 3
      CLS #
      MOVE "usage5" TO #2
    3 DEF PROC import drive
        DEFAULT drive=2
        LET cr$=CHR$ 13,endOfSource=0,sectorLen=512,sectorsPerTrack=10,trackLen=sectorsPerTrack*sectorLen,track=127,crPos=0,lastTrack=79 BOR 128,buffer=RAMTOP,bufferEnd=buffer+trackLen-1,lineN=1
        MODE 2
        CLS #
        feedBuffer
    4   DO
        EXIT IF NOT PEEK (sliceStart)
          LET l$=""
          DO
            LET crPos=LOCN(sliceStart,bufferEnd,cr$,ABS )
            IF crPos
              LET l$=l$+MEM$(sliceStart TO crPos-1)
              IF crPos=bufferEnd
                feedBuffer
              ELSE LET sliceStart=crPos+1
              END IF
            ELSE LET l$=l$+MEM$(sliceStart TO bufferEnd)
              feedBuffer
            END IF
          LOOP UNTIL crPos
          PRINT AT 0,0;lineN
          KEYIN l$
          LET lineN=lineN+1
        LOOP
        KEYIN "delete to MBimport5Top:renum line 1 step 1:cls#:print ""Done!"":scroll restore:list:stop"
      END PROC
    5 DEF PROC feedBuffer
        IF track=lastTrack
          LET endOfSource=1
        ELSE
          IF track<80
            LET track=track BOR 128
          ELSE LET track=track-127
          END IF
          READ AT drive,track,1,buffer,10
          LET sliceStart=buffer
        END IF
      END PROC
    6 DEF PROC s5
        DELETE MBimport5Top+1 TO
        RENUM LINE 1 STEP 1
        SAVE OVER "MBimport5" LINE 1
      END PROC
    7 LABEL MBimport5Top

Downloads

The MGT disk image contains:

Related pages

MasterBASIC Vim syntax file
Vim syntax file for MasterBASIC.