%{ /** * Parser for the ULNAv2-C instruction set. * * Generates an image file suitable to be read by Logisim. * * Copyright(c) 2019-2020,2024 Jason Tang * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include "ulnav2-c-as.h" void yyerror(char *s); int yylex(void); static bool check_imm(long lval, int bits); static bool check_simm5(long lval, int *newsimm5); %} %union { char *sval; long lval; int regnum; } %token ADD AND BR CMP LDW OR STW %token ADDI ASH BRL LDWI LSH NEGI ROT STWI %token ANDI BL CMPI MOVI MOVIS MOVUP ORI %token B BEQ BGE BGT BLE BLT BNE HALT %token MUL %token LABEL %token TARGET %token RNUM %token IMM %% program: program line | line ; line: label '\n' | operand '\n' | error { yyclearin; input_parsed = false; } '\n' { yyerrok; } | '\n' ; label: LABEL { add_label($1); } ; operand: op_a | op_b | op_c | op_d ; op_a: ADD RNUM ',' RNUM ',' RNUM { add_inst_a(1, $2, $4, $6); } | AND RNUM ',' RNUM ',' RNUM { add_inst_a(2, $2, $4, $6); } | BR RNUM { add_inst_a(4, 0, $2, 0); } | CMP RNUM ',' RNUM { add_inst_a(5, 0, $2, $4); } | MUL RNUM ',' RNUM ',' RNUM { #ifdef FEATURE_MUL add_inst_a(6, $2, $4, $6); #else yyerrorv(false, "This assembler does not support the MUL instruction."); YYERROR; #endif } | LDW RNUM ',' RNUM ',' RNUM { add_inst_a(7, $2, $4, $6); } | OR RNUM ',' RNUM ',' RNUM { add_inst_a(0, $2, $4, $6); } | STW RNUM ',' RNUM ',' RNUM { add_inst_a(3, $2, $4, $6); } ; op_b: ADDI RNUM ',' RNUM ',' IMM { if (!check_imm($6, 5)) YYERROR; add_inst_b(9, $2, $4, $6, NULL); } | ASH RNUM ',' RNUM ',' IMM { int simm5; if (!check_simm5($6, &simm5)) YYERROR; add_inst_b_simm(8, $2, $4, simm5, NULL); } | BRL RNUM ',' RNUM { add_inst_b(13, $2, $4, 0, NULL); } | LDWI RNUM ',' RNUM ',' IMM { if (!check_imm($6, 5)) YYERROR; add_inst_b(15, $2, $4, $6, NULL); } | LSH RNUM ',' RNUM ',' IMM { int simm5; if (!check_simm5($6, &simm5)) YYERROR; add_inst_b_simm(10, $2, $4, simm5, NULL); } | NEGI RNUM ',' RNUM ',' IMM { if (!check_imm($6, 5)) YYERROR; add_inst_b(12, $2, $4, $6, NULL); } | ROT RNUM ',' RNUM ',' IMM { int simm5; if (!check_simm5($6, &simm5)) YYERROR; add_inst_b_simm(14, $2, $4, simm5, NULL); } | STWI RNUM ',' RNUM ',' IMM { if (!check_imm($6, 5)) YYERROR; add_inst_b(11, $2, $4, $6, NULL); } ; op_c: ANDI RNUM ',' IMM { if (!check_imm($4, 8)) YYERROR; add_inst_c(18, $2, $4, NULL); } | BL RNUM ',' IMM { if (!check_imm($4, 8)) YYERROR; add_inst_c(23, $2, $4, NULL); } | CMPI RNUM ',' IMM { if (!check_imm($4, 8)) YYERROR; add_inst_c(21, $2, $4, NULL); } | MOVI RNUM ',' IMM { if (!check_imm($4, 8)) YYERROR; add_inst_c(20, $2, $4, NULL); } | MOVIS RNUM ',' IMM { if (!check_imm($4, 8)) YYERROR; add_inst_c(22, $2, $4, NULL); } | MOVUP RNUM { #ifdef FEATURE_MUL add_inst_c(17, $2, 0, NULL); #else yyerrorv(false, "This assembler does not support the MOVUP instruction."); YYERROR; #endif } | ORI RNUM ',' IMM { if (!check_imm($4, 8)) YYERROR; add_inst_c(16, $2, $4, NULL); } | BL RNUM ',' TARGET { add_inst_c(23, $2, 0, $4); } ; op_d: B IMM { if (!check_imm($2, 11)) YYERROR; add_inst_d(24, $2, NULL); } | BEQ IMM { if (!check_imm($2, 11)) YYERROR; add_inst_d(26, $2, NULL); } | BGE IMM { if (!check_imm($2, 11)) YYERROR; add_inst_d(27, $2, NULL); } | BGT IMM { if (!check_imm($2, 11)) YYERROR; add_inst_d(25, $2, NULL); } | BLE IMM { if (!check_imm($2, 11)) YYERROR; add_inst_d(30, $2, NULL); } | BLT IMM { if (!check_imm($2, 11)) YYERROR; add_inst_d(28, $2, NULL); } | BNE IMM { if (!check_imm($2, 11)) YYERROR; add_inst_d(29, $2, NULL); } | HALT IMM { if (!check_imm($2, 11)) YYERROR; add_inst_d(31, $2, NULL); } | B TARGET { add_inst_d(24, 0, $2); } | BEQ TARGET { add_inst_d(26, 0, $2); } | BGE TARGET { add_inst_d(27, 0, $2); } | BGT TARGET { add_inst_d(25, 0, $2); } | BLE TARGET { add_inst_d(30, 0, $2); } | BLT TARGET { add_inst_d(28, 0, $2); } | BNE TARGET { add_inst_d(29, 0, $2); } ; %% static bool check_imm(long lval, int bits) { long maxval = 1 << bits; if ((lval < 0) && (lval >= -1 * maxval)) { return true; } if ((lval >= 0) && (lval < maxval)) { return true; } yyerrorv(false, "immediate %ld not within range %ld to %ld", lval, -1 * maxval, maxval - 1); return false; } static bool check_simm5(long lval, int *newsimm5) { int min = 0; int max = (1 << 5) - 1; if (lval < 0 || lval > max) { yyerrorv(false, "immediate %ld not within range %d to %d", lval, min, max); return false; } *newsimm5 = lval; if (*newsimm5 & 0x10) { *newsimm5 = -1 * (*newsimm5 & 0x0f); } return true; }