make program work sorta

This commit is contained in:
sfja 2026-01-25 02:57:05 +01:00
parent 7878663268
commit eab5a313ab
9 changed files with 404 additions and 109 deletions

View File

@ -1,13 +1,20 @@
const KBD_STATUS 0x1ffc const fl_zero 0x1
const KBD_CODE 0x1ffe const fl_eq 0x2
const VCD 0x2000 const fl_be 0x4
const fl_lt 0x8
const fl_err 0xa
const FL_EQ 0x2 const vcd_base 0x2000
const KBD_FLAG_IS_RELEASE 0x1 const kbd_status 0x1ffc
const kbd_code 0x1ffe
const kbd_flag_is_release 0x1
const counter 0x600
_start:
mov rsp, 0x1000 mov rsp, 0x1000
mov rbp, rsp mov rbp, rsp
@ -18,7 +25,7 @@ const KBD_FLAG_IS_RELEASE 0x1
; setup video character display (VCD) ; setup video character display (VCD)
; descriptor table structure: ; descriptor table structure:
; [addr + 0] memory map base ; [addr + 0] memory map base
mov r0, VCD mov r0, vcd_base
mov [0x700], r0 mov [0x700], r0
lvcd 0x700 lvcd 0x700
@ -27,9 +34,9 @@ const KBD_FLAG_IS_RELEASE 0x1
; [addr + 0] status address ; [addr + 0] status address
; [addr + 2] keycode address ; [addr + 2] keycode address
; [addr + 4] keyboard interrupt handler ; [addr + 4] keyboard interrupt handler
mov r0, KBD_STATUS mov r0, kbd_status
mov [0x702], r0 mov [0x702], r0
mov r0, KBD_CODE mov r0, kbd_code
mov [0x704], r0 mov [0x704], r0
mov r0, key_press_int mov r0, key_press_int
mov [0x706], r0 mov [0x706], r0
@ -37,33 +44,91 @@ const KBD_FLAG_IS_RELEASE 0x1
; character counter ; character counter
mov r0, 0 mov r0, 0
mov [0x600], r0 mov [counter], r0
main_loop: main_loop:
hlt hlt
jmp main_loop jmp main_loop
key_press_int: key_press_int:
mov r0, [KBD_STATUS] mov r0, [kbd_status]
and r0, r0, KBD_FLAG_IS_RELEASE and r0, kbd_flag_is_release
jnz r0, .leave jnz r0, .leave
mov r0, [0x600] mov r0, [counter]
add r0, r0, VCD add r0, vcd_base
mov r1, [KBD_CODE] mov r1, [kbd_code]
cmp r1, 44 ; spacebar cmp r1, 44 ; spacebar
mov r2, rfl mov r2, rfl
and r2, r2, FL_EQ and r2, fl_eq
jnz r2, .incr ; jnz r2, .incr
add r1, r1, 61 ; add r1, 61
mov byte [r0], r1 ; mov byte [r0], r1
.incr: ;.incr:
mov r0, [0x600] ; mov r0, [counter]
add r0, r0, 1 ; add r0, 1
mov [0x600], r0 ; mov [counter], r0
jnz r2, .print_space
add r1, 61 ; scancode letter -> ascii
mov [rsp], r0
add rsp, 2
call print_char
jmp .leave
.print_space:
mov r0, ' '
mov [rsp], r0
add rsp, 2
call print_char
.leave: .leave:
reti reti
print_char:
mov [rsp], rbp
mov rbp, rsp
add rsp, 2
mov r1, [rbp-6]
add r0, vcd_base
mov r1, [kbd_code]
cmp r1, ' '
mov r2, rfl
and r2, fl_eq
jnz r2, .incr
add r1, 61
mov byte [r0], r1
.incr:
mov r0, [counter]
add r0, 1
mov [counter], r0
.leave:
sub rsp, 2
mov rbp, [rsp]
ret
;print_int:
; mov [rsp], rbp
; add rsp, 2
; mov rbp, rsp
; add rsp, rsp, 2
;
; mov r1, [rbp-2]
;
; mov r2, 10000
; mov r3, 0
; jmp .c_10000
;.b_10000:
; add r3, 1
; sub r1, r2
;.c_10000:
; cmp r2, r1
; mov r0, rfl
; and r0, fl_eq | fl_lt
; jnz r0, .b_10000
; add r3, 48
;scancode_to_char: ;scancode_to_char:
; cmp r1, 61 ; cmp r1, 61
; ;

