MBim
Description of the page content
Cross-development toolkit for MasterBASIC and SimCoupe.
Project developed from 2011-03-10 to 2012-12.
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:
- .mbim.mb: MasterBASIC source code ready to be added into an MGT disk image and imported by MBimport 3 (or copied into the SAM Coupé's memory by SimCoupe and then imported by MBimport 1).
- .mbim.mb.mgt.gz or mbim.mb.mgr.zip: a compressed fake MGT disk image, ready to be imported by MBimport 3 or newer versions.
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
- masterbasic.vim (12.07 KiB) Vim syntax file for MasterBASIC.
- mbim.vim (3.19 KiB) MBim Vim syntax file.
- mbimport.mgt.gz (149.93 KiB) MBimport in an MGT disk image, compressed with gzip.
- mbim2mb.vim (14.16 KiB) Vim program to convert the MBim source code into plain MasterBASIC suitable to be imported into SimCoupe.