URL
https://opencores.org/ocsvn/zipcpu/zipcpu/trunk
Subversion Repositories zipcpu
Compare Revisions
- This comparison shows the changes necessary to convert path
/zipcpu/trunk/sw
- from Rev 12 to Rev 13
- ↔ Reverse comparison
Rev 12 → Rev 13
/zasm/test.S
8,8 → 8,8
; the CPU. As a disorganized test, it doesn't prove anything |
; beyond the generic operation of the CPU. |
; |
; Status: As of July, 2015, the assembler isn't sophisticated enough |
; to handle the address resolution needed to assemble this file. |
; Status: As of August, 2015, this file assembles, builds, and passes |
; all of its tests in the Verilator simulator. |
; |
; Creator: Dan Gisselquist, Ph.D. |
; Gisselquist Tecnology, LLC |
34,9 → 34,135
; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
; |
sys.bus equ 0xc0000000 |
sys.breaken equ 0x080 |
sys.step equ 0x040 |
sys.gie equ 0x020 |
sys.sleep equ 0x010 |
sys.ccv equ 0x008 |
sys.ccn equ 0x004 |
sys.ccc equ 0x002 |
sys.ccz equ 0x001 |
sys.bu.pic equ 0x000 |
sys.bus.wdt equ 0x001 |
sys.bus.cache equ 0x002 |
sys.bus.ctrpic equ 0x003 |
sys.bus.tma equ 0x004 |
sys.bus.tmb equ 0x005 |
sys.bus.tmc equ 0x006 |
sys.bus.jiffies equ 0x007 |
sys.bus.mtask equ 0x008 |
sys.bus.mpstl equ 0x009 |
sys.bus.mastl equ 0x00a |
sys.bus.mstl equ 0x00b |
sys.bus.utask equ 0x00c |
sys.bus.upstl equ 0x00d |
sys.bus.uastl equ 0x00e |
sys.bus.ustl equ 0x00f |
#define DO_TEST_ASSEMBLER |
test: |
#ifdef DO_TEST_ASSEMBLER |
; We start out by testing our assembler. We give it some instructions, which |
; are then manually checked by disassembling/dumping the result and making |
; certain they match. This is not an automated test, but it is an important |
; one. |
noop |
bra continue_test_with_testable_instructions |
break |
wait |
busy |
rtu |
continue_test_with_testable_instructions: |
; Now, let's place the assembler into a known state |
clr r0 |
mov r0,r1 |
clr r1 |
clr r2 |
clr r3 |
clr r4 |
clr r5 |
clr r6 |
clr r7 |
clr r9 |
clr r10 |
clr r11 |
clr r12 |
clr r13 |
; Don't clear the CC register |
; Don't clear the SP register |
; And repeat for the user registers |
mov R0,uR0 |
mov R0,uR1 |
mov R0,uR2 |
mov R0,uR3 |
mov R0,uR4 |
mov R0,uR5 |
mov R0,uR6 |
mov R0,uR7 |
mov R0,uR8 |
mov R0,uR9 |
mov R0,uR10 |
mov R0,uR11 |
mov R0,uR12 |
mov R0,uR13 |
mov R0,uCC |
; Don't clear the user PC register |
; Now, let's try loading some constants into registers |
dead_beef equ 0xdeadbeef |
ldi 0x0dead,r5 |
ldi 0x0beef,r6 |
ldi 0xdeadbeef,r7 |
ldihi 0xdead, r8 |
ldilo 0xbeef, r8 |
ldi dead_beef,r9 |
cmp r5,r6 |
bz test_failure |
cmp r7,r8 |
bnz test_failure |
ldi $deadbeefh,r7 ; Try loading with the $[HEX]h mneumonic |
cmp r7,r8 |
bnz test_failure |
cmp r7,r9 |
bnz test_failure |
bra skip_dead_beef |
dead_beef.base: |
word 0 |
fill 5,dead_beef |
word 0 |
dead_beef.zero equ 0 |
dead_beef.values equ 1 |
skip_dead_beef: |
lod dead_beef.base(pc),r10 ; Should load a zero here |
cmp r10,r11 ; r11 should still be zero from init abv |
bnz test_failure |
mov dead_beef.base(pc),r10 ; Now, let's get the address |
lod dead_beef.values(r10),r10 ; r10 now equals 0xdeadbeef |
cmp r10,r9 |
bnz test_failure |
|
; Test whether or not we can properly decode OCTAL values |
clr r0 ; Re-clear our register set first |
clr r1 |
clr r2 |
clr r3 |
clr r4 |
clr r5 |
clr r6 |
clr r7 |
clr r9 |
clr r10 |
clr r11 |
clr r12 |
clr r13 |
; |
ldi $024o,r0 |
ldi $20,r1 |
cmp r0,r1 |
bnz test_failure |
ldi $024,r0 |
cmp r0,r1 |
bnz test_failure |
clr r0 |
clr r1 |
mov $1+r0,r2 |
mov $2+r0,r3 |
mov $22h+r0,r4 |
46,30 → 172,76
add r2,r0 |
add $32,r0 |
add $-33,r0 |
bnz test_failure |
not.z r0 |
bge test_failure |
junk_address: |
clrf r0 |
bnz test_failure |
ldi $5,r1 |
cmp $0+r0,r1 |
not.lt r0 |
not.ge r1 |
lod $-7+pc,r2 |
ldihi $deadh,r3 |
ldihi $beefh,r3 |
mov junk_address(pc),r2 ; Test pc-relative addressing |
mov junk_address(pc),r3 |
cmp r2,r3 |
bnz test_failure |
lod junk_address(pc),r5 ; Test loads with pc-relative addressing |
lod junk_address(pc),r6 |
cmp r5,r6 |
bnz test_failure |
; Now, let's test whether or not our LSR and carry flags work |
ldi -1,r0 ; First test: shifting all the way should yield zero |
lsr 32,r0 |
cmp 0,r0 |
bnz test_failure |
ldi -1,r0 ; Second test: anything greater than zero should set |
lsr 0,r0 ; the carry flag |
bc test_failure |
lsr 1,r0 |
tst sys.ccc,cc |
bz test_failure |
lsr 31,r0 |
tst sys.ccc,cc |
bz test_failure |
lsr 1,r0 |
bc test_failure |
; Now repeat the above tests, looking to see whether or not ASR works |
ldi -1,r0 |
asr 32,r0 |
cmp -1,r0 |
bnz test_failure |
ldi -1,r0 |
asr 0,r0 |
bc test_failure |
cmp -1,r0 |
bnz test_failure |
asr 1,r0 |
tst sys.ccc,r14 |
bz test_failure |
asr 30,r0 |
tst sys.ccc,r14 |
bz test_failure |
#endif |
|
#ifdef NOONE // Testing comments after ifdef |
#else ; After else |
#endif /* and after endif */ |
testbench: |
// Let's build a software test bench. |
clr r12 ; R12 will point to our peripherals |
ldihi $c000h,r12 |
ldi $c0000000h,r12 ; Set R12 to point to our peripheral address |
mov r12,ur12 |
mov test_start,upc |
ldihi $8001,r0 |
ldilo $-1,r0 |
sto r0,$1+r12 |
mov test_start(pc),upc |
ldi 0x8000ffff,r0 ; Clear interrupts, turn all vectors off |
sto r0,(r12) |
rtu |
lod r12,r0 |
cmp $0,r0 |
bnz $1 |
mov ucc,r0 |
tst -256,r0 |
bnz test_failure |
halt |
// Go into an infinite loop if the trap fails |
// Permanent loop instruction -- a busy halt if you will |
test_failure: |
busy |
|
; Now for a series of tests. If the test fails, call the trap |
76,77 → 248,77
; interrupt with the test number that failed. Upon completion, |
; call the trap with #0. |
|
; Now for a series of tests. If the test fails, call the trap |
; interrupt with the test number that failed. Upon completion, |
; call the trap with #0. |
|
; Test LDI to PC |
; Some data registers |
.dat __here__+5 |
test_data: |
.dat __here__+0x0100000+5 |
test_start: |
ldi $2,r11 |
lod $-3+pc,pc |
ldi $0x0100,r11 |
lod test_data+pc,pc |
clr r11 |
noop |
cmp $0,r11 |
sto.z r11,(r12) |
trap.z r11 |
add $1,r0 |
add $1,r0 |
|
// Let's test whether overflow works |
ldi $3,r11 |
ldi $0x0200,r11 |
ldi $-1,r0 |
lsr $1,r0 |
add $1,r0 |
bv $1 |
sto r11,(r12) |
bv first_overflow_passes |
trap r11 |
first_overflow_passes: |
// Overflow set from subtraction |
ldi $4,r11 |
ldi $0x0300,r11 |
ldi $1,r0 |
.dat 0x5000001f ; rol $31,r0 |
rol $31,r0 ; rol $31,r0 |
sub $1,r0 |
bv $1 |
sto r11,(r12) |
bv subtraction_overflow_passes |
trap r11 |
subtraction_overflow_passes: |
// Overflow set from LSR |
ldi $5,r11 |
ldi $0x0400,r11 |
ldi $1,r0 |
.dat 0x5000001f ; rol $31,r0 |
rol $31,r0 ; rol $31,r0 |
lsr $1,r0 |
bv $1 |
sto r11,(r12) |
bv lsr_overflow_passes |
trap r11 |
lsr_overflow_passes: |
// Overflow set from LSL |
ldi $6,r11 |
ldi $0x0500,r11 |
ldi $1,r0 |
.dat 0x5000001e |
rol $30,r0 |
lsl $1,r0 |
bv $1 |
sto r11,(r12) |
|
bv lsl_overflow_passes |
trap r11 |
lsl_overflow_passes: |
// Overflow set from LSL, negative to positive |
ldi $7,r11 |
ldi $0x0600,r11 |
ldi $1,r0 |
.dat 0x5000001f; // E: ROL $30,R0 |
rol $31,r0 |
lsl $1,r0 |
bv $1 |
sto r11,(r12) |
|
bv second_lsl_overflow_passes |
trap r11 |
second_lsl_overflow_passes: |
// Test carry |
ldi $0x010,r11 |
ldi $0x0700,r11 |
ldi $-1,r0 |
add $1,r0 |
tst $2,cc |
sto.z r11,(r12) |
trap.z r11 |
// and carry from subtraction |
ldi $17,r11 |
ldi $0x0800,r11 |
sub $1,r0 |
tst $2,cc |
sto.z r11,(r12) |
trap.z r11 |
|
// Let's try a loop: for i=0; i<5; i++) |
// We'll use R0=i, Immediates for 5 |
ldi $0x0800,r11 |
clr r0 |
for_loop: |
ldi $18,r11 |
clr r0 |
noop |
add $1,r0 |
cmp $5,r0 |
158,8 → 330,8
// R0 = 5; (from before) |
// do { |
// } while (R0 > 0); |
ldi $0x0900,r11 |
bgt_loop: |
ldi $19,r11 |
noop |
sub $1,r0 |
bgt bgt_loop |
168,7 → 340,7
// R1 = 5; // Need to do this explicitly |
// do { |
// } while(R1 >= 0); |
ldi $20,r00 |
ldi $20,r0 |
ldi $5,r1 |
bge_loop: |
noop |
180,31 → 352,31
// R0 = 5; (from before) |
// do { |
// } while (R0 > 0); |
ldi $21,r11 |
bra $1 |
ldi $0x0a00,r11 |
bra mem_loop_test |
loop_var: |
.dat 0 |
mem_loop_test: |
mov loop_var(pc),r1 |
ldi $5,r0 |
clr r2 |
sto r0,(r1) |
mem_loop: |
mov $-2+pc,r1 |
clr r2 |
ldi $5,r0 |
sto r1,(r0) |
add $1,r2 |
add $14,r0 |
lod (r1),r0 |
sub $1,r0 |
bgt $-6 |
sto r0,(r1) |
bgt mem_loop |
cmp $5,r2 |
sto.ne r11,(r12) |
trap.ne r11 |
|
// Return success / Test the trap interrupt |
clr r11 |
sto r11,(r12) |
trap r11 |
noop |
noop |
|
// Go into an infinite loop if the trap fails |
// Permanent loop instruction -- a busy halt if you will |
busy |
|
// And, in case we miss a halt ... |
/zasm/zasm.y
0,0 → 1,508
/******************************************************************************* |
** |
** Filename: zasm.y |
** |
** Project: Zip CPU -- a small, lightweight, RISC CPU core |
** |
** Purpose: The parser for the Zip Assembler. This is actually not just |
** the parser, but the main program as well. |
** |
** Creator: Dan Gisselquist, Ph.D. |
** Gisselquist Tecnology, LLC |
** |
******************************************************************************** |
** |
** Copyright (C) 2015, Gisselquist Technology, LLC |
** |
** This program is free software (firmware): you can redistribute it and/or |
** modify it under the terms of the GNU General Public License as published |
** by the Free Software Foundation, either version 3 of the License, or (at |
** your option) any later version. |
** |
** This program is distributed in the hope that it will be useful, but WITHOUT |
** ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
** for more details. |
** |
** You should have received a copy of the GNU General Public License along |
** with this program. (It's in the $(ROOT)/doc directory, run make with no |
** target there if the PDF file isn't present.) If not, see |
** <http://www.gnu.org/licenses/> for a copy. |
** |
** License: GPL, v3, as defined and found on www.gnu.org, |
** http://www.gnu.org/licenses/gpl.html |
** |
** |
*******************************************************************************/ |
|
%{ |
#include <stdio.h> |
#include <string.h> |
#include "asmdata.h" |
|
extern "C" int yylex(void); |
extern "C" int yyparse(void); |
// extern "C" FILE *yyin; |
void yyerror(const char *); |
unsigned int global_parser_pc; |
char *master_input_filename = NULL; |
extern int yylineno; |
char *linecp = NULL; // A copy of the input line |
%} |
|
%token COMMA EQU PLUS MINUS TIMES HERE DOLLAR COLON |
%token BOOLEANOR BITWISEOR BOOLEANAND BITWISEAND BITWISEXOR DOT |
%token WORD FILL |
%token LOADOP STOROP LDIOP |
%token BAREOP BRANCHOP COND DUALOP IDENTIFIER INT LDHLOP REG SINGLOP |
|
%union { |
ZPARSER::ZIPREG u_reg; |
ZPARSER::ZIPCOND u_cond; |
int u_ival; |
LEXOPCODE u_op; |
char *u_id; |
ASMLINE *u_ln; |
AST *u_ast; |
} |
|
%type <u_reg> REG |
%type <u_cond> COND opcond |
%type <u_ival> INT |
%type <u_id> IDENTIFIER |
%type <u_op> BAREOP SINGLOP DUALOP BRANCHOP LDHLOP |
%type <u_ln> unlabeledline instruction wordlist fillist opb |
%type <u_ln> bareop singlop dualop loadop storop line |
%type <u_ast> expr value multident |
|
%% /* The grammar follows */ |
|
input: |
%empty |
| input line { if ($2) {objcode += $2; global_parser_pc += $2->nlines(); if ($2->isdefined()) delete $2; } } |
; |
|
line: |
'\n' { $$ = NULL; } |
| unlabeledline '\n' { $$ = $1; } |
| multident COLON unlabeledline '\n' { |
if ($1->m_node_type == 'I') { |
if (((AST_IDENTIFIER*)$1)->m_id[0] == 'L') |
stb_define(((AST_IDENTIFIER *)$1)->m_id, new AST_NUMBER(global_parser_pc)); |
else |
gbl_define(((AST_IDENTIFIER *)$1)->m_id, new AST_NUMBER(global_parser_pc)); |
delete $1; |
} |
$$ = $3; |
} |
| multident COLON '\n' { |
if ($1->m_node_type == 'I') { |
if (((AST_IDENTIFIER*)$1)->m_id[0] == 'L') |
stb_define(((AST_IDENTIFIER *)$1)->m_id, new AST_NUMBER(global_parser_pc)); |
else |
gbl_define(((AST_IDENTIFIER *)$1)->m_id, new AST_NUMBER(global_parser_pc)); |
delete $1; |
} |
$$ = new VLINE(); |
} |
| multident EQU expr '\n' { |
if ($1->m_node_type == 'I') { |
stb_define(((AST_IDENTIFIER *)$1)->m_id, $3); |
delete $1; |
} |
$$ = new VLINE(); |
} |
; |
|
unlabeledline: |
instruction { $$ = $1; } |
| WORD wordlist { $$ = $2; } |
| FILL fillist { $$ = $2; } |
; |
|
wordlist: |
expr { |
if ($1->isdefined()) |
$$ = new DLINE($1->eval()); |
else { |
$$ = new VLINE(); |
yyerror("ERROR: word list undefined"); |
}} |
| expr COMMA wordlist { |
if ($1->isdefined()) |
$$ = new DLINE($1->eval()); |
else { |
$$ = new VLINE(); |
yyerror("ERROR: word list undefined\n"); |
}} |
; |
|
fillist: |
expr COMMA expr { |
if (($1->isdefined())&&($3->isdefined())) { |
int ntimes = $1->eval(), |
val = $3->eval(); |
LLINE *ln = new LLINE(); |
for(int i=0; i<ntimes; i++) |
ln->addline(new DLINE(val)); |
$$ = ln; |
} else { |
yyerror("Fill list undefined\n"); |
$$ = new VLINE(); |
} |
} |
; |
|
instruction: |
dualop opb COMMA REG { |
$$ = $1; |
((TLINE*)$1)->m_imm = ((TLINE*)$2)->m_imm; ((TLINE*)$2)->m_imm = NULL; |
((TLINE*)$1)->m_opb = ((TLINE*)$2)->m_opb; |
((TLINE*)$1)->m_opa = $4; |
if ($1->isdefined()) { |
$$ = ((TLINE*)$1)->eval(); |
delete $1; |
delete $2; |
} |
} |
| dualop opb COMMA multident { |
char buf[256]; |
sprintf(buf, "%s is not a register", ((AST_IDENTIFIER *)$4)->m_id.c_str()); |
yyerror(buf); |
$$ = new VLINE(); |
delete $1; |
delete $2; |
delete $4; |
} |
| singlop opb { |
$$ = $1; |
((TLINE*)$1)->m_imm = ((TLINE*)$2)->m_imm; ((TLINE*)$2)->m_imm = NULL; |
((TLINE*)$1)->m_opb = ((TLINE*)$2)->m_opb; |
if ($1->isdefined()) { |
$$ = ((TLINE *)$1)->eval(); |
delete $1; |
} |
} |
| bareop { $$ = $1; } |
| LDHLOP opcond expr COMMA REG { |
TLINE *tln = new TLINE; |
tln->m_opcode = $1; |
tln->m_cond = $2; |
tln->m_imm = $3; |
tln->m_opa = $5; |
|
if (tln->isdefined()) { |
$$ = tln->eval(); |
delete tln; |
} else |
$$ = tln; |
} |
| LDHLOP opcond expr COMMA multident { |
char buf[256]; |
sprintf(buf, "%s is not a register", ((AST_IDENTIFIER *)$5)->m_id.c_str()); |
yyerror(buf); |
$$ = new VLINE(); |
delete $3; |
delete $5; |
} |
| LDIOP expr COMMA REG { |
TLINE *tln = new TLINE; |
tln->m_opcode = OP_LDI; |
tln->m_cond = ZPARSER::ZIPC_ALWAYS; |
tln->m_imm = $2; |
tln->m_opa = $4; |
|
if (tln->isdefined()) { |
$$ = tln->eval(); |
delete tln; |
} else |
$$ = tln; |
} |
| LDIOP expr COMMA multident { |
char buf[256]; |
sprintf(buf, "%s is not a register", ((AST_IDENTIFIER *)$4)->m_id.c_str()); |
yyerror(buf); |
$$ = new VLINE(); |
delete $2; |
delete $4; |
} |
| BRANCHOP expr { |
TLINE *tln = new TLINE; |
tln->m_opcode = $1; |
tln->m_imm = $2; |
|
if (tln->isdefined()) { |
$$ = tln->eval(); |
delete tln; |
} else |
$$ = tln; |
} |
| loadop opb COMMA REG { |
TLINE *tln = new TLINE; |
((TLINE*)$2)->m_opcode = OP_LOD; |
((TLINE*)$2)->m_cond = ((TLINE*)$1)->m_cond; |
((TLINE*)$2)->m_opa = $4; |
|
delete $1; |
|
if (((TLINE *)$2)->isdefined()) { |
$$ = ((TLINE *)$2)->eval(); |
delete $2; |
} else |
$$ = $2; |
} |
| loadop opb COMMA multident { |
char buf[256]; |
sprintf(buf, "%s is not a register", ((AST_IDENTIFIER *)$4)->m_id.c_str()); |
yyerror(buf); |
$$ = new VLINE(); |
delete $1; |
delete $2; |
delete $4; |
} |
| storop REG COMMA opb { |
TLINE *tln = new TLINE; |
tln->m_opcode = OP_STO; |
tln->m_cond = ((TLINE*)$1)->m_cond; |
tln->m_imm = ((TLINE*)$4)->m_imm; |
tln->m_opb = ((TLINE*)$4)->m_opb; |
tln->m_opa = $2; |
|
delete $1; |
|
if (tln->isdefined()) { |
$$ = tln->eval(); |
delete tln; |
} else |
$$ = tln; |
} |
| storop multident COMMA opb { |
char buf[256]; |
sprintf(buf, "%s is not a register", ((AST_IDENTIFIER *)$2)->m_id.c_str()); |
yyerror(buf); |
$$ = new VLINE(); |
delete $1; |
delete $2; |
delete $4; |
} |
; |
|
dualop: DUALOP opcond { |
TLINE *tln = new TLINE(); |
tln->m_opcode = $1; |
tln->m_cond = $2; |
$$ = tln; |
} |
; |
|
singlop: SINGLOP opcond { |
TLINE *tln = new TLINE(); |
tln->m_opcode = $1; |
tln->m_cond = $2; |
$$ = tln; |
} |
; |
|
storop: STOROP opcond { |
TLINE *tln = new TLINE(); |
tln->m_opcode = OP_STO; |
tln->m_cond = $2; |
$$ = tln; |
} |
; |
|
loadop: LOADOP opcond { |
TLINE *tln = new TLINE(); |
tln->m_opcode = OP_LOD; |
tln->m_cond = $2; |
$$ = tln; |
} |
; |
|
bareop: BAREOP opcond { |
TLINE *tln = new TLINE(); |
tln->m_opcode = $1; |
tln->m_cond = $2; |
$$ = tln; |
} |
; |
|
opcond: |
COND { $$ = $1; } |
| %empty { $$ = ZPARSER::ZIPC_ALWAYS; } |
; |
|
opb: |
expr { |
TLINE *tln = new TLINE(); |
tln->m_imm = $1; |
$$ = tln; |
} |
| expr PLUS REG { |
TLINE *tln = new TLINE(); |
tln->m_imm = $1; |
tln->m_opb = $3; |
$$ = tln; |
} |
| expr '(' REG ')' { |
TLINE *tln = new TLINE(); |
tln->m_imm = $1; |
tln->m_opb = $3; |
$$ = tln; |
} |
| '(' REG ')' { |
TLINE *tln = new TLINE(); |
tln->m_imm = new AST_NUMBER(0); |
tln->m_opb = $2; |
$$ = tln; |
} |
| REG { |
TLINE *tln = new TLINE(); |
tln->m_imm = new AST_NUMBER(0); |
tln->m_opb = $1; |
$$ = tln; |
} |
; |
|
expr: |
value { $$ = $1; } |
| MINUS value { $$ = new AST_BRANCH('-',new AST_NUMBER(0), $2); } |
| expr PLUS value { $$ = new AST_BRANCH('+',$1,$3); } |
| expr MINUS value { $$ = new AST_BRANCH('-',$1,$3); } |
| expr TIMES value { $$ = new AST_BRANCH('*',$1,$3); } |
| expr BOOLEANOR value { $$ = new AST_BRANCH('o',$1,$3); } |
| expr BITWISEOR value { $$ = new AST_BRANCH('|',$1,$3); } |
| expr BOOLEANAND value { $$ = new AST_BRANCH('a',$1,$3); } |
| expr BITWISEAND value { $$ = new AST_BRANCH('&',$1,$3); } |
| expr BITWISEXOR value { $$ = new AST_BRANCH('^',$1,$3); } |
| '(' expr ')' { $$ = $2; } |
; |
/* expr OR (|) value */ |
/* expr XOR (^) value */ |
/* expr AND (&) value */ |
|
value: |
INT { $$ = new AST_NUMBER($1); } |
| multident { $$ = $1; } |
| HERE { $$ = new AST_NUMBER(global_parser_pc); } |
; |
|
multident: |
IDENTIFIER { $$ = new AST_IDENTIFIER($1); delete $1; } |
| multident DOT IDENTIFIER { $$ = new AST_IDENTIFIER($1,$3); delete $3; } |
; |
%% |
|
#include <unistd.h> |
#include <sys/types.h> |
#include <sys/stat.h> |
#include <sys/fcntl.h> |
#include <assert.h> |
|
OBJFILE objcode; |
|
void yyerror(const char *str) { |
fprintf(stderr, "%s:%d: ERROR: %s\n", master_input_filename, yylineno, str); |
if (linecp) fprintf(stderr, "Offending line was: %s\n", linecp); |
} |
|
FILE *run_preprocessor(const char *zname = NULL) { |
int pipefd[2]; |
int pid; |
|
if (pipe(pipefd)!=0) { |
fprintf(stderr, "PIPE FAILED!\n"); |
perror("O/S Err:"); |
exit(-2); |
} if ((zname)&&(access(zname, R_OK)!=0)) { // if !zname, then use stdin |
fprintf(stderr, "Cannot open %s\n", zname); |
perror("O/S Err:"); |
exit(-2); |
} |
|
|
if (0 == (pid = fork())) { |
int fdin, fdout; |
|
// Child process -- run the preprocessor |
if (zname) { |
fdin = open(zname, O_RDONLY); |
close(STDIN_FILENO); |
dup2(fdin, STDIN_FILENO); |
} // else use stdin, already set up |
|
close(pipefd[0]); |
|
fdout = pipefd[1]; |
close(STDOUT_FILENO); |
dup2(fdout, STDOUT_FILENO); |
|
// This call should never return |
execlp("./zpp", "zpp", NULL); |
|
fprintf(stderr, "Could not start pre-processor!\n"); |
perror("O/S Err:"); |
|
exit(-5); |
} |
|
close(pipefd[1]); |
return fdopen(pipefd[0], "r"); |
} |
|
int main(int argc, char **argv) { |
int skp = 0; |
const char *zout_fname = NULL; |
master_input_filename = NULL; |
|
skp=1; |
for(int argn=0; argn+skp<argc; argn++) { |
if (argv[argn+skp][0] == '-') { |
if (argv[argn+skp][1] == 'o') { |
if (zout_fname) |
free((void *)zout_fname); |
zout_fname = strdup(argv[argn+skp+1]); |
skp++; |
} |
|
skp++; |
} argv[argn] = argv[argn+skp]; |
} argc -= skp; |
|
if (!zout_fname) |
zout_fname = "z.out"; |
|
objcode.open(zout_fname); |
|
master_input_filename = NULL; |
|
if (argc > 0) { |
for(int argn=0; argn<argc; argn++) { |
extern FILE *yyin; |
extern void yyrestart(FILE *); |
FILE *tst; |
|
create_new_context(); |
if (master_input_filename) |
free(master_input_filename); |
master_input_filename = strdup(argv[argn]); |
yyrestart(run_preprocessor(master_input_filename)); |
yylineno = 1; |
yyparse(); |
} |
} else { // Run from Stdin |
extern FILE *yyin; |
extern void yyrestart(FILE *); |
|
create_new_context(); |
master_input_filename = strdup("(stdin)"); |
yyin = run_preprocessor(NULL); |
yyrestart(yyin); |
yyparse(); |
} |
|
if (!objcode.reduce()) |
fprintf(stderr, "Not all symbols defined!\n"); |
} |
|
|
/zasm/asmdata.cpp
0,0 → 1,708
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: asmdata.cpp |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU core |
// |
// Purpose: Like asmdata.h, this contains necessary data structures for the |
// assembler. Specifically, in C/C++ fashion, this contains most |
// of the code for actually building such structures. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Tecnology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
|
#include <stdlib.h> |
#include <assert.h> |
#include <string.h> |
#include "asmdata.h" |
|
extern void yyerror(const char *str); |
|
unsigned int ILINE::eval(const int lno) { |
return (lno==0)?m_in:DEFAULT_LINE; |
} |
|
unsigned int VLINE::eval(const int lno) { |
return DEFAULT_LINE; |
} |
|
unsigned int DLINE::eval(const int lno) { |
return (lno==0)?m_data:DEFAULT_LINE; |
} |
|
void LLINE::addline(ASMLINE *line) { |
if (m_lines != NULL) { |
ASMLINE **nwlines = new ASMLINE *[m_nlines+1]; |
for(int i=0; i<m_nlines; i++) |
nwlines[i] = m_lines[i]; |
delete[] m_lines; |
nwlines[m_nlines++] = line; |
|
m_lines = nwlines; |
} else { |
m_lines = new ASMLINE *[1]; |
m_lines[m_nlines++] = line; |
} |
}; |
|
bool LLINE::isdefined(void) { |
for(int i=0; i<m_nlines; i++) |
if (!m_lines[i]->isdefined()) |
return false; |
return true; |
}; |
|
LLINE::~LLINE(void) { |
for(int i=0; i<m_nlines; i++) |
delete m_lines[i]; |
delete m_lines; |
} |
|
unsigned int LLINE::eval(const int lno) { |
return (lno < m_nlines)?m_lines[lno]->eval(0) : DEFAULT_LINE; |
} |
|
// int m_op; // An operator |
// AST *m_left, *m_right; |
|
int AST_BRANCH::eval(void) { |
int lft = m_left->eval(), rht = m_right->eval(); |
|
switch(m_op) { |
case '+': return lft + rht; |
case '-': return lft - rht; |
case '*': return lft * rht; |
case '/': return lft / rht; |
case '%': return lft % rht; |
case '^': return lft ^ rht; |
case '|': return lft | rht; |
case '&': return lft & rht; |
case '~': return ~lft; |
default: yyerror("Unknown operation"); return lft; |
} |
} void AST_BRANCH::reduce(void) { |
if ((m_left)&&(m_left->m_node_type != 'N')&&(m_left->isdefined())) { |
int val = m_left->eval(); |
delete m_left; |
m_left = new AST_NUMBER(val); |
} else |
m_left->reduce(); |
if ((m_right)&&(m_right->m_node_type != 'N')&&(m_right->isdefined())) { |
int val = m_right->eval(); |
delete m_right; |
m_right = new AST_NUMBER(val); |
} else |
m_right->reduce(); |
} |
|
AST_IDENTIFIER::AST_IDENTIFIER(AST *ida, const char *idb) { |
m_node_type = 'I'; |
m_id = ((AST_IDENTIFIER*)ida)->m_id + "." + std::string(idb); |
delete ida; |
} |
|
bool AST_IDENTIFIER::isdefined(void) { |
bool answer = stb_isdefined(m_id); |
return answer; |
} int AST_IDENTIFIER::eval(void) { |
return stb_value(m_id); |
} void AST_IDENTIFIER::reduce(void) {} |
|
bool AST_LABEL::isdefined(void) { |
bool answer = stb_isdefined(m_label); |
return answer; |
} int AST_LABEL::eval(void) { |
return stb_value(m_label); |
} void AST_LABEL::reduce(void) {} |
|
|
int AST_NUMBER::eval(void) { |
return m_val; |
} void AST_NUMBER::reduce(void) {} |
|
void OBJFILE::open(const char *fname) { |
if ((m_fp != NULL)||(m_pc != 0l)) { |
fprintf(stderr, "Error: Can only change file names at startup\n"); |
exit(-2); |
} |
m_fp = fopen(fname, "w"); |
if (m_fp == NULL) { |
fprintf(stderr, "Cannot open %s for writing\n", fname); |
perror("O/S Err:"); |
m_fp = fopen("/dev/null","w"); |
} |
} |
|
void OBJFILE::operator+=(ASMLINE *ln) { |
unsigned int buf[1]; |
int nlines = ln->nlines(); |
|
if (!ln->isdefined()) { |
// fprintf(stderr, "%08x: Adding undefined line:\n", m_pc); |
// ((TLINE *)ln)->dump(stderr); |
m_tbl.insert(m_tbl.end(), SAVED_ASMLINE(m_pc,ln)); |
/* |
} else { |
fprintf(stderr, "%08x: Adding to file:\n", m_pc); |
((TLINE *)ln)->dump(stderr); |
*/ |
} |
for(int i=0; i<nlines; i++) { |
buf[0] = ln->eval(i); |
if (m_fp) fwrite(buf, sizeof(ZIPI), 1, m_fp); |
m_pc++; |
} |
} |
|
bool OBJFILE::reduce(void) { |
SVDTBL::iterator i; |
bool all_reduced = true; |
|
// printf("Checking for reductions\n"); |
unsigned int tmp = m_pc; |
for(i=m_tbl.begin(); i != m_tbl.end(); i++) { |
// printf("LINE %08x\n", i->m_pc); |
ASMLINE *ln = i->m_ln; |
m_pc = i->m_pc; |
if (ln->isdefined()) { |
// printf("PC = 0x%08x reduces\n", i->m_pc); |
fseek(m_fp, sizeof(ZIPI)*i->m_pc, SEEK_SET); |
for(int k=0; k< ln->nlines(); k++) { |
ZIPI buf[1]; |
m_pc = i->m_pc+k; |
buf[0] = ln->eval(k); |
// printf("\t0x%08x -> %08x\n", i->m_pc+k, |
// buf[0]); |
fwrite(buf, sizeof(ZIPI), 1, m_fp); |
} |
} else { |
fprintf(stderr, "PC = 0x%08x isn\'t ready yet\n", i->m_pc); |
i->m_ln->dump(stderr); |
all_reduced = false; |
} |
} m_pc = tmp; |
return all_reduced; |
} |
|
bool fitsin(const int v, const int b) { |
if (v>0) |
return (v < (1<<(b-1))); |
else |
return (-v <= (1<<b)); |
} |
|
#define BLD_DUALOP(OP) \ |
if (m_opa == zp.ZIP_Rnone) \ |
yyerror("Err: Dual Ops need a result register"); \ |
if (m_opb != zp.ZIP_Rnone) { \ |
if(!fitsin(imm, 16)) \ |
yyerror("16-bit: Immediate out of range"); \ |
in = zp.OP(m_cond,imm,m_opb,m_opa); \ |
} else { \ |
if(!fitsin(imm, 20)) \ |
yyerror("20-bit: Immediate out of range"); \ |
in = zp.OP(m_cond,imm,m_opa); \ |
} |
|
#define BLD_BRANCH(OP,CND) \ |
if (fitsin(offset, 16)) \ |
in = zp.OP(offset); \ |
else if (fitsin(offset, 20)) \ |
in = zp.op_add(zp.CND, offset, zp.ZIP_PC); \ |
else { in = zp.OP(offset); yyerror("LONG JUMP NOT SUPPORTED"); } |
|
ASMLINE *TLINE::eval(void) { |
ZIPI in; |
ZPARSER zp; |
int offset = 0, imm = 0; |
|
if (m_opcode != OP_MOV) { |
if ( ((m_opa!=zp.ZIP_Rnone)&&(m_opa > zp.ZIP_PC)) |
|| ((m_opb!=zp.ZIP_Rnone)&&(m_opb > zp.ZIP_PC)) ) |
yyerror("Only move instructions can reference user regs"); |
} |
|
// Offset used in jumps |
if (m_imm) { |
imm = m_imm->eval(); |
offset = imm-objcode.pc()-1; |
|
if (m_opb == zp.ZIP_PC) |
imm = offset; |
} |
|
switch(m_opcode) { |
case OP_CMP: |
BLD_DUALOP(op_cmp) |
break; |
case OP_TST: |
BLD_DUALOP(op_tst) |
break; |
case OP_MOV: |
if ((m_opa == zp.ZIP_Rnone)||(m_opb == zp.ZIP_Rnone)) { |
yyerror("Moves can only occurr between registers"); |
fprintf(stderr, "m_opa = %d, m_opb = %d\n", m_opa, m_opb); |
fprintf(stderr, "m_imm = %d\n", imm); |
} else if (!fitsin(imm, 16)) |
yyerror("Immediate overflow on move"); |
in = zp.op_mov(m_cond, imm, m_opb, m_opa); |
break; |
case OP_LDIHI: |
if ((imm & (-1<<16))!=0) |
yyerror("16-bit Immediate out of range"); |
if (m_opb != zp.ZIP_Rnone) |
yyerror("LDIHI cannot accept OP-B registers"); |
if (m_opa == zp.ZIP_Rnone) |
yyerror("LDIHI needs a register result"); |
in = zp.op_ldihi(m_cond, imm, m_opa); |
break; |
case OP_LDILO: |
if ((imm & (-1<<16))!=0) |
yyerror("16-bit Immediate out of range"); |
if (m_opb != zp.ZIP_Rnone) |
yyerror("LDIHI cannot accept OP-B registers"); |
if (m_opa == zp.ZIP_Rnone) |
yyerror("LDIHI needs a register result"); |
if ((imm & (-1<<16))!=0) |
yyerror("16-bit Immediate out of range"); |
in = zp.op_ldilo(m_cond, imm, m_opa); |
break; |
case OP_MPY: |
in = zp.op_mpy(m_cond, imm, m_opb, m_opa); |
break; |
case OP_ROL: |
if (m_opa == zp.ZIP_Rnone) |
yyerror("ROL needs a register result"); |
if (m_opb != zp.ZIP_Rnone) |
in = zp.op_rol(m_cond, imm, m_opb, m_opa); |
else |
in = zp.op_rol(m_cond, imm, m_opa); |
break; |
case OP_SUB: |
BLD_DUALOP(op_sub) |
break; |
case OP_AND: |
BLD_DUALOP(op_and) |
break; |
case OP_ADD: |
BLD_DUALOP(op_add) |
break; |
case OP_OR: |
BLD_DUALOP(op_or) |
break; |
case OP_XOR: |
BLD_DUALOP(op_xor) |
break; |
case OP_LSL: |
BLD_DUALOP(op_lsl) |
break; |
case OP_ASR: |
BLD_DUALOP(op_asr) |
break; |
case OP_LSR: |
BLD_DUALOP(op_lsr) |
break; |
case OP_LOD: |
if (m_opb != zp.ZIP_Rnone) |
in = zp.op_lod(m_cond, imm, m_opb, m_opa); |
else |
in = zp.op_lod(m_cond, imm, m_opa); |
break; |
case OP_STO: |
if (m_opb != zp.ZIP_Rnone) |
in = zp.op_sto(m_cond, m_opa, imm, m_opb); |
else |
in = zp.op_sto(m_cond, m_opa, imm); |
break; |
case OP_LDI: |
if ((!fitsin(imm, 24))||(m_cond != zp.ZIPC_ALWAYS)) { |
if (m_opa == zp.ZIP_PC) |
yyerror("Cannot LDI 32-bit addresses into PC register!"); |
LLINE *lln = new LLINE; |
lln->addline(new ILINE(zp.op_ldihi(m_cond, (imm>>16)&0x0ffff, m_opa))); |
lln->addline(new ILINE(zp.op_ldilo(m_cond, imm&0x0ffff, m_opa))); |
return lln; |
} else |
in = zp.op_ldi(imm, m_opa); |
break; |
case OP_CLRF: |
in = zp.op_clrf(m_cond, m_opb); |
break; |
case OP_NOT: |
in = zp.op_not(m_cond, m_opb); |
break; |
case OP_JMP: |
if (!fitsin(imm, 16)) |
yyerror("JMP: Immediate out of range"); |
else if (m_opb == zp.ZIP_Rnone) { |
if (m_cond != zp.ZIPC_ALWAYS) |
yyerror("JMP: Conditions are not allowed for absolute jumps."); |
imm &= (1<<24)-1; |
if (!fitsin(imm, 24)) |
yyerror("JMP: Absolute jump address out of range"); |
zp.op_ldi(imm, zp.ZIP_PC); |
} |
in = zp.op_mov(m_cond, imm, m_opb, zp.ZIP_PC); |
case OP_BRA: |
BLD_BRANCH(op_bra,ZIPC_ALWAYS) |
break; |
case OP_BZ: |
BLD_BRANCH(op_brz,ZIPC_Z) |
break; |
case OP_BNZ: |
BLD_BRANCH(op_bnz,ZIPC_NZ) |
break; |
case OP_BGE: |
BLD_BRANCH(op_bge,ZIPC_GE) |
break; |
case OP_BGT: |
BLD_BRANCH(op_bgt,ZIPC_GT) |
break; |
case OP_BLT: |
BLD_BRANCH(op_blt,ZIPC_LT) |
break; |
case OP_BRC: |
BLD_BRANCH(op_brc,ZIPC_C) |
break; |
case OP_BRV: |
BLD_BRANCH(op_brv,ZIPC_V) |
break; |
case OP_CLR: |
in = zp.op_clr(m_opb); |
break; |
case OP_TRAP: |
if((m_opb == zp.ZIP_Rnone)&&(m_cond == zp.ZIPC_ALWAYS)) |
in = zp.op_ldi(imm, zp.ZIP_CC); |
else if((m_opb == zp.ZIP_Rnone)&&((imm&0x0ffff)==imm)) |
in = zp.op_ldilo(imm, zp.ZIP_CC); |
else if((m_opb != zp.ZIP_Rnone)&&(fitsin(imm, 16))) |
in = zp.op_mov(m_cond, imm, m_opb, zp.ZIP_CC); |
else { |
yyerror("Illegal trap!"); |
in = zp.op_trap(m_cond, 0); |
} |
break; |
case OP_HALT: in = zp.op_halt(m_cond); break; |
case OP_RTU: in = zp.op_rtu(m_cond); break; |
case OP_BUSY: in = zp.op_busy(m_cond); break; |
case OP_BREAK: in = zp.op_break(); break; |
case OP_NOOP: in = zp.op_noop(); break; |
// OP_LJMP: |
case OP_NONE: |
default: { char ebuf[256]; sprintf(ebuf, "Unrecognized OP-Code, %d, NONE = %d, CLR=%d", m_opcode, OP_NONE, OP_CLR); |
yyerror(ebuf); |
in = zp.op_noop(); break; |
} |
} return new ILINE(in); |
} |
|
int TLINE::nlines(void) { |
if ((m_opcode == OP_LDI)&&( (!(m_imm->isdefined())) |
|| (m_cond != ZPARSER::ZIPC_ALWAYS) |
|| (!fitsin(m_imm->eval(), 24)) )) { |
return 2; |
} |
return 1; |
} |
|
unsigned int TLINE::eval(const int lno) { |
if (!isdefined()) |
return DEFAULT_LINE; |
else { |
ASMLINE *ln = this->eval(); |
unsigned int val = ln->eval(lno); |
delete ln; |
return val; |
} |
} |
|
void TLINE::dump(FILE *fp) { |
if (m_state == 'V') |
fprintf(fp, "Void\n"); |
else if (m_state != 'T') |
fprintf(fp, "TLINE state != T (== %c)\n", m_state); |
else { |
fprintf(fp, "TLINE\n"); |
switch(m_opcode) { |
case OP_CMP: fprintf(fp, "\tTLINE OP = CMP\n"); |
break; |
case OP_TST: fprintf(fp, "\tTLINE OP = TST\n"); |
break; |
case OP_MOV: fprintf(fp, "\tTLINE OP = MOV\n"); |
break; |
case OP_LDIHI: fprintf(fp, "\tTLINE OP = LDIHI\n"); |
break; |
case OP_LDILO: fprintf(fp, "\tTLINE OP = LDILO\n"); |
break; |
case OP_MPY: fprintf(fp, "\tTLINE OP = MPY\n"); |
break; |
case OP_ROL: fprintf(fp, "\tTLINE OP = ROL\n"); |
break; |
case OP_SUB: fprintf(fp, "\tTLINE OP = SUB\n"); |
break; |
case OP_AND: fprintf(fp, "\tTLINE OP = AND\n"); |
break; |
case OP_ADD: fprintf(fp, "\tTLINE OP = ADD\n"); |
break; |
case OP_OR: fprintf(fp, "\tTLINE OP = OR\n"); |
break; |
case OP_XOR: fprintf(fp, "\tTLINE OP = XOR\n"); |
break; |
case OP_LSL: fprintf(fp, "\tTLINE OP = LSL\n"); |
break; |
case OP_ASR: fprintf(fp, "\tTLINE OP = ASR\n"); |
break; |
case OP_LSR: fprintf(fp, "\tTLINE OP = LSR\n"); |
break; |
case OP_LOD: fprintf(fp, "\tTLINE OP = LOD\n"); |
break; |
case OP_STO: fprintf(fp, "\tTLINE OP = STO\n"); |
break; |
case OP_LDI: fprintf(fp, "\tTLINE OP = LDI\n"); |
break; |
case OP_CLRF: fprintf(fp, "\tTLINE OP = CLRF\n"); |
break; |
case OP_NOT: fprintf(fp, "\tTLINE OP = NOT\n"); |
break; |
case OP_JMP: fprintf(fp, "\tTLINE OP = JMP\n"); |
break; |
case OP_BRA: fprintf(fp, "\tTLINE OP = BRA\n"); |
break; |
case OP_BZ: |
case OP_BNZ: |
case OP_BGE: |
case OP_BGT: |
case OP_BLT: |
case OP_BRC: |
case OP_BRV: |
fprintf(fp, "\tTLINE OP = BRA.C\n"); |
break; |
case OP_CLR: fprintf(fp, "\tTLINE OP = CLR\n"); |
break; |
case OP_TRAP: fprintf(fp, "\tTLINE OP = TRAP\n"); |
break; |
case OP_HALT: fprintf(fp, "\tTLINE OP = HALT\n"); |
break; |
case OP_RTU: fprintf(fp, "\tTLINE OP = RTU\n"); |
break; |
case OP_BUSY: fprintf(fp, "\tTLINE OP = BUSY\n"); |
break; |
case OP_BREAK: fprintf(fp, "\tTLINE OP = BREAK\n"); |
break; |
case OP_NOOP: fprintf(fp, "\tTLINE OP = NOOP\n"); |
break; |
// OP_LJMP: |
case OP_NONE: |
default: |
fprintf(fp, "\tTLINE OP = (Unrecognized, %d)\n", m_opcode); |
break; |
} |
fprintf(fp, "\tTLINE COND = %d\n", m_cond); |
if (m_imm == NULL) |
fprintf(fp, "\tTLINE imm = (NULL)\n"); |
else if (!m_imm->isdefined()) { |
m_imm->reduce(); |
fprintf(fp, "\tTLINE imm = "); |
m_imm->dump(fp); |
fprintf(fp, "\n"); |
} else |
fprintf(fp, "\tTLINE imm = %d\n", m_imm->eval()); |
fprintf(fp, "\tTLINE opb = %d\n", m_opb); |
fprintf(fp, "\tTLINE opa = %d\n", m_opa); |
} |
} |
|
|
// Now, for our symbol table |
class SYMTABLE_ENTRY { |
private: |
int m_recursion_check; |
|
std::string &trim(std::string &s) { |
std::string::iterator ptr = s.end()-1; |
|
while((ptr >= s.begin())&&(isspace(*ptr))) |
*ptr-- = '\0'; |
if (*ptr == ':') |
*ptr-- = '\0'; |
|
// printf("STORING: %s\n", s.c_str()); |
|
return s; |
} |
|
public: |
std::string m_name; |
AST *m_value; |
SYMTABLE_ENTRY(const char *str) : m_recursion_check(0), m_name(str), m_value(NULL) { |
trim(m_name); |
} SYMTABLE_ENTRY(const char *str, AST *v) : m_recursion_check(0), m_name(str), m_value(v) { |
trim(m_name); |
} ~SYMTABLE_ENTRY(void) { |
delete m_value; |
} |
|
SYMTABLE_ENTRY &operator=(AST *new_value) { |
if (m_value) |
delete m_value; |
m_value = new_value; |
} |
|
bool isdefined(void) { |
if (m_recursion_check > 0) { |
fprintf(stderr, "RECURSION DETECTED! Symbol: %s\n", |
m_name.c_str()); |
return false; |
} |
m_recursion_check = 1; |
if (m_value->m_node_type != 'N') |
m_value->reduce(); |
bool answer = m_value->isdefined(); |
m_recursion_check = 0; |
return answer; |
} |
int val(void) { |
if ((m_value->isdefined())&&(m_value->m_node_type != 'N')) { |
int v = m_value->eval(); |
AST *tmp; |
tmp = m_value; |
m_value = new AST_NUMBER(v); |
delete tmp; |
} return (m_value->eval()); |
} |
void dump(FILE *fp) { m_value->dump(fp); } |
}; |
|
class SYMBOL_TABLE { |
private: |
typedef SYMTABLE_ENTRY *TBLV; |
typedef std::list<TBLV> TBLT; |
|
TBLT m_tbl; |
|
TBLT::iterator lookup(const char *str) { |
TBLT::iterator i = m_tbl.begin(); |
for(; (i!=m_tbl.end())&&(strcmp(str, (*i)->m_name.c_str())>0); |
i++) |
; |
if ((i != m_tbl.end())&&(strcmp(str,(*i)->m_name.c_str())==0)) |
return i; |
return m_tbl.end(); |
} |
|
public: |
SYMBOL_TABLE(void) {} |
~SYMBOL_TABLE(void) { |
TBLT::iterator i = m_tbl.begin(); |
while(i != m_tbl.end()) { |
delete (*i); |
m_tbl.erase(i); |
} |
} |
|
void define(const char *key, AST *value) { |
SYMTABLE_ENTRY *v = new SYMTABLE_ENTRY(key, value); |
TBLT::iterator i = m_tbl.begin(); |
for(; (i!=m_tbl.end())&&(strcmp(key, (*i)->m_name.c_str())>0); |
i++) |
; |
m_tbl.insert(i, v); |
|
/* |
fprintf(stderr, "Defining: %s = ", key); |
value->dump(stderr); |
fprintf(stderr, "\n"); |
*/ |
} |
|
bool isdefined(const char *key) { |
TBLT::iterator i = lookup(key); |
if (i == m_tbl.end()) { |
// fprintf(stderr, "%s is not in the symbol table\n", key); |
return false; |
} else { |
bool defined = (*i)->isdefined(); |
/* |
if (!defined) { |
fprintf(stderr, "KEY: %s = ", key); |
(*i)->dump(stderr); |
fprintf(stderr, " is not yet defined\n"); |
} */ |
return (*i)->isdefined(); |
} |
} |
|
int value(const char *key) { |
TBLT::iterator i = lookup(key); |
if (i == m_tbl.end()) |
return 0; |
else |
return (*i)->val(); |
} |
}; |
|
SYMBOL_TABLE *global_context = NULL, *file_context = NULL; |
|
bool stb_isdefined(const char *key) { |
if ((file_context)&&(file_context->isdefined(key))) |
return true; |
else |
return global_context->isdefined(key); |
} int stb_value(const char *key) { |
if (file_context->isdefined(key)) |
return file_context->value(key); |
else |
return global_context->value(key); |
} void stb_define(const char *key, AST *value) { |
file_context->define(key, value); |
} void gbl_define(const char *key, AST *value) { |
global_context->define(key, value); |
} |
|
void create_new_context(void) { |
if (global_context == NULL) |
global_context = new SYMBOL_TABLE; |
if (file_context != NULL) |
delete file_context; |
file_context = new SYMBOL_TABLE; |
} |
|
|
// Convenience functions for accessing the symbol table |
bool stb_isdefined(const std::string &key) { |
bool answer = stb_isdefined(key.c_str()); |
return answer; |
} int stb_value(const std::string &key) { |
return stb_value(key.c_str()); |
} void stb_define(const std::string &key, AST *value) { |
stb_define(key.c_str(), value); |
} void gbl_define(const std::string &key, AST *value) { |
gbl_define(key.c_str(), value); |
} |
|
|
/zasm/optest.cpp
4,7 → 4,10
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU core |
// |
// Purpose: A quick test of whether we can decode opcodes properly. |
// Purpose: A quick test of whether we can decode opcodes properly. This |
// test bypasses the assembler, and is useful when the assembler |
// isn't working. Now that we've got the assembler running, this |
// code isn't nearly as useful anymore. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Tecnology, LLC |
/zasm/asmdata.h
0,0 → 1,283
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: asmdata.h |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU core |
// |
// Purpose: Data structures for the assembler. In particular, this file |
// declares: Abstract Syntax Trees (ASTs) to store operations for |
// later calculation, ASMLINEs or assembled lines (which may or |
// may not be defined, depending upon symbol table status), |
// symbol table access and the final output object file together |
// with its necessary relocations. Yes, linking is done, but as |
// part of the assembler and not part of a separate linker. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Tecnology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
// your option) any later version. |
// |
// This program is distributed in the hope that it will be useful, but WITHOUT |
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
#ifndef ASMDATA_H |
#define ASMDATA_H |
|
#include <stdio.h> |
#include <string> |
#include <list> |
#include "zopcodes.h" |
#include "zparser.h" |
|
extern "C" char *linecp; |
|
typedef enum { |
// TST OPCND |
// Dual operand instructions that take conditions |
OP_CMP, OP_TST, OP_MOV, OP_LDIHI, OP_LDILO, OP_MPY, OP_ROL, |
OP_SUB, OP_AND, OP_ADD, OP_OR, OP_XOR, |
OP_LSL, OP_ASR, OP_LSR, |
// Memory operands/operators |
OP_LOD, OP_STO, |
// Dual operand instructions that do not take conditions |
OP_LDI, |
// Single operand instructions that can take conditions |
OP_CLRF, OP_JMP, OP_LJMP, OP_NOT, |
// Branch operands |
OP_BRA, OP_BZ, OP_BNZ, OP_BGE, OP_BGT, OP_BLT, OP_BRC, OP_BRV, |
// Single operand instructions that have no explicit conditions |
OP_CLR, OP_TRAP, |
// BAREOPs that can have conditions |
OP_HALT, OP_RTU, OP_BUSY, |
// BAREOPs without conditions |
OP_BREAK, OP_NOOP, |
// Error condition--undefined operand |
OP_NONE |
} LEXOPCODE; |
|
#define DEFAULT_LINE 0x4e000000 |
|
class ASMLINE { |
public: |
char m_state; |
virtual bool isdefined(void) { return true; }; |
virtual ~ASMLINE(void) {}; |
virtual int nlines(void) { return 0; } |
virtual unsigned int eval(const int lno) { return DEFAULT_LINE; } |
virtual void dump(FILE *fp) = 0; |
}; |
|
class AST { // The actual expression tree |
public: |
// node type is one of: |
// 'B' a branch with two sides |
// 'N' a number |
// 'I' an identifier |
// 'A' ?? an address ?? would we need this? |
char m_node_type; |
virtual bool isdefined(void) = 0; |
virtual int eval(void) = 0; |
virtual void reduce(void) = 0; |
virtual void dump(FILE *fp) = 0; |
}; |
|
/* |
class ULINE : public ASMLINE { // Unprocessed line of assembly |
public: |
std::string m_file, m_text; |
int m_lineno; |
ULINE(const int line, const std::string file, |
const std::string text) : m_file(file), m_text(text), |
m_lineno(line) { m_state = 'U'; } |
class TLINE *eval(void); |
int nlines(void) { return 0; }; |
virtual bool isdefined(void) { return false; }; |
}; |
*/ |
|
class ILINE : public ASMLINE { // Instruction line |
public: |
ZIPI m_in; |
ILINE(const ZIPI in) : m_in(in) { m_state = 'I'; }; |
virtual bool isdefined(void) { return true; }; |
virtual int nlines(void) { return 1; } |
virtual unsigned int eval(const int lno); |
void dump(FILE *fp) { fprintf(fp, "IN: %08x\n", m_in); } |
}; |
|
class VLINE : public ASMLINE { // Void line |
public: |
VLINE(void) { m_state = 'V'; }; |
virtual bool isdefined(void) { return true; }; |
virtual int nlines(void) { return 0; } |
virtual unsigned int eval(const int lno); |
void dump(FILE *fp) { fprintf(fp, "void\n"); } |
}; |
|
class DLINE : public ASMLINE { // Data line |
public: |
ZIPI m_data; |
DLINE(const ZIPI dat) : m_data(dat) { m_state = 'D'; }; |
virtual bool isdefined(void) { return true; }; |
virtual int nlines(void) { return 1; } |
virtual unsigned int eval(const int lno); |
void dump(FILE *fp) { fprintf(fp, "\tWORD\t%08d\n", m_data); } |
}; |
|
class LLINE : public ASMLINE { // List line -- one line that has turned into |
// many |
public: |
int m_nlines; |
ASMLINE **m_lines; |
LLINE(void) : m_nlines(0), m_lines(NULL) { m_state = 'L'; }; |
void addline(ASMLINE *line) ; |
virtual bool isdefined(void); |
~LLINE(void); |
virtual int nlines(void) { return m_nlines; } |
virtual unsigned int eval(const int lno); |
void dump(FILE *fp) { |
for(int i=0; i<m_nlines; i++) |
m_lines[i]->dump(fp); |
} |
}; |
|
class TLINE : public ASMLINE { // Expression tree |
public: |
LEXOPCODE m_opcode; |
ZPARSER::ZIPCOND m_cond; |
AST *m_imm; |
ZPARSER::ZIPREG m_opb, m_opa; |
|
TLINE(void) : m_opcode(OP_NONE), m_cond(ZPARSER::ZIPC_ALWAYS), |
m_imm(NULL), m_opa(ZPARSER::ZIP_Rnone), m_opb(ZPARSER::ZIP_Rnone) |
{ m_state = 'T'; }; |
virtual bool isdefined(void) { |
if (m_imm != NULL) { |
bool answer = m_imm->isdefined(); |
return answer; |
} else return true; |
} |
ASMLINE *eval(void); |
~TLINE(void) { if (m_imm) delete m_imm; } |
virtual int nlines(void); // { return 1; } |
virtual unsigned int eval(const int lno); |
void dump(FILE *fp); |
}; |
|
class AST_BRANCH : public AST { // The actual expression tree |
public: |
int m_op; // An operator |
AST *m_left, *m_right; |
|
AST_BRANCH(int op, AST *l, AST *r) |
: m_op(op), m_left(l), m_right(r) { m_node_type = 'B'; |
} |
|
bool isdefined(void) { |
return ((m_left)&&(m_right) |
&&(m_left->isdefined()) |
&&(m_right->isdefined())); |
} |
~AST_BRANCH(void) { delete m_left; delete m_right; } |
int eval(void); |
void reduce(void); |
void dump(FILE *fp) { |
fprintf(fp, "("); |
m_left->dump(fp); |
fprintf(fp, ") %c (", m_node_type); |
m_right->dump(fp); |
fprintf(fp, ")"); |
} |
}; |
|
class AST_NUMBER : public AST { // A number at the base of the tree |
public: |
int m_val; |
AST_NUMBER(int val) : m_val(val) { m_node_type = 'N'; } |
bool isdefined(void) { return true; } |
int eval(void); |
void reduce(void); |
void dump(FILE *fp) { fprintf(fp, "%d", m_val); } |
}; |
|
class AST_IDENTIFIER : public AST { // An identifier within the expression tree |
public: |
std::string m_id; |
AST_IDENTIFIER(const char *id) : m_id(id) { m_node_type = 'I'; } |
AST_IDENTIFIER(AST *ida, const char *idb); |
bool isdefined(void); |
int eval(void); |
void reduce(void); |
void dump(FILE *fp) { fprintf(fp, "%s", m_id.c_str()); } |
}; |
|
class AST_LABEL : public AST { // An address label within the expression tree |
// This is special, because it cannot be evaluated without knowing |
// the PC value at this address |
public: |
std::string m_label; |
AST_LABEL(const char *id) : m_label(id) { m_node_type = 'L'; } |
bool isdefined(void); |
int eval(void); |
void reduce(void); |
void dump(FILE *fp) { fprintf(fp, "%s", m_label.c_str()); } |
}; |
|
class SAVED_ASMLINE { |
public: |
unsigned int m_pc; |
ASMLINE *m_ln; |
SAVED_ASMLINE(const unsigned int pc, ASMLINE *ln) : m_pc(pc), m_ln(ln) |
{} |
}; |
|
class OBJFILE { |
typedef std::list<SAVED_ASMLINE> SVDTBL; |
|
SVDTBL m_tbl; |
unsigned int m_pc; |
FILE *m_fp; |
|
public: |
OBJFILE(void) { m_fp = NULL; m_pc = 0; } |
void open(const char *fname); |
unsigned int pc(void) { return m_pc; } |
void operator+=(ASMLINE *ln); |
bool reduce(void); |
}; |
|
// The file we are building |
extern OBJFILE objcode; |
|
// Functions for accessing and defining elements in our symbol table |
extern bool stb_isdefined(const char *key); |
extern int stb_value(const char *key); |
extern void stb_define(const char *key, AST *value); |
extern void gbl_define(const char *key, AST *value); |
extern bool stb_isdefined(const std::string &key); // { return stb_isdefined(key.c_str()); } |
extern int stb_value(const std::string &key); // { return stb_value(key.c_str()); } |
extern void stb_define(const std::string &key, AST *value); // { return stb_define(key.c_str(), value); } |
extern void gbl_define(const std::string &key, AST *value); // { return stb_define(key.c_str(), value); } |
extern void create_new_context(void); // Create a new symbol table context |
|
|
#endif // ASMDATA_H |
/zasm/zpp.l
0,0 → 1,309
/******************************************************************************* |
** |
** Filename: zpp.l |
** |
** Project: Zip CPU -- a small, lightweight, RISC CPU core |
** |
** Purpose: The preprocessor for the Zip Assembler. |
** |
** This routine strips comments, handles #define's, #ifdef's, and |
** #include statements in a C fashion. |
** |
** #define macro's are also defined in the language and therefore |
** supposed to be supported, but the support isn't there yet. |
** |
** Creator: Dan Gisselquist, Ph.D. |
** Gisselquist Tecnology, LLC |
** |
******************************************************************************** |
** |
** Copyright (C) 2015, Gisselquist Technology, LLC |
** |
** This program is free software (firmware): you can redistribute it and/or |
** modify it under the terms of the GNU General Public License as published |
** by the Free Software Foundation, either version 3 of the License, or (at |
** your option) any later version. |
** |
** This program is distributed in the hope that it will be useful, but WITHOUT |
** ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
** for more details. |
** |
** You should have received a copy of the GNU General Public License along |
** with this program. (It's in the $(ROOT)/doc directory, run make with no |
** target there if the PDF file isn't present.) If not, see |
** <http://www.gnu.org/licenses/> for a copy. |
** |
** License: GPL, v3, as defined and found on www.gnu.org, |
** http://www.gnu.org/licenses/gpl.html |
** |
** |
*******************************************************************************/ |
|
%{ |
// #include <FlexLexer.h> |
#include <string> |
#include <ctype.h> |
#include <stdio.h> |
#include <list> |
|
using namespace std; |
|
extern "C" int yylex(); |
// #include "zprepr.tab.h" |
int ndef = 0; |
void stb_define(const char *str); |
void stb_args(const char *str); |
void stb_macro(const char *value); |
void stb_addmacro(const char *value); |
bool stb_isdefined(const char *str); |
const char *stb_getdefn(const char *str); |
%} |
%x DEF DFA DFV INDEF IFDEFV INNOTDEF NODEF NVRDEF COMMENT |
%x INDEF_EOL INNOTDEF_EOL |
%option noyywrap |
%option stack |
ID [_:A-Za-z][_:A-Za-z0-9]* |
|
%% |
<COMMENT>"*/" { yy_pop_state(); } |
<COMMENT>[^*]+ { /* Ignore comments */ } |
<COMMENT>"*"+[^/] { /* Ignore comments */ } |
<INITIAL,INDEF>^"#include"[ \t]+\"[^\"]+\" { |
char *ptr = &yytext[9], *start, *end, *str; |
while(isspace(*ptr)) |
ptr++; |
start = ptr; |
ptr++; |
while((*ptr)&&(*ptr != '\"')) |
ptr++; |
*ptr = '\0'; |
yypush_buffer_state(yy_create_buffer( |
fopen(start, "r"), |
YY_BUF_SIZE)); |
// push_file_state(yylineno); // and filename ... |
fprintf(yyout, "#line 0 \"%s\"\n", start); |
} |
<INITIAL,INDEF>^"#define"[ \t]+ { yy_push_state(DEF); } |
/* <*>^"#line"[ \t]+(0-9)+[ \t]+["][^"]*["][ \t]*\n { } */ |
<DEF>[_A-Za-z][_:A-Za-z0-9]*/[^(] { |
stb_define(yytext); |
BEGIN DFV; |
} |
<DEF>{ID}/[(] { fprintf(stderr, "DEF::Found MACRO\n"); |
stb_define(yytext); |
BEGIN DFA; } |
<DFA>([^)]+) { |
/* Process arguments */ |
stb_args(yytext); |
BEGIN DFV; |
} |
<DFV>[ \t]+ { /* Ignore initial spaces */ } |
<DFV>[.]*$ {/* Parse to end of line, get value for our define */ |
// printf("End of define, value = \'%s\'\n", yytext); |
stb_macro(yytext); |
yy_pop_state(); |
} |
<INITIAL,INDEF>^[ \t]+.[dD][aA][tT][aA]? { fprintf(yyout, "\tWORD"); } |
<INITIAL,INDEF>^"#defcont"[ \t]+ { yy_push_state(DFV); } |
<INITIAL,INDEF>^"#ifdef"[ \t]* { ndef = 0; yy_push_state(IFDEFV); } |
<INITIAL,INDEF>^"#ifndef"[ \t]* { ndef = 1; yy_push_state(IFDEFV); } |
<IFDEFV>{ID} { |
bool known = stb_isdefined(yytext); |
if ( ((known)&&(ndef==0)) || ((!known)&&(ndef!=0)) ) { |
BEGIN INDEF_EOL; |
} else { |
BEGIN INNOTDEF_EOL; |
} |
} |
/* Not yet: <INITIAL,INDEF>^"#if"[ \t]* { yy_push_state(IFDEFE); } |
/* Not yet: <INITIAL,INDEF>^"#if"[ \t]* { yy_push_state(IFDEFE); } |
/* Not yet: <IFDEFE><expr> { yy_push_state(IFDEFE); } */ |
/* <INNOTDEF>^"#elsif"[ \t]* { yy_pop_state(); yy_push_state(IFDEFE); } */ |
<INDEF_EOL>[ \t]*$ { BEGIN INDEF; } |
<INDEF_EOL>(;|"//").*$ { BEGIN INDEF; } |
<INNOTDEF_EOL>[ \t]*$ { BEGIN INNOTDEF; } |
<INNOTDEF_EOL>(;|"//").*$ { BEGIN INNOTDEF; } |
<INDEF_EOL>[^ \t\n].*$ { BEGIN INDEF; fprintf(stderr, "WARNING! Unexpected characters on IFDEF line, \'%s\'\n", yytext); } |
<INNOTDEF_EOL>[^ \t\n].*$ { BEGIN INNOTDEF; fprintf(stderr, "WARNING! Unexpected characters on IFNDEF line, %s\n", yytext); } |
<INDEF,NVRDEF>^"#else"[ \t]*((;|"//").*)?$ { BEGIN NODEF; } |
<INNOTDEF>^"#else"[ \t]*((;|"//").*)?$ { BEGIN INDEF; } |
<INNOTDEF>(.*) { } |
<INDEF>^"#elsif"[ \t]* { BEGIN NVRDEF; } |
<NODEF,INDEF,INNOTDEF>^"#endif"[ \t]*((;|"//").*)?$ { yy_pop_state(); } |
<NODEF,INDEF,INNOTDEF>^"#endif"[ \t]*"/*" { BEGIN COMMENT; } |
<*>^"#endif"[ \t]* { fprintf(stderr, "ERR: Unknown endif!!\n");} |
/* Not yet: ^"#struct"[ \t]* {} */ |
/* Not yet: ^"#endstruct"[ \t]* {} */ |
/* Not yet: ^"#seg"[ \t]* {} */ |
<NODEF,NVRDEF>.* { /* Ignore everything in these states*/ } |
<INITIAL,INDEF>{ID}/[^(] { |
if (stb_isdefined(yytext)) |
fprintf(yyout, "%s", stb_getdefn(yytext)); |
else |
fprintf(yyout, "%s", yytext); |
} |
<*>^[ \t]*"//".*$ { /* Ignore comment only lines */ } |
<*>^[ \t]*";".*$ { /* Ignore comment only lines */ } |
<*>"//".* { /* Ignore trailing comments */ } |
<*>";".* { /* Ignore trailing comments */ } |
<*>"/*" { yy_push_state(COMMENT); } |
<*>[ \t]+ { ECHO; } |
<INITIAL,INDEF>[.]* { ECHO; } |
<*>\n { ECHO; } |
/* <*>. { printf("Unmatched \'%c\'\n", yytext[0]); } */ |
/* <<EOF>> { printf("EOF!\n"); } */ |
|
%% |
|
class SYMTABLE_ENTRY { |
private: |
std::string &trim(std::string &s) { |
std::string::iterator ptr = s.end(); |
|
while((ptr >= s.begin())&&(isspace(*ptr))) |
*ptr-- = '\0'; |
|
return s; |
} |
|
public: |
std::string m_name, m_value, m_args; |
SYMTABLE_ENTRY(const char *str) : m_name(str) { |
trim(m_name); |
} |
SYMTABLE_ENTRY &operator+=(const char *str) { |
const char *start = str; |
|
while(isspace(*start)) |
start++; |
if (m_value.length()!=0) |
m_value += " "; |
|
std::string trimd(start); |
trim(trimd); |
m_value += trimd; |
|
/* |
printf("ENTRY::SYMBOL \'%s\' NOW = \'%s\'\n", |
m_name.c_str(), m_value.c_str()); |
*/ |
return *this; |
} |
SYMTABLE_ENTRY &setargs(const char *str) { |
m_args += str; |
return *this; |
} |
|
std::string getdefn(void) { |
return m_value; |
} |
}; |
|
class SYMBOL_TABLE { |
private: |
typedef SYMTABLE_ENTRY *TBLV; |
typedef std::list<TBLV> TBLT; |
|
TBLT m_tbl; |
TBLT::iterator lookup(const char *str) { |
TBLT::iterator i = m_tbl.begin(); |
for(; (i!= m_tbl.end())&&(strcmp(str, (*i)->m_name.c_str())>0); i++) |
; |
if ((i != m_tbl.end())&&(strcmp(str, (*i)->m_name.c_str())==0)) |
return i; |
return m_tbl.end(); |
} |
|
public: |
SYMBOL_TABLE(void) {} |
|
void define(const char *str) { |
SYMTABLE_ENTRY *v = new SYMTABLE_ENTRY(str); |
TBLT::iterator i = m_tbl.begin(); |
for(; (i!= m_tbl.end())&&(strcmp(str, (*i)->m_name.c_str())>0); i++) |
; |
m_tbl.insert(i, v); |
|
// printf("SYMS::Defining SYMBOL: \'%s\'\n", str); |
} |
|
bool defined(const char *str) { |
TBLT::iterator i = lookup(str); |
if (i==m_tbl.end()) |
return false; |
else |
return true; |
} |
|
|
void undefine(const char *str) { |
TBLT::iterator i = lookup(str); |
if (i == m_tbl.end()) |
return; |
TBLV v = (*i); |
m_tbl.erase(i); |
delete v; |
} |
|
void addmacro(const char *name, const char *str) { |
TBLT::iterator i = lookup(name); |
if (i == m_tbl.end()) { |
fprintf(stderr, "INTERNAL ERR, %s NOT DEFINED!\n", name); |
} *(*i) += str; |
} |
|
void addargs(const char *name, const char *str) { |
TBLT::iterator i = lookup(name); |
if (i == m_tbl.end()) { |
fprintf(stderr, "INTERNAL ERR, %s NOT DEFINED!\n", name); |
} (*i)->setargs(str); |
} |
const char *getdefn(const char *name) { |
TBLT::iterator i = lookup(name); |
if (i == m_tbl.end()) { |
fprintf(stderr, "INTERNAL ERR, %s NOT DEFINED!\n", name); |
return NULL; |
} (*i)->getdefn().c_str(); |
} |
|
}; |
|
SYMTABLE_ENTRY *last = NULL; |
SYMBOL_TABLE syms; |
std::string last_define; |
|
void stb_define(const char *str) { |
if (syms.defined(str)) { |
fprintf(stderr, "WARNING! Symbol \'%s\' is already defined!\n", str); |
syms.undefine(str); |
} |
|
syms.define(str); |
last_define = str; |
} |
|
void stb_args(const char *args) { |
syms.addargs(last_define.c_str(), args); |
} |
|
void stb_macro(const char *value) { |
syms.addmacro(last_define.c_str(), value); |
} |
|
void stb_addmacro(const char *value) { |
syms.addmacro(last_define.c_str(),value); |
} |
|
bool stb_isdefined(const char *str) { |
return syms.defined(str); |
} |
|
const char *stb_getdefn(const char *str) { |
return syms.getdefn(str); |
} |
|
int main(int argc, char **argv) { |
yylex(); |
} |
|
/zasm/zasm.l
0,0 → 1,167
/******************************************************************************* |
** |
** Filename: zasm.l |
** |
** Project: Zip CPU -- a small, lightweight, RISC CPU core |
** |
** Purpose: The lexical analyzer for the assembler. This converts assembler |
** input into tokens, to feed the parser. |
** |
** |
** Creator: Dan Gisselquist, Ph.D. |
** Gisselquist Tecnology, LLC |
** |
******************************************************************************** |
** |
** Copyright (C) 2015, Gisselquist Technology, LLC |
** |
** This program is free software (firmware): you can redistribute it and/or |
** modify it under the terms of the GNU General Public License as published |
** by the Free Software Foundation, either version 3 of the License, or (at |
** your option) any later version. |
** |
** This program is distributed in the hope that it will be useful, but WITHOUT |
** ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
** for more details. |
** |
** You should have received a copy of the GNU General Public License along |
** with this program. (It's in the $(ROOT)/doc directory, run make with no |
** target there if the PDF file isn't present.) If not, see |
** <http://www.gnu.org/licenses/> for a copy. |
** |
** License: GPL, v3, as defined and found on www.gnu.org, |
** http://www.gnu.org/licenses/gpl.html |
** |
** |
*******************************************************************************/ |
|
%{ |
#include <stdio.h> |
#include "asmdata.h" |
#include "zasm.tab.h" |
|
extern "C" int yylex(); |
extern "C" int yywrap() { return 1;} |
extern char *master_input_filename; |
%} |
|
%option yylineno |
%option warn |
|
%% |
|
^"#line"[ \t]+[0-9]+[ \t]+["][^"]*["][ \t]*$ { |
yylineno = atoi(&yytext[6]); |
char *bg, *en; |
bg = strchr(yytext, '\"')+1; |
en = strchr(bg, '\"'); *en = '\0'; |
if ((master_input_filename == NULL)||(strcmp(master_input_filename, bg)!=0)) { |
if (!master_input_filename) delete[] master_input_filename; |
master_input_filename = new char[en-bg+2]; |
strcpy(master_input_filename, bg); |
} |
} |
^"#line"[ \t]+[0-9]+[ \t]+$ { yylineno = atoi(&yytext[6]); } |
[uU][rR]1[0-5] { yylval.u_reg=(ZPARSER::ZIPREG)(26+yytext[3]-'0');return REG; } |
[sS][rR]1[0-5] { yylval.u_reg=(ZPARSER::ZIPREG)(10+yytext[3]-'0');return REG; } |
[rR]1[0-5] { yylval.u_reg=(ZPARSER::ZIPREG)(10+yytext[2]-'0');return REG; } |
[uU][rR][0-9] { yylval.u_reg=(ZPARSER::ZIPREG)(16+yytext[2]-'0');return REG; } |
[sS][rR][0-9] { yylval.u_reg=(ZPARSER::ZIPREG)( yytext[2]-'0');return REG; } |
[rR][0-9] { yylval.u_reg=(ZPARSER::ZIPREG)( yytext[1]-'0');return REG; } |
[uU][pP][cC] { yylval.u_reg = ZPARSER::ZIP_uPC; return REG; } |
[sS][pP][cC] { yylval.u_reg = ZPARSER::ZIP_PC; return REG; } |
[pP][cC] { yylval.u_reg = ZPARSER::ZIP_PC; return REG; } |
[uU][cC][cC] { yylval.u_reg = ZPARSER::ZIP_uCC; return REG; } |
[sS][cC][cC] { yylval.u_reg = ZPARSER::ZIP_CC; return REG; } |
[cC][cC] { yylval.u_reg = ZPARSER::ZIP_CC; return REG; } |
[uU][sS][pP] { yylval.u_reg = ZPARSER::ZIP_uSP; return REG; } |
[sS][sS][pP] { yylval.u_reg = ZPARSER::ZIP_SP; return REG; } |
[sS][pP] { yylval.u_reg = ZPARSER::ZIP_SP; return REG; } |
[bB][rR][aA] { yylval.u_op = OP_BRA; return BRANCHOP; } |
[bB][rR][zZ] { yylval.u_op = OP_BZ; return BRANCHOP; } |
[bB][zZ] { yylval.u_op = OP_BZ; return BRANCHOP; } |
[bB][nN][zZ] { yylval.u_op = OP_BNZ; return BRANCHOP; } |
[bB][nN][eE] { yylval.u_op = OP_BNZ; return BRANCHOP; } |
[bB][gG][eE] { yylval.u_op = OP_BGE; return BRANCHOP; } |
[bB][gG][tT] { yylval.u_op = OP_BGT; return BRANCHOP; } |
[bB][gG] { yylval.u_op = OP_BGT; return BRANCHOP; } |
[bB][lL][tT] { yylval.u_op = OP_BLT; return BRANCHOP; } |
[bB][nN] { yylval.u_op = OP_BLT; return BRANCHOP; } |
[bB][rR][cC] { yylval.u_op = OP_BRC; return BRANCHOP; } |
[bB][cC] { yylval.u_op = OP_BRC; return BRANCHOP; } |
[bB][rR][vV] { yylval.u_op = OP_BRV; return BRANCHOP; } |
[bB][vV] { yylval.u_op = OP_BRV; return BRANCHOP; } |
[cC][lL][rR] { yylval.u_op = OP_CLR; return SINGLOP; } |
[cC][lL][rR][fF] {yylval.u_op = OP_CLRF;return SINGLOP; } |
[iI][nN][tT] { yylval.u_op = OP_TRAP;return SINGLOP; } |
[tT][rR][aA][pP] {yylval.u_op = OP_TRAP;return SINGLOP; } |
[jJ][mM][pP] { yylval.u_op = OP_JMP; return SINGLOP; } |
[lL][jJ][mM][pP] {yylval.u_op = OP_LJMP;return SINGLOP; } |
[nN][oO][tT] { yylval.u_op = OP_NOT; return SINGLOP; } |
[cC][mM][pP] { yylval.u_op = OP_CMP; return DUALOP; } |
[tT][sS][tT] { yylval.u_op = OP_TST; return DUALOP; } |
[mM][oO][vV] { yylval.u_op = OP_MOV; return DUALOP; } |
[lL][dD][iI] { yylval.u_op = OP_LDI; return LDIOP; } |
[lL][dD][iI][hH][iI] { yylval.u_op =OP_LDIHI; return LDHLOP; } |
[lL][dD][iI][lL][oO] { yylval.u_op =OP_LDILO; return LDHLOP; } |
[mM][pP][yY] { yylval.u_op = OP_MPY; return DUALOP; } |
[rR][oO][lL] { yylval.u_op = OP_ROL; return DUALOP; } |
[sS][uU][bB] { yylval.u_op = OP_SUB; return DUALOP; } |
[aA][nN][dD] { yylval.u_op = OP_AND; return DUALOP; } |
[aA][dD][dD] { yylval.u_op = OP_ADD; return DUALOP; } |
[oO][rR] { yylval.u_op = OP_OR; return DUALOP;; } |
[xX][oO][rR] { yylval.u_op = OP_XOR; return DUALOP; } |
[lL][sS][lL] { yylval.u_op = OP_LSL; return DUALOP; } |
[aA][sS][rR] { yylval.u_op = OP_ASR; return DUALOP; } |
[lL][sS][rR] { yylval.u_op = OP_LSR; return DUALOP; } |
[lL][oO][dD] { yylval.u_op = OP_LOD; return LOADOP; } |
[sS][tT][oO] { yylval.u_op = OP_STO; return STOROP; } |
[hH][aA][lL][tT] { yylval.u_op = OP_HALT; return BAREOP; } |
[wW][aA][iI][tT] { yylval.u_op = OP_HALT; return BAREOP; } |
[rR][tT][uU] { yylval.u_op = OP_RTU; return BAREOP; } |
[nN][oO][pP] { yylval.u_op = OP_NOOP; return BAREOP; } |
[nN][oO][oO][pP] { yylval.u_op = OP_NOOP; return BAREOP; } |
[bB][rR][eE][aA][kK] { yylval.u_op = OP_BREAK; return BAREOP; } |
[bB][rR][kK] { yylval.u_op = OP_BREAK; return BAREOP; } |
[bB][uU][sS][yY] { yylval.u_op = OP_BUSY; return BAREOP; } |
[eE][qQ][uU] { return EQU; } |
[fF][iI][lL][lL] { return FILL; } |
[wW][oO][rR][dD] { return WORD; } |
"__"[hH][eE][rR][eE]"__" { return HERE; } |
[\.][dD][aA][tT] { return WORD; } |
[\.][zZ]/[ \t\n] { yylval.u_cond = ZPARSER::ZIPC_Z; return COND; } |
[\.][nN][eE]/[ \t\n] { yylval.u_cond = ZPARSER::ZIPC_NZ; return COND; } |
[\.][nN][zZ]/[ \t\n] { yylval.u_cond = ZPARSER::ZIPC_NZ; return COND; } |
[\.][gG][eE]/[ \t\n] { yylval.u_cond = ZPARSER::ZIPC_GE; return COND; } |
[\.][gG][tT]/[ \t\n] { yylval.u_cond = ZPARSER::ZIPC_GT; return COND; } |
[\.][lL][tT]/[ \t\n] { yylval.u_cond = ZPARSER::ZIPC_LT; return COND; } |
[\.][nN]/[ \t\n] { yylval.u_cond = ZPARSER::ZIPC_LT; return COND; } |
[\.][cC]/[ \t\n] { yylval.u_cond = ZPARSER::ZIPC_C; return COND; } |
[\.][vV]/[ \t\n] { yylval.u_cond = ZPARSER::ZIPC_V; return COND; } |
[_A-Za-z][_a-zA-Z0-9]* { yylval.u_id = strdup(yytext); return IDENTIFIER; } |
"$"?[-]?0[xX][0-9A-Fa-f]+ { yylval.u_ival = strtoul(yytext+(((*yytext)=='$')?1:0),NULL,16);return INT;} |
"$"?[-]?0[0-7]+ { yylval.u_ival = strtoul(yytext+(((*yytext)=='$')?1:0),NULL, 8);return INT;} |
"$"?[-]?[1-9][0-9]* { yylval.u_ival = strtoul(yytext+(((*yytext)=='$')?1:0),NULL,10);return INT;} |
"$"?[-]?[1-9A-Fa-f][0-9A-Fa-f]*h {yylval.u_ival=strtoul(yytext+(((*yytext)=='$')?1:0),NULL,16); return INT;} |
"$"?[-]?[0-7]+o { yylval.u_ival = strtoul(yytext+(((*yytext)=='$')?1:0),NULL, 8);return INT;} |
"$"?"0" { yylval.u_ival = 0;return INT;} |
"$" { return DOLLAR; } |
"," { return COMMA; } |
"+" { return PLUS; } |
"-" { return MINUS; } |
"*" { return TIMES; } |
"||" { return BOOLEANOR; } |
"|" { return BITWISEOR; } |
"&&" { return BOOLEANAND; } |
"&" { return BITWISEAND; } |
"^" { return BITWISEXOR; } |
":" { return COLON; } |
"." { return DOT; } |
[ \t]+ { } |
[(] { return '('; } |
[)] { return ')'; } |
\n.* { if (linecp) free(linecp); linecp = strdup(&yytext[1]); yyless(1); return '\n'; } |
|
%% |
|
/zasm/sys.i
0,0 → 1,192
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
; |
; Filename: sys.i |
; |
; Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
; |
; Purpose: This is the beginnings of a system wide header file for the |
; Zip System. It describes and declares the peripherals |
; that will the be used and referenced by the assembly files. |
; |
; Status: As of August, 2015, I have no confidence that the preprocessor |
; can properly include this file. It certainly cannot handle |
; macros (yet). |
; |
; Creator: Dan Gisselquist, Ph.D. |
; Gisselquist Tecnology, LLC |
; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
; |
; Copyright (C) 2015, Gisselquist Technology, LLC |
; |
; This program is free software (firmware): you can redistribute it and/or |
; modify it under the terms of the GNU General Public License as published |
; by the Free Software Foundation, either version 3 of the License, or (at |
; your option) any later version. |
; |
; This program is distributed in the hope that it will be useful, but WITHOUT |
; ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or |
; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
; for more details. |
; |
; License: GPL, v3, as defined and found on www.gnu.org, |
; http://www.gnu.org/licenses/gpl.html |
; |
; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
; |
sys.bus equ 0xc0000000 |
sys.breaken equ 0x080 |
sys.step equ 0x040 |
sys.gie equ 0x020 |
sys.sleep equ 0x010 |
sys.ccv equ 0x008 |
sys.ccn equ 0x004 |
sys.ccc equ 0x002 |
sys.ccz equ 0x001 |
sys.bu.pic equ 0x000 |
sys.bus.wdt equ 0x001 |
sys.bus.cache equ 0x002 |
sys.bus.ctrpic equ 0x003 |
sys.bus.tma equ 0x004 |
|
|
|
; Define the location(s) of our peripherals, |
#define sys.base 0xc0000000 |
#define sys.cache.base 0xc0100000 |
#struct sys |
pic |
wdt |
cache |
ctrpic |
tma |
tmb |
tmc |
jiffies |
mtask |
mstl |
mpstl |
mastl |
utask |
ustl |
upstl |
uastl |
#endstruct |
; and their associated interrupt vectors ... |
#define CACHEINT 0x01 |
#define JIFFYINT 0x02 ; |
#define TMCINT 0x04 ; |
#define TMBINT 0x08 ; |
#define TMAINT 0x10 ; |
#define CTRPICINT 0x20 ; The aux interrupt controller |
; Masks to send to enable those same vectors |
#define CACHEINTEN 0x80010000 |
#define JIFFYINTEN 0x80020000 |
#define TMCINTEN 0x80040000 |
#define TMBINTEN 0x80080000 |
#define TMAINTEN 0x80100000 |
#define CTRPICEN 0x80200000 |
; And similar masks to disable them |
#define CACHEINTDIS 0x00010000 |
#define JIFFYINTDIS 0x00020000 |
#define TMCINTDIS 0x00040000 |
#define TMBINTDIS 0x00080000 |
#define TMAINTDIS 0x00100000 |
#define CTRPICDIS 0x00200000 |
|
; Define our condition code bits |
#define CCZ 0x001 |
#define CCC 0x002 |
#define CCN 0x004 |
#define CCV 0x008 |
#define CCSLEEP 0x010 |
#define CCGIE 0x020 |
#define CCSTEP 0x040 |
#define CCUBRK 0x080 |
|
; Now, some macros |
#define PUSH(RG,SP) SUB 1,SP \ |
STO RG,-1(SP) |
#define POP(RG,SP) LOD -1(SP),RG \ |
ADD 1,SP |
#define FJSR(LBL,RG) MOV __here__+2(PC),RG \ |
JMP LBL |
#define FRET(RG) MOV RG,PC |
#define JSR(LBL,RG) SUB 1,SP \ |
MOV __here__+3(PC),RG \ |
STO RG,-1(SP) \ |
JMP LBL \ |
ADD 1,SP |
#define RET LOD -1(SP),PC |
#define SAVE_USER_CONTEXT(DR,AR) \ |
MOV -16(uSP),AR \ |
MOV uPC,DR \ |
STO DR,-16(AR) \ |
MOV uCC,DR \ |
STO DR,-15(AR) \ |
MOV uSP,DR \ |
STO DR,-14(AR) \ |
MOV uR12,DR \ |
STO DR,-13(AR) \ |
MOV uR11,DR \ |
STO DR,-12(AR) \ |
MOV uR10,DR \ |
STO DR,-11(AR) \ |
MOV uR9,DR \ |
STO DR,-10(AR) \ |
MOV uR8,DR \ |
STO DR,-9(AR) \ |
MOV uR7,DR \ |
STO DR,-8(AR) \ |
MOV uR6,DR \ |
STO DR,-7(AR) \ |
MOV uR5,DR \ |
STO DR,-6(AR) \ |
MOV uR4,DR \ |
STO DR,-5(AR) \ |
MOV uR3,DR \ |
STO DR,-4(AR) \ |
MOV uR2,DR \ |
STO DR,-3(AR) \ |
MOV uR1,DR \ |
STO DR,-2(AR) \ |
MOV uR0,DR \ |
STO DR,-1(AR) |
#define RESTORE_USER_CONTEXT(DR,AR) \ |
LOD -1(AR),DR \ |
MOV DR,uR0 \ |
LOD -2(AR),DR \ |
MOV DR,uR1 \ |
LOD -3(AR),DR \ |
MOV DR,uR2 \ |
LOD -4(AR),DR \ |
MOV DR,uR3 \ |
LOD -5(AR),DR \ |
MOV DR,uR4 \ |
LOD -6(AR),DR \ |
MOV DR,uR5 \ |
LOD -7(AR),DR \ |
MOV DR,uR6 \ |
LOD -8(AR),DR \ |
MOV DR,uR7 \ |
LOD -9(AR),DR \ |
MOV DR,uR8 \ |
LOD -10(AR),DR \ |
MOV DR,uR9 \ |
LOD -11(AR),DR \ |
MOV DR,uR10 \ |
LOD -12(AR),DR \ |
MOV DR,uR11 \ |
LOD -13(AR),DR \ |
MOV DR,uR12 \ |
LOD -14(AR),DR \ |
MOV DR,uSP \ |
LOD -15(AR),DR \ |
MOV DR,uCC \ |
LOD -16(AR),DR \ |
MOV DR,uPC |
#define READ_USER_TRAP(RG) \ |
MOV uCC,RG \ |
AND -256,RG |
|
/zasm/zopcodes.cpp
4,7 → 4,10
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU core |
// |
// Purpose: |
// Purpose: A simple program to handle the disassembly and definition |
// of the various Zip Assembly opcodes. The primary function |
// of this file is the zipi_to_string, or Zip Instruction to |
// string (disassemble) conversion. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Tecnology, LLC |
65,6 → 68,7
// Special case instructions. These are general instructions, but with |
// special opcodes |
// Conditional branches |
"BUSY", 0xffffffff, 0x2f0f7fff, OPUNUSED, OPUNUSED, OPUNUSED, OPUNUSED, BITFIELD(3,21), |
"BRA", 0xffff8000, 0x2f0f0000, OPUNUSED, OPUNUSED, OPUNUSED, IMMFIELD(15,0), OPUNUSED, |
"BRZ", 0xffff8000, 0x2f2f0000, OPUNUSED, OPUNUSED, OPUNUSED, IMMFIELD(15,0), OPUNUSED, |
"BNZ", 0xffff8000, 0x2f4f0000, OPUNUSED, OPUNUSED, OPUNUSED, IMMFIELD(15,0), OPUNUSED, |
/zasm/zparser.cpp
4,7 → 4,14
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU core |
// |
// Purpose: |
// Purpose: This file is really mis-named. At one time it was going to |
// be the parser for the Zip Assembler, zasm. Since then, I |
// discovered Flex and Bison and have written a parser using |
// those tools. The true parser may therefore be found in zasm.y. |
// This file, however, still contains some very valuable tools. |
// In particular, all of the routines used to build instructions |
// from the appropriate fields are kept in this file. For example, |
// op_noop() returns the instruction code for a NOOP instruction. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Tecnology, LLC |
45,359 → 52,6
|
typedef ZPARSER::ZIPI ZIPI; // A Zip Instruction (i.e. uint32) |
|
bool ZPARSER::iscomment(const char *line) const { |
const char *sp = line; |
do { |
if (*sp == '\0') |
return true; |
else if (*sp == ';') |
return true; |
else if (*sp == '#') |
return true; |
else if (*sp == '\n') |
return true; |
else if (*sp == '\r') |
return true; |
else if ((*sp == '/')&&(sp[1] == '/')) |
return true; |
else if (!isspace(*sp)) |
return false; |
} while(*sp++); |
|
return true; |
} |
|
bool ZPARSER::islabel(const char *line) const { |
const char *sp = line; |
if (isspace(*line)) |
return false; |
if (!isalpha(*line)) |
return false; |
while((isalpha(*sp))||(isdigit(*sp))||(*sp=='_')) |
sp++; |
if (*sp != ':') |
return false; |
sp++; |
return iscomment(sp); |
} |
|
bool ZPARSER::parse_op(const char *line, ZPARSER::ZIPA pc, |
ZPARSER::ZIPI &ins, const unsigned lineno) const { |
const char *sp = line; |
char cpy[128], *cp = cpy, *point, *dollar, *plus, *comma, *paren, |
*opc, *opb, *opa; |
ZPARSER::ZIPREG ra = ZPARSER::ZIP_Rnone, rb = ZPARSER::ZIP_Rnone; |
ZIPCOND cnd = ZIPC_ALWAYS; |
ZIPIMM imm = 0; |
bool valid = true; |
|
if (!isspace(*sp)) |
return false; |
|
while(isspace(*sp)) |
sp++; |
// Remove comments from our local copy |
for(unsigned int i=0; i<sizeof(cpy); i++) { |
if (*sp == '\0') |
break; |
else if (*sp == ';') |
break; |
else if (*sp == '#') |
break; |
else if (*sp == '\n') |
break; |
else if (*sp == '\r') |
break; |
else if ((*sp == '/')&&(sp[1] == '/')) |
break; |
*cp++ = *sp++; |
} if (cp-cpy >= (int)sizeof(cpy)) |
return false; |
*cp = '\0'; |
point = strchr(cpy, '.'); |
comma = strchr(cpy, ','); |
plus = strchr(cpy, '+'); |
dollar= strchr(cpy, '$'); |
paren = strchr(cpy, '('); |
if (point) *point++ = '\0'; |
if (comma) *comma++ = '\0'; |
if (plus) *plus++ = '\0'; |
if (dollar) *dollar++ = '\0'; |
// if (paren) *paren++ = '\0'; |
cp = cpy; |
while(isalpha(*cp)) |
cp++; |
opc = cpy; |
if ((*cp == '\0')&&(point == NULL)) |
cp[1] = '\0'; |
*cp = '\0'; |
|
if ((point)&&(strncasecmp("DAT",point,3)!=0)) { |
cp = point; |
while(isalpha(*cp)) |
cp++; |
if (*cp == '\0') |
cp[1] = '\0'; |
*cp = '\0'; |
|
for(int i=1; i<8; i++) { |
if (strcasecmp(&zop_ccstr[i][1], point)==0) { |
cnd = (ZIPCOND)i; |
break; |
} |
} if (cnd == ZIPC_ALWAYS) { |
printf("ERR: Unrecognized condition, %s\n", point); |
valid = false; |
} |
} |
|
cp++; |
while(isspace(*cp)) |
cp++; |
opb = cp; |
opa = comma; |
|
if (paren) { |
opb = paren+1; |
if ((comma)&&(opb > comma)) |
opa = cp; |
} else if (plus) |
opb = plus; |
|
if (dollar) { |
// Figure out the base |
{ |
char *ip = dollar, mxd = 0; |
if ((*ip == '0')&&(toupper(ip[1])=='X')) |
imm = strtoul(dollar, NULL, 16); |
else { |
bool neg = false; |
if (*ip == '-') { |
neg = true; |
ip++; |
dollar++; |
} |
while(isdigit(*ip)||((toupper(*ip)>='A')&&(toupper(*ip)<='F'))) { |
if (isalpha(*ip)) |
mxd = (*ip-'a')+10; |
else |
mxd = (*ip-'0'); |
ip++; |
} |
|
if ((mxd <= 1)&&(*ip=='d')) |
imm = strtoul(dollar, NULL, 2); |
else if ((mxd <= 7)&&((*dollar == '0')||(toupper(*ip)=='O'))) |
imm = strtoul(dollar, NULL, 8); |
else if ((mxd <= 15)&&(toupper(*ip)=='H')) |
imm = strtoul(dollar, NULL, 16); |
else if ((toupper(*ip)=='D')||(*ip == '+')||(isspace(*ip))||(*ip == '(')||(*ip == '\0')) |
imm = atoi(dollar); |
else { |
printf("Cannot parse immediate, %s\n", dollar); |
printf("Assuming you meant %d\n", atoi(dollar)); |
imm = atoi(dollar); |
} |
|
if (neg) |
imm = -imm; |
} |
} |
} else |
imm = 0; |
|
if (*opb) for(int i=31; i>=0; i--) { |
// printf("%s Checking for match to opB: \'%s\' to %s", opc, opb, zop_regstr[i]); |
if (NULL != strcasestr(opb, zop_regstr[i])) { |
// printf(" --- Match\n"); |
rb = (ZIPREG)i; |
break; |
} // else printf(" -- nope\n"); |
} if (opa) for(int i=31; i>=0; i--) { |
// printf("%s Checking for match to opA: ,%s to %s", opc, opa, zop_regstr[i]); |
if (NULL != strcasestr(opa, zop_regstr[i])) { |
ra = (ZIPREG)i; |
// printf(" --- Match\n"); |
break; |
} // else printf(" -- nope\n"); |
} |
|
if (strcasecmp("MOV",opc)!=0) { |
// Only move instructions can reference user regs |
if ((ra != ZIP_Rnone)&&(ra >= ZIP_uR0)) |
valid = false; |
if ((rb != ZIP_Rnone)&&(rb >= ZIP_uR0)) |
valid = false; |
if (!valid) |
printf("ERR: Only Mov can specify user regs\n"); |
} |
|
if ((!*opc)&&(strncasecmp("DAT",point,3)==0)) { |
ins = imm; |
valid = true; |
} else if (strcasecmp("CMP",opc)==0) { |
if (rb != ZIP_Rnone) |
ins = op_cmp(cnd, imm, rb, ra); |
else ins = op_cmp(cnd, imm, ra); |
} else if (strcasecmp("TST",opc)==0) { |
if (rb != ZIP_Rnone) |
ins = op_tst(cnd, imm, rb, ra); |
else if (!dollar) |
ins = op_tst(cnd, ra); |
else |
ins = op_tst(cnd, imm, ra); |
} else if (strcasecmp("MOV",opc)==0) { |
if ((rb != ZIP_Rnone)&&(ra != ZIP_Rnone)) |
ins = op_mov(cnd, imm, rb, ra); |
else { printf("ERR MOV, ra = %d, rb = %d, imm = %d, cnd = %d\nLine was: %s", (int)ra, (int)rb, (int)imm, (int)cnd, line); valid = false; } |
} else if (strcasecmp("LDI",opc)==0) { |
if ((rb == ZIP_Rnone)&&(cnd == ZIPC_ALWAYS)) |
ins = op_ldi(imm, ra); |
else valid = false; |
} else if (strcasecmp("trap",opc)==0) { |
if ((rb == ZIP_Rnone)&&(rb == ZIP_Rnone)) |
ins = op_trap(cnd, imm); |
else |
valid = false; |
} else if (strcasecmp("CLR",opc)==0) { |
if ((ra == ZIP_Rnone)&&(!dollar)&&(cnd == ZIPC_ALWAYS)) |
ins = op_clr(rb); // Good |
else valid = false; |
} else if ((strcasecmp("NOOP",opc)==0)||(strcasecmp("NOP",opc)==0)) { |
if ((rb == ZIP_Rnone)&&(ra == ZIP_Rnone)&&(!dollar)&&(cnd == ZIPC_ALWAYS)) |
ins = op_noop(); |
else { printf("ERR: NOP, ra=%d, rb=%d, dollar = %s\n", |
(int)ra, (int)rb, (dollar)?"true":"false"); valid = false; } |
} else if ((strcasecmp("BREAK",opc)==0)||(strcasecmp("BRK",opc)==0)) { |
if ((rb == ZIP_Rnone)&&(ra == ZIP_Rnone)&&(!dollar)&&(cnd == ZIPC_ALWAYS)) |
ins = op_break(); |
else { printf("ERR: BRK, ra=%d, rb=%d, dollar = %s\n", |
(int)ra, (int)rb, (dollar)?"true":"false"); valid = false; } |
} else if ((strcasecmp("LDIHI",opc)==0)||(strcasecmp("LODIHI",opc)==0)) { |
if ((dollar)&&(ra != ZIP_Rnone)) |
ins = op_ldihi(cnd, imm, ra); |
else valid = false; |
} else if ((strcasecmp("LDILO",opc)==0)||(strcasecmp("LODILO",opc)==0)){ |
if ((dollar)&&(ra != ZIP_Rnone)) |
ins = op_ldilo(cnd, imm, ra); |
else valid = false; |
} else if ((strcasecmp("LOD",opc)==0)||(strcasecmp("LOAD",opc)==0)) { |
if (rb != ZIP_Rnone) |
ins = op_lod(cnd,imm,rb,ra); |
else ins = op_lod(cnd,imm,ra); |
} else if ((strcasecmp("STO",opc)==0)||(strcasecmp("STOR",opc)==0)) { |
// printf("STO: Imm = %d, RA = %d, RB = %d\n", imm, ra, rb); |
if (rb != ZIP_Rnone) |
ins = op_sto(cnd,ra,imm,rb); |
else ins = op_sto(cnd,ra,imm); |
} else if (strcasecmp("SUB",opc)==0) { |
if (rb != ZIP_Rnone) |
ins = op_sub(cnd,imm,rb,ra); |
else ins = op_sub(cnd,imm,ra); |
} else if (strcasecmp("AND",opc)==0) { |
if (rb != ZIP_Rnone) |
ins = op_and(cnd,imm,rb,ra); |
else ins = op_and(cnd,imm,ra); |
} else if (strcasecmp("ADD",opc)==0) { |
if (rb != ZIP_Rnone) |
ins = op_add(cnd,imm,rb,ra); |
else ins = op_add(cnd,imm,ra); |
} else if (strcasecmp("OR",opc)==0) { |
if (rb != ZIP_Rnone) |
ins = op_or(cnd,imm,rb,ra); |
else ins = op_or(cnd,imm,ra); |
} else if (strcasecmp("XOR",opc)==0) { |
if (rb != ZIP_Rnone) |
ins = op_xor(cnd,imm,rb,ra); |
else ins = op_xor(cnd,imm,ra); |
} else if ((strcasecmp("LSL",opc)==0)||(strcasecmp("ASL",opc)==0)) { |
if (rb != ZIP_Rnone) |
ins = op_lsl(cnd,imm,rb,ra); |
else ins = op_lsl(cnd,imm,ra); |
} else if (strcasecmp("ASR",opc)==0) { |
if (rb != ZIP_Rnone) |
ins = op_asr(cnd,imm,rb,ra); |
else ins = op_asr(cnd,imm,ra); |
} else if (strcasecmp("LSR",opc)==0) { |
if (rb != ZIP_Rnone) |
ins = op_lsr(cnd,imm,rb,ra); |
else ins = op_lsr(cnd,imm,ra); |
} else if (strcasecmp("BR",opc)==0) { |
if ((dollar)||(ra != ZIP_Rnone)||(rb != ZIP_Rnone)) |
valid = false; |
else ins = op_bra(cnd, imm); |
} else if (strcasecmp("BRA",opc)==0) { |
if ((!dollar)||(ra != ZIP_Rnone)||(rb != ZIP_Rnone)||(cnd != ZIPC_ALWAYS)) |
valid = false; |
else ins = op_bra(imm); |
} else if (strcasecmp("BRZ",opc)==0) { |
if ((!dollar)||(ra != ZIP_Rnone)||(rb != ZIP_Rnone)||(cnd != ZIPC_ALWAYS)) |
valid = false; |
else ins = op_brz(imm); |
} else if ((strcasecmp("BRNZ",opc)==0)||(strcasecmp("BNZ",opc)==0)) { |
if ((!dollar)||(ra != ZIP_Rnone)||(rb != ZIP_Rnone)||(cnd != ZIPC_ALWAYS)) |
valid = false; |
else ins = op_bnz(imm); |
} else if ((strcasecmp("BRGE",opc)==0)||(strcasecmp("BGE",opc)==0)) { |
if ((!dollar)||(ra != ZIP_Rnone)||(rb != ZIP_Rnone)||(cnd != ZIPC_ALWAYS)) |
valid = false; |
else ins = op_bge(imm); |
} else if ((strcasecmp("BRGT",opc)==0)||(strcasecmp("BGT",opc)==0)) { |
if ((!dollar)||(ra != ZIP_Rnone)||(rb != ZIP_Rnone)||(cnd != ZIPC_ALWAYS)) |
valid = false; |
else ins = op_bgt(imm); |
} else if (strcasecmp("BRZ",opc)==0) { |
} else if ((strcasecmp("BRLT",opc)==0)||(strcasecmp("BLT",opc)==0)) { |
if ((!dollar)||(ra != ZIP_Rnone)||(rb != ZIP_Rnone)||(cnd != ZIPC_ALWAYS)) |
valid = false; |
else ins = op_blt(imm); |
} else if ((strcasecmp("BRC",opc)==0)||(strcasecmp("BC",opc)==0)) { |
if ((!dollar)||(ra != ZIP_Rnone)||(rb != ZIP_Rnone)||(cnd != ZIPC_ALWAYS)) |
valid = false; |
else ins = op_brc(imm); |
} else if ((strcasecmp("BRV",opc)==0)||(strcasecmp("BV",opc)==0)) { |
if ((!dollar)||(ra != ZIP_Rnone)||(rb != ZIP_Rnone)||(cnd != ZIPC_ALWAYS)) |
valid = false; |
else ins = op_brv(imm); |
} else if (strcasecmp("CLRF",opc)==0) { |
if ((ra == ZIP_Rnone)&&(!dollar)&&(imm==0)) |
ins = op_clrf(cnd, rb); |
else valid = false; |
} else if((strcasecmp("HALT",opc)==0)||(strcasecmp("WAIT",opc)==0)) { |
if ((rb == ZIP_Rnone)&&(ra==ZIP_Rnone)&&(!opa)&&(!dollar)) |
ins = op_halt(cnd); |
else valid = false; |
} else if (strcasecmp("BUSY",opc)==0) { |
if ((rb == ZIP_Rnone)&&(ra==ZIP_Rnone)&&(!opa)&&(!dollar)) |
ins = op_busy(cnd); |
else valid = false; |
} else if (strcasecmp("RTU",opc)==0) { |
if ((rb == ZIP_Rnone)&&(ra==ZIP_Rnone)&&(imm==0)&&(!opa)&&(!dollar)) |
ins = op_rtu(cnd); |
else { printf("ERRR,RTU, ra=%d,rb=%d,imm=%08x,comma=%s,dollar=%s\n", |
(int)ra, (int)rb, imm, (opa)?"true":"false", |
(dollar)?"true":"false"); |
valid = false; |
} |
} else if (strcasecmp("JMP",opc)==0) { |
if ((rb != ZIP_Rnone)&&(!opa)) |
ins = op_not(cnd, rb); |
else valid = false; |
} else if (strcasecmp("NOT",opc)==0) { |
if ((rb != ZIP_Rnone)&&(ra==ZIP_Rnone)&&(!opa)&&(!dollar)) |
ins = op_not(cnd, rb); |
else valid = false; |
} else valid = false; |
|
return valid; |
} |
|
bool ZPARSER::parse(const char *line, ZPARSER::ZIPA &pc, ZPARSER::ZIPI &instruction, const unsigned int lineno) { |
bool v = parse_op(line, pc, instruction, lineno); |
pc = pc + 1; |
return v; |
} |
|
#define IMMOP(OP,CND,IMM,A) (((OP&0x0f)<<28)|((A&0x0f)<<24)|((CND&0x07)<<21) \ |
| (IMM & 0x0fffff)) |
|
439,8 → 93,12
|
ZIPI ZPARSER::op_trap(ZIPCOND cnd, ZIPIMM imm) const { |
ZIPI in; |
in = ((0x4f)<<24)|((cnd&0x07)<<21)|(1<<20)|((0x0e)<<16); |
in |= (imm & 0x0ffff); |
if (cnd != ZIPC_ALWAYS) |
return op_ldilo(cnd, imm, ZIP_CC); |
else |
return op_ldi(imm, ZIP_CC); |
// in = ((0x4f)<<24)|((cnd&0x07)<<21)|(1<<20)|((0x0e)<<16); |
// in |= (imm & 0x0ffff); |
return in; |
} |
|
/zasm/obj-pc/depends.txt
1,3 → 1,4
obj-pc/asmdata.o: asmdata.cpp asmdata.h zopcodes.h zparser.h |
obj-pc/optest.o: optest.cpp zopcodes.h |
obj-pc/twoc.o: twoc.cpp twoc.h |
obj-pc/zasm.o: zasm.cpp zopcodes.h zparser.h |
/zasm/zparser.h
4,7 → 4,14
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU core |
// |
// Purpose: The beginnings of a parser for Zip CPU assembly. |
// Purpose: This file is really mis-named. At one time it was going to |
// be header file for the parser for the Zip Assembler, zasm. |
// Since then, I discovered Flex and Bison and have written a |
// parser using those tools. The true parser may therefore be |
// found in zasm.y. This file, however, still declares some |
// very valuable tools. In particular, all of the routines used |
// to build instructions from the appropriate fields are declared |
// in this file. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Tecnology, LLC |
56,10 → 63,6
ZIPC_C, ZIPC_V |
} ZIPCOND; |
|
bool iscomment(const char *line) const; |
bool islabel(const char *line) const; |
bool parse_op(const char *line, ZIPA pc, ZIPI &instruction, const unsigned lineno) const; |
bool parse(const char *line, ZIPA &pc, ZIPI &instruction, const unsigned lineno); |
|
ZIPI op_cmp(ZIPCOND cnd, ZIPIMM imm, ZIPREG b, ZIPREG a) const; |
ZIPI op_cmp(ZIPCOND cnd, ZIPIMM imm, ZIPREG a) const; |
/zasm/zopcodes.h
48,7 → 48,7
#define IMMFIELD(LN,MN) (0x40000000 + (((LN&0x0ff)<<8)+(MN&0x0ff))) // Sgn extnd |
// #define REGVAL(V) ((V & 0x0f)+0x020) |
|
typedef unsigned int ZIPI; |
typedef unsigned int ZIPI; // A Zip CPU instruction |
|
typedef struct { |
char s_opstr[8]; |
/zasm/Makefile
4,9 → 4,19
# |
# Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
# |
# Purpose: This makefile builds an assembler and disassembler. |
# Purpose: This makefile builds an assembler preprocessor (zpp), an |
# assembler (zasm) and a disassembler (zdump). Make with no |
# arguments will produce these files. Other targets include: |
# |
# make clean |
# make test |
# Assembles a test file and then produces a disassembly of it. |
# make depends |
# Doesn't work. Dependencies are currently all hand coded. |
# make optest |
# make tags |
# |
# |
# Creator: Dan Gisselquist, Ph.D. |
# Gisselquist Tecnology, LLC |
# |
30,16 → 40,16
# |
################################################################################ |
# |
.PHONY: all |
all: zasm optest zdump |
OBJDIR= obj-pc |
CXX= g++ |
CC= gcc |
YACC= bison |
LEX= flex |
YYMMDD=`date +%Y%m%d` |
DBGFLAGS= -g -O0 |
OPTFLAGS= -O3 |
CCFLAGS= $(DBGFLAGS) |
PROGRAMS= zasm optest zdump |
PROGRAMS= zasm optest zdump zpp |
|
.PHONY: programs |
all: $(OBJDIR)/ programs |
46,11 → 56,29
|
programs: $(PROGRAMS) |
|
ZASMSRC= zasm.cpp zopcodes.cpp zparser.cpp twoc.cpp |
ZASMOBJ= $(addprefix $(OBJDIR)/,$(subst .cpp,.o,$(ZASMSRC))) |
zasm: $(ZASMOBJ) |
$(CXX) $(CCFLAGS) $(ZASMOBJ) -o $@ |
$(OBJDIR)/zpp.cpp: zpp.l |
$(LEX) -o $@ $^ |
$(OBJDIR)/zpp.o: $(OBJDIR)/zpp.cpp |
$(CXX) -c $(CCFLAGS) $(OBJDIR)/zpp.cpp -o $@ |
zpp: $(OBJDIR)/zpp.o |
$(CXX) -o $@ $(CCFLAGS) $^ |
|
$(OBJDIR)/zasm.tab.h: zasm.y asmdata.h zparser.h |
$(YACC) -b $(OBJDIR)/zasm -d zasm.y |
zasm.output: zasm.y asmdata.h |
$(YACC) -v -b $(OBJDIR)/zasm -d zasm.y |
$(OBJDIR)/zasm.lex.cpp: zasm.l $(OBJDIR)/zasm.tab.h |
$(LEX) -o $@ zasm.l |
$(OBJDIR)/zasm.lex.o: $(OBJDIR)/zasm.lex.cpp |
$(CXX) -c -I. -I$(OBJDIR)/ $(CCFLAGS) $(OBJDIR)/zasm.lex.cpp -o $@ |
$(OBJDIR)/zasm.tab.o: $(OBJDIR)/zasm.tab.c |
$(CXX) -c -I. -I$(OBJDIR)/ $(CCFLAGS) $(OBJDIR)/zasm.tab.c -o $@ |
$(OBJDIR)/asmdata.o: asmdata.cpp zopcodes.h zparser.h |
$(CXX) -c -I. $(CCFLAGS) asmdata.cpp -o $@ |
zasm: $(OBJDIR)/zasm.lex.o $(OBJDIR)/zasm.tab.o $(OBJDIR)/asmdata.o |
zasm: $(OBJDIR)/zparser.o $(OBJDIR)/zopcodes.o $(OBJDIR)/twoc.o |
$(CXX) -o $@ $(CCFLAGS) $^ |
|
optest: optest.cpp zopcodes.cpp twoc.cpp twoc.h zopcodes.h |
$(CXX) $(CCFLAGS) optest.cpp zopcodes.cpp twoc.cpp -o optest |
|
65,9 → 93,16
$(OBJDIR)/: |
@bash -c "if [ ! -e $(OBJDIR) ]; then mkdir -p $(OBJDIR); fi" |
|
.PHONY: test |
test: dumpd.txt |
z.out: test.S zasm zdump |
./zasm test.S -o z.out |
dumpd.txt: z.out zdump |
./zdump z.out > dumpd.txt |
|
define build-depends |
@echo "Building dependency file(s)" |
@$(CXX) $(CCFLAGS) -MM *.cpp > xd.txt |
@$(CXX) -I $(OBJDIR)/ $(CCFLAGS) -MM *.cpp > xd.txt |
@sed -e 's/^.*.o: /$(OBJDIR)\/&/' < xd.txt > $(OBJDIR)/depends.txt |
@rm xd.txt |
endef |
80,19 → 115,13
depends: tags |
$(build-depends) |
|
$(OBJDIR)/depends.txt: $(OBJDIR)/ $(SOURCES) $(HEADERS) |
$(OBJDIR)/depends.txt: $(SOURCES) $(HEADERS) |
$(build-depends) |
|
.PHONY: clean |
clean: |
rm -rf $(OBJDIR) $(PROGRAMS) |
rm -rf $(OBJDIR) $(PROGRAMS) z.out dumpd.txt |
|
-include $(OBJDIR)/depends.txt |
|
|
|
|
|
|
|
|