View File

@ -101,7 +101,11 @@ auto Assembler::assemble_file()
} }
assert(value->ty == EOT::Imm); assert(value->ty == EOT::Imm);
m_syms[std::string(stmt->ident)] = value->as_imm(); if (auto result
= define_sym(std::string(stmt->ident), value->as_imm());
not result) {
error(stmt->loc, result.error());
}
} else if (parser.next_is_align()) { } else if (parser.next_is_align()) {
auto stmt = parser.parse_align(); auto stmt = parser.parse_align();
@ -172,9 +176,20 @@ auto Assembler::assemble_file()
m_second_pass = true; m_second_pass = true;
for (const auto& line : lines) { for (const auto& line : lines) {
if (line->labels) {
for (const auto& label : *line->labels) {
if (not label.is_local) {
m_superlabel = label.ident;
}
}
}
assemble_line(*line); assemble_line(*line);
} }
if (m_failed)
return std::unexpected("assembling failed");
return std::move(m_program); return std::move(m_program);
} }
@ -182,10 +197,17 @@ void Assembler::define_labels(const std::vector<Label>& labels)
{ {
for (const auto& label : labels) { for (const auto& label : labels) {
if (label.is_local) { if (label.is_local) {
m_syms[label_ident(label)] = m_builder.ip(); if (auto result = define_sym(label_ident(label), m_builder.ip());
not result) {
error(label.loc, result.error());
}
} else { } else {
m_superlabel = label.ident; m_superlabel = label.ident;
m_syms[std::string(label.ident)] = m_builder.ip(); if (auto result
= define_sym(std::string(label.ident), m_builder.ip());
not result) {
error(label.loc, result.error());
}
} }
} }
} }
@ -205,36 +227,62 @@ auto Assembler::sublabel_ident(std::string_view ident) const -> std::string
namespace { namespace {
enum class Mnemonic { enum class Mnemonic {
// clang-format off db,
db, dw, nop, dw,
hlt, jmp, jnz, nop,
mov, cmp, hlt,
or_, and_, xor_, jmp,
shl, rshl, shr, jnz,
rshr, add, sub, mov,
rsub, reti, lvcd, cmp,
lkbd, dskr, dskw, or_,
// clang-format on and_,
xor_,
shl,
rshl,
shr,
rshr,
add,
sub,
rsub,
call,
ret,
reti,
lvcd,
lkbd,
dskr,
dskw,
}; };
using M = Mnemonic; using M = Mnemonic;
static const auto mnemonic_map static const auto mnemonic_map
= std::unordered_map<std::string_view, Mnemonic> { = std::unordered_map<std::string_view, Mnemonic> {
// clang-format off { "db", M::db },
{ "db", M::db }, { "dw", M::dw }, { "dw", M::dw },
{ "nop", M::nop }, { "hlt", M::hlt }, { "nop", M::nop },
{ "jmp", M::jmp }, { "jnz", M::jnz }, { "hlt", M::hlt },
{ "mov", M::mov }, { "cmp", M::cmp }, { "jmp", M::jmp },
{ "or", M::or_ }, { "and", M::and_ }, { "jnz", M::jnz },
{ "xor", M::xor_ }, { "shl", M::shl }, { "mov", M::mov },
{ "rshl", M::rshl }, { "shr", M::shr }, { "cmp", M::cmp },
{ "rshr", M::rshr }, { "add", M::add }, { "or", M::or_ },
{ "sub", M::sub }, { "rsub", M::rsub }, { "and", M::and_ },
{ "reti", M::reti }, { "lvcd", M::lvcd }, { "xor", M::xor_ },
{ "lkbd", M::lkbd }, { "dskr", M::dskr }, { "shl", M::shl },
{ "rshl", M::rshl },
{ "shr", M::shr },
{ "rshr", M::rshr },
{ "add", M::add },
{ "sub", M::sub },
{ "rsub", M::rsub },
{ "call", M::call },
{ "ret", M::ret },
{ "reti", M::reti },
{ "lvcd", M::lvcd },
{ "lkbd", M::lkbd },
{ "dskr", M::dskr },
{ "dskw", M::dskw }, { "dskw", M::dskw },
// clang-format on
}; };
} }
@ -412,11 +460,24 @@ void Assembler::assemble_line(const Line& line)
case M::add: case M::add:
case M::sub: case M::sub:
case M::rsub: { case M::rsub: {
if (arg_count_wrong(line, 3)) auto dst = std::unique_ptr<EvaledOperand> {};
return; auto op1_store = std::unique_ptr<EvaledOperand> {};
auto dst = eval_operand(*line.args[0]); auto op2 = std::unique_ptr<EvaledOperand> {};
auto op1 = eval_operand(*line.args[1]);
auto op2 = eval_operand(*line.args[2]); EvaledOperand* op1;
if (line.args.size() == 3) {
dst = eval_operand(*line.args[0]);
op1_store = eval_operand(*line.args[1]);
op1 = &*op1_store;
op2 = eval_operand(*line.args[2]);
} else if (line.args.size() == 2) {
dst = eval_operand(*line.args[0]);
op1 = &*dst;
op2 = eval_operand(*line.args[1]);
} else {
operation_not_supported();
}
if (not dst or not op1 or not op2) if (not dst or not op1 or not op2)
return; return;
@ -501,6 +562,28 @@ void Assembler::assemble_line(const Line& line)
} }
break; break;
} }
case Mnemonic::call: {
if (arg_count_wrong(line, 1))
return;
auto op = eval_operand(*line.args[0]);
if (not op)
return;
if (op->is_reg()) {
l.call_reg(op->as_reg());
} else if (op->is_imm()) {
l.call_imm(op->as_imm());
} else {
operation_not_supported();
}
break;
}
case Mnemonic::ret: {
if (arg_count_wrong(line, 0))
return;
l.ret();
break;
}
case M::reti: { case M::reti: {
if (arg_count_wrong(line, 0)) if (arg_count_wrong(line, 0))
return; return;
@ -715,32 +798,28 @@ auto Assembler::eval_operand_to_imm(const Expr& expr)
auto loc = expr.loc; auto loc = expr.loc;
switch (expr.ty) { switch (expr.ty) {
case Expr::Ty::Ident: { case Expr::Ty::Ident: {
auto ident = std::string(expr.as_ident()); auto result = get_sym(std::string(expr.as_ident()));
if (!m_syms.contains(ident)) { if (not result) {
if (not m_second_pass) if (not m_second_pass)
return std::make_unique<EO>(loc, EOT::Imm, uint16_t { 0 }); return std::make_unique<EO>(loc, EOT::Imm, uint16_t { 0 });
error(expr.loc, error(expr.loc, result.error());
std::format("symbol \"{}\" not defined", expr.as_ident()));
return nullptr; return nullptr;
} }
auto value = m_syms[ident]; return std::make_unique<EO>(loc, EOT::Imm, *result);
return std::make_unique<EO>(loc, EOT::Imm, value);
} }
case Expr::Ty::SubLabel: { case Expr::Ty::SubLabel: {
auto ident = sublabel_ident(expr.as_ident()); auto result = get_sym(sublabel_ident(expr.as_ident()));
if (!m_syms.contains(ident)) { if (not result) {
if (not m_second_pass) if (not m_second_pass)
return std::make_unique<EO>(loc, EOT::Imm, uint16_t { 0 }); return std::make_unique<EO>(loc, EOT::Imm, uint16_t { 0 });
error(expr.loc, error(expr.loc, result.error());
std::format("symbol \"{}\" not defined", expr.as_ident()));
return nullptr; return nullptr;
} }
auto value = m_syms[ident]; return std::make_unique<EO>(loc, EOT::Imm, *result);
return std::make_unique<EO>(loc, EOT::Imm, value);
} }
case Expr::Ty::Reg: case Expr::Ty::Reg:
error(loc, "registers cannot be part of an expression"); error(loc, "registers cannot be part of an expression");
@ -830,6 +909,32 @@ bool Assembler::arg_count_wrong(const Line& ins, size_t count)
return false; return false;
} }
auto Assembler::define_sym(std::string ident, uint16_t value)
-> std::expected<void, std::string>
{
std::println("define_sym(\"{}\", {})", ident, value);
if (m_syms.contains(ident)) {
return std::unexpected(
std::format("symbol \"{}\" already defined", ident));
}
m_syms[std::move(ident)] = value;
return {};
}
auto Assembler::get_sym(const std::string& ident)
-> std::expected<uint16_t, std::string>
{
std::println(" get_sym(\"{}\")", ident);
if (not m_syms.contains(ident)) {
return std::unexpected(std::format("symbol \"{}\" not defined", ident));
}
return m_syms[ident];
}
void Assembler::error(Loc loc, std::string_view message) void Assembler::error(Loc loc, std::string_view message)
{ {
m_failed = true; m_failed = true;
@ -1003,15 +1108,19 @@ auto Parser::parse_binary(int prec) -> std::unique_ptr<Expr>
auto left = parse_binary(prec - 1); auto left = parse_binary(prec - 1);
auto should_continue = false; auto should_continue = true;
while (should_continue) { while (should_continue) {
should_continue = false; should_continue = false;
for (auto [op, ty, p] : ops) { for (auto [op, ty, p] : ops) {
if (prec >= p and eat(op)) { if (prec >= p and eat(op)) {
auto right = parse_binary(prec - 1); auto right = parse_binary(prec - 1);
left = std::make_unique<Expr>(loc, left = std::make_unique<Expr>(loc,
ty, ty,
Expr::Binary { std::move(left), std::move(right) }); Expr::Binary { std::move(left), std::move(right) });
should_continue = true;
break;
} }
} }
} }
@ -1022,13 +1131,14 @@ auto Parser::parse_binary(int prec) -> std::unique_ptr<Expr>
auto Parser::parse_prefix() -> std::unique_ptr<Expr> auto Parser::parse_prefix() -> std::unique_ptr<Expr>
{ {
auto loc = current_loc(); auto loc = current_loc();
auto expr = parse_operand();
auto next = [&]() { return parse_operand(); };
if (eat('-')) { if (eat('-')) {
return std::make_unique<Expr>(loc, Expr::Ty::Negate, std::move(expr)); return std::make_unique<Expr>(loc, Expr::Ty::Negate, next());
} else if (eat('!')) { } else if (eat('!')) {
return std::make_unique<Expr>(loc, Expr::Ty::Not, std::move(expr)); return std::make_unique<Expr>(loc, Expr::Ty::Not, next());
} else { } else {
return expr; return next();
} }
} }

View File

@ -234,6 +234,11 @@ namespace asmer {
/// true means fail /// true means fail
bool arg_count_wrong(const Line& ins, size_t count); bool arg_count_wrong(const Line& ins, size_t count);
auto define_sym(std::string ident, uint16_t value)
-> std::expected<void, std::string>;
auto get_sym(const std::string& ident)
-> std::expected<uint16_t, std::string>;
void error(Loc loc, std::string_view message); void error(Loc loc, std::string_view message);
std::string_view m_filename; std::string_view m_filename;

View File

@ -373,6 +373,29 @@ void Builder::binary_imm(Reg dst, Reg op1, uint16_t op2, Op op)
i.build(*this); i.build(*this);
} }
void Builder::call_reg(Reg op1)
{
LOG;
auto i = InsBuilder(Op::Call);
i.op1_reg(op1);
i.build(*this);
}
void Builder::call_imm(uint16_t op1)
{
LOG;
auto i = InsBuilder(Op::Call);
i.imm(op1);
i.build(*this);
}
void Builder::ret()
{
LOG;
auto i = InsBuilder(Op::Ret);
i.build(*this);
}
void Builder::reti() void Builder::reti()
{ {
LOG; LOG;

View File

@ -53,6 +53,9 @@ public:
void add_imm(Reg dst, Reg op1, uint16_t op2); void add_imm(Reg dst, Reg op1, uint16_t op2);
void sub_imm(Reg dst, Reg op1, uint16_t op2); void sub_imm(Reg dst, Reg op1, uint16_t op2);
void rsub_imm(Reg dst, Reg op1, uint16_t op2); void rsub_imm(Reg dst, Reg op1, uint16_t op2);
void call_reg(Reg op1);
void call_imm(uint16_t op1);
void ret();
void reti(); void reti();
void lvcd_reg(Reg op1); void lvcd_reg(Reg op1);
void lvcd_imm(uint16_t op1); void lvcd_imm(uint16_t op1);

View File

@ -70,6 +70,8 @@ auto IoDevice::create() -> std::expected<std::unique_ptr<IoDevice>, std::string>
if (SDL_Init(SDL_INIT_VIDEO) != 0) { if (SDL_Init(SDL_INIT_VIDEO) != 0) {
return std::unexpected(SDL_GetError()); return std::unexpected(SDL_GetError());
} }
SDL_StopTextInput();
} }
SDL_Window* window; SDL_Window* window;
@ -204,8 +206,8 @@ auto IoDevice::poll_event() -> std::unique_ptr<IoEvent>
= (event.key.keysym.mod & KMOD_RALT) != 0, = (event.key.keysym.mod & KMOD_RALT) != 0,
}); });
// default: // default:
// std::println(stderr, "warning: unhandled event '{}'", // std::println(
// event.type); // stderr, "warning: unhandled event '{}'", event.type);
} }
} }

