MBim

Description of the page content

Cross-development toolkit for MasterBASIC and SimCoupe.

Project developed from 2011-03-10 to 2012-12.

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 numbers.

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

Until this page is updated, the BBim format (page in Spanish) can be consulted, as it's very similar to MBim.

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"


Downloads

Related pages

MasterBASIC Vim syntax file
Vim syntax file for MasterBASIC.