Setup NeoVim for BASIC ZX Spectrum development
Definitely, typing on the original ZX Spectrum keyboard gives a romantic retro charm. That’s not arguable. But if you want to speed up development, testing, and overall DX, it makes sense to use modern environments like today’s text editors and operating systems. Here’s my humble attempt to set up NeoVim and terminal for a simple workflow with minimal effort. With this setup, you can enjoy the spirit of the 1980s while using the comfort of modern development tools.
Requirements
We will need three things.
- NeoVim – it can usually be installed with your package manager, such as
dnf,apt,apk, etc. - Emulator – nowadays, there are plenty of ZX Spectrum emulators.
Here I use fbzx, simply because it’s free and released under the GPL license. - BASIC to
.TAPfile compiler – I use the simplest one I could find on the web: zmakebas. It’s very easy to build using:bash make make installThis software has no external dependencies, only the standard C library.
Workflow Overview
The idea is stupid simple:
- Write your BASIC program in NeoVim.
- Convert it into a
.TAPfile usingzmakebaswith a shortcut. - Run the generated
.TAPin fbzx emulator.
Configuration
If you struggle with setup NeoVim there are plenty resources on the web how to do this, or check my setup NeoVim IDE.
Compile zmakbase
You need any C compiler. On Linux it is straightforward, download the source code and run:
make
make install
NeoVim setup
Set up BASIC syntax through NeoVim/Vim’s autocommand feature. Add the snippet below to your init.lua, and you’re ready to go.
-- ZX emulator. Here I use fbzx; replace it if you different one.
local zxEmulator = "fbzx"
-- Command to run the zmakebas compiler.
local zmakebasCmd = "<cmd>!zmakebas -o %<.tap %"
-- Command to run the emulator. It may differ for other emulators;
-- adjust as needed.
local zxCmd = "<cmd>!" .. zxEmulator .. " %<.tap"
vim.api.nvim_create_autocmd({ "FileType" }, {
pattern = { "basic" },
callback = function(args)
-- In BASIC, we usually type line numbers manually.
-- Set this to true if you want automatic line numbering,
-- during compilation.
vim.opt.number = false
-- Map the F5 key to save and compile.
vim.keymap.set(
"n",
"<f5>",
"<cmd>w<cr>" .. zmakebasCmd .. "<cr>"
)
-- Map the F6 key to save, compile and run.
vim.keymap.set(
"n",
"<f6>",
zmakebasCmd .. "<cr>" .. zxCmd .. "<cr>"
)
end,
})
Bonus. Simple line auto-increment
Very dirty BASIC line auto-increment, should be enough for the beginning. Works for Enter in insert mode and o in normal mode.
vim.api.nvim_create_autocmd({ "FileType" }, {
pattern = { "basic" },
callback = function(_)
-- Rest of the keymaps from above
-- Auto increment line numbers
local autoincrement = function(opts)
local newline = opts.newline or false
local line = vim.api.nvim_get_current_line()
local num = line:match("^%s*(%d+)")
if not num then
return "\n"
end
local prev = tonumber(num)
local row = vim.api.nvim_win_get_cursor(0)[1]
local step = 10
if row > 1 then
local prevline = vim.api.nvim_buf_get_lines(
0, row - 2, row - 1, false
)[1]
local pnum = prevline:match("^%s*(%d+)")
if pnum then
pnum = tonumber(pnum)
step = prev - pnum
end
end
return string.format(
"%s\n%d\t",
newline and "\n" or "",
prev + step
)
end
vim.keymap.set(
"i", "<CR>",
function()
return autoincrement({ newline = false })
end,
{ buffer = true, expr = true }
)
vim.keymap.set("n", "o", function()
local text = autoincrement({ newline = true })
local lines = {}
for line in text:gmatch("([^\n]*)\n?") do
if line ~= "" then table.insert(lines, line) end
end
vim.api.nvim_put(lines, "l", true, true)
vim.cmd("startinsert!")
end, { noremap = true })
end,
})
vim.api.nvim_create_autocmd({ "BufWritePre" }, {
pattern = "*.bas",
callback = function()
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false)
for i, line in ipairs(lines) do
lines[i] = line:gsub("^(%d+)%s*", "%1\t")
end
vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)
end,
})
Usage
- Open neovim
nvim test.bas. - Type i
10 PRINT "HELLO WORLD". - Hit F6; NeoVim saves the file, compiles it to
.TAPand run tape in emulator.
In emulator type LOAD "" and hit F6
That’s all. Easy.
Feedback
For feedback, please check the contacts section. Before writing, please specify where you came from and who you are. Sometimes spammers go insane. Thank you in advance for your understanding.