View File

@ -6,7 +6,9 @@
#include <print> #include <print>
#include <stdexcept> #include <stdexcept>
#include <string_view> #include <string_view>
#include <thread>
#include <utility> #include <utility>
#include <vector>
using namespace vc5; using namespace vc5;
using namespace std::literals; using namespace std::literals;
@ -16,6 +18,93 @@ void VM::load(uint16_t offset, const uint8_t* data, size_t data_size)
std::memcpy(&m_mem[offset], data, data_size); std::memcpy(&m_mem[offset], data, data_size);
} }
namespace {
class Debugger {
public:
explicit Debugger(VM& vm)
: vm(&vm)
{
}
void run_on_line(bool halted)
{
if (halted) {
m_halted_last_call = true;
return;
}
if (m_continue) {
if (m_halted_last_call and m_break_on_halt) {
m_continue = false;
} else {
auto ip = vm->reg(Reg::Rip);
for (const auto& addr : m_break_on_address) {
if (addr == ip) {
m_continue = false;
break;
}
}
}
if (m_continue)
return;
}
m_halted_last_call = false;
auto line = std::string();
do {
std::print("> ");
std::cout.flush();
std::getline(std::cin, line);
if (line == "r") {
for (uint16_t reg = 0; reg < 10; ++reg) {
constexpr auto reg_strs = std::array {
"r0"sv,
"r1"sv,
"r2"sv,
"r3"sv,
"r4"sv,
"r5"sv,
"rbp"sv,
"rsp"sv,
"rfl"sv,
"rip"sv,
};
std::println(" {: <3} | {:04x} {: 5}",
reg_strs[reg],
vm->reg(static_cast<Reg>(reg)),
vm->reg(static_cast<Reg>(reg)));
}
} else if (line == "c") {
m_continue = true;
break;
} else if (line == "brhlt") {
m_break_on_halt = true;
} else if (line.starts_with("p")) {
uint16_t v
= std::strtoul(line.c_str() + 2, nullptr, 16) & 0xfffe;
std::println("[{: 4x}] {: 4x}", v, vm->word(v));
} else if (line.starts_with("br")) {
uint16_t v
= std::strtoul(line.c_str() + 3, nullptr, 16) & 0xfffe;
m_break_on_address.push_back(v);
}
} while (!line.empty());
}
private:
VM* vm;
std::vector<uint16_t> m_break_on_address {};
bool m_halted_last_call = false;
bool m_break_on_halt = false;
bool m_continue = false;
};
}
int VM::run() int VM::run()
{ {
constexpr uint16_t bootloader_size = 512; constexpr uint16_t bootloader_size = 512;
@ -23,6 +112,8 @@ int VM::run()
m_disk->read(&m_mem[i * m_disk->block_size], i); m_disk->read(&m_mem[i * m_disk->block_size], i);
} }
auto debugger = Debugger(*this);
m_on = true; m_on = true;
while (m_on) { while (m_on) {
if (not m_halted) { if (not m_halted) {
@ -31,42 +122,12 @@ int VM::run()
return result; return result;
} }
// if (not m_halted) { // debugger.run_on_line(m_halted);
// auto line = std::string();
// do {
// std::print("> ");
// std::cout.flush();
// std::getline(std::cin, line);
//
// if (line == "r") {
// for (uint16_t reg = 0; reg < 10; ++reg) {
// constexpr auto reg_strs = std::array {
// "R0"sv,
// "R1"sv,
// "R2"sv,
// "R3"sv,
// "R4"sv,
// "R5"sv,
// "Rbp"sv,
// "Rsp"sv,
// "Rfl"sv,
// "Rip"sv,
// };
// std::println(" {: <3} | {:04x} {: 5}",
// reg_strs[reg],
// m_regs[reg],
// m_regs[reg]);
// }
// } else if (line.starts_with("p")) {
// uint16_t v
// = std::strtoul(line.c_str() + 2, nullptr, 16) &
// 0xfffe;
// std::println("[{: 4x}] {: 4x}", v, word(v));
// }
// } while (!line.empty());
// }
poll_events(); poll_events();
if (m_halted) {
std::this_thread::sleep_for(15ms);
}
} }
return 0; return 0;
} }
@ -255,7 +316,7 @@ int VM::run_instruction()
break; break;
} }
case Op::LoadWord: { case Op::LoadWord: {
auto addr = ins.mem_addr(ins.op2(), eat_word()); auto addr = ins.mem_addr(ins.op1(), eat_word());
if ((addr & 0b1) != 0) { if ((addr & 0b1) != 0) {
std::println(stderr, std::println(stderr,
@ -293,7 +354,7 @@ int VM::run_instruction()
break; break;
} }
case Op::LoadByte: { case Op::LoadByte: {
auto addr = ins.mem_addr(ins.op2(), eat_word()); auto addr = ins.mem_addr(ins.op1(), eat_word());
auto reg = ins.dst_reg(); auto reg = ins.dst_reg();
set_reg(reg, byte(addr)); set_reg(reg, byte(addr));
@ -384,6 +445,22 @@ int VM::run_instruction()
break; break;
} }
case Op::Call: {
auto op1 = ins.op1_or_imm();
auto return_addr = word(*m_rip);
set_word(*m_rsp, return_addr);
*m_rsp -= 2;
*m_rip = op1;
break;
}
case Op::Ret: {
*m_rsp -= 2;
auto return_addr = word(*m_rsp);
*m_rip = return_addr;
break;
}
case Op::RetI: { case Op::RetI: {
*m_rsp -= 2; *m_rsp -= 2;
auto return_addr = word(*m_rsp); auto return_addr = word(*m_rsp);
@ -469,6 +546,7 @@ void VM::poll_events()
set_word(d.keycode_addr, event->as_key_event().key); set_word(d.keycode_addr, event->as_key_event().key);
interrupt(d.int_handler); interrupt(d.int_handler);
} }
continue;
} }
} }
} }
@ -544,6 +622,10 @@ const char* vc5::op_str(Op op)
return "Sub"; return "Sub";
case Op::RSub: case Op::RSub:
return "RSub"; return "RSub";
case Op::Call:
return "Call";
case Op::Ret:
return "Ret";
case Op::RetI: case Op::RetI:
return "RetI"; return "RetI";
case Op::LVCD: case Op::LVCD:

View File

@ -33,6 +33,9 @@ enum class Op : uint16_t {
Sub, Sub,
RSub, RSub,
Call,
Ret,
// return from interrupt // return from interrupt
RetI, RetI,
// load video character display // load video character display

View File

@ -9,7 +9,7 @@ endif
syn keyword Keyword const align db dw syn keyword Keyword const align db dw
syn keyword Keyword nop hlt jmp jnz mov cmp or and xor shl rshl shr rshr add sub rsub syn keyword Keyword nop hlt jmp jnz mov cmp or and xor shl rshl shr rshr add sub rsub
syn keyword Keyword reti lkbd lvcd dskr dskw syn keyword Keyword call ret reti lkbd lvcd dskr dskw
syn keyword Operator r0 r1 r2 r3 r4 r5 rbp rsp rfl rip syn keyword Operator r0 r1 r2 r3 r4 r5 rbp rsp rfl rip
syn match Operator '+' syn match Operator '+'
@ -27,6 +27,8 @@ syn match Number '[1-9][0-9]*'
syn match Number '0b[01]*' syn match Number '0b[01]*'
syn match Number '0x[0-9a-fA-F]*' syn match Number '0x[0-9a-fA-F]*'
syn match Character "'\\\?.'"
syn region String start=+"+ skip=+\\\\\|\\"+ end=+"+ syn region String start=+"+ skip=+\\\\\|\\"+ end=+"+
syn match Comment ";.*$" syn match Comment ";.*$"