OpenCores
URL https://opencores.org/ocsvn/socgen/socgen/trunk

Subversion Repositories socgen

[/] [socgen/] [trunk/] [tools/] [bin/] [p65] - Blame information for rev 130

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 130 jt_eaton
eval 'exec `which perl` -S $0 ${1+"$@"}'
2
   if 0;
3
# The P65 Assembler, v 1.1
4
# Copyright (c) 2001,2 Michael Martin
5
# All rights reserved.
6
#
7
# Redistribution and use, with or without modification, are permitted
8
# provided that the following conditions are met:
9
#
10
# - Redistributions of the code must retain the above copyright
11
#   notice, this list of conditions and the following disclaimer.
12
#
13
# - The name of Michael Martin may not be used to endorse or promote
14
#   products derived from this software without specific prior written
15
#   permission.
16
#
17
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
23
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
# POSSIBILITY OF SUCH DAMAGE.
29
 
30
use strict;
31
use integer;
32
 
33
# Global variables
34
 
35
my $pc;               # Current program counter
36
my $linenum;          # Current line number
37
my $currentfile;      # Current file name
38
my @IR = ( );         # Intermediate Representation list
39
my @code = ( );       # Final binary
40
my %segments = ( );   # Segment map for gensymming segment labels
41
my $segment = "text"; # Current segment
42
 
43
 
44
my ($codecount, $datacount, $fillercount);
45
 
46
my ($verbose, $trace, $printbin);
47
 
48
# Error reporting routines
49
 
50
my $errorcount = 0;
51
 
52
sub asmerror {
53
    my $err = shift;
54
    print "ERROR: $currentfile:$linenum: $err\n";
55
    $errorcount++;
56
}
57
 
58
sub num_errors { return $errorcount; }
59
 
60
sub report_errors {
61
    my $errornum = $errorcount ? $errorcount : "No";
62
    my $errorname = ($errorcount == 1) ? "error" : "errors";
63
    if ($verbose || $errorcount) {
64
        print "$errornum $errorname\n";
65
    }
66
}
67
 
68
# Argument Evaluation Routines
69
 
70
sub create_arg {
71
    my ($prefix, $type, $val, $offset) = @_;
72
    return [$prefix, $type, $val, $offset];
73
}
74
 
75
sub can_evaluate {
76
    my $arg = shift;
77
    my ($prefix, $type, $val, $offset) = @$arg;
78
    return ($type eq "num" || label_exists($val));
79
}
80
 
81
sub hardcoded_arg {
82
    my $arg = shift;
83
    my ($prefix, $type, $val, $offset) = @$arg;
84
    return ($type eq "num");
85
}
86
 
87
sub eval_arg {
88
    my $result = 0;
89
    my $arg = shift;
90
    my ($prefix, $type, $val, $offset) = @$arg;
91
    if ($type eq "num") {
92
        $result = $val;
93
    } else {
94
        $result = label_value($val);
95
    }
96
    $result += $offset;
97
    if ($prefix eq "<") {
98
        return $result % 256;
99
    } elsif ($prefix eq ">") {
100
        return $result / 256;
101
    } else {
102
        return $result;
103
    }
104
}
105
 
106
sub arg_as_string {
107
    my $arg = shift;
108
    my ($prefix, $type, $val, $offset) = @$arg;
109
 
110
    my $sign = ($offset < 0) ? "" : "+";
111
    my $suffix = ($offset == 0) ? "" : "${sign}$offset";
112
 
113
    if ($prefix eq "") {
114
        return "${val}$suffix";
115
    } else {
116
        return "${prefix}${val}$suffix";
117
    }
118
}
119
 
120
1;
121
 
122
# The IR Walker
123
 
124
sub walk {
125
    my $dispatchtable = shift;
126
    $pc = 0;
127
    for (@IR) {
128
        ($linenum, $currentfile) = @$_;
129
        my $node_type = $$_[2];
130
        if (exists $$dispatchtable{$node_type}) {
131
            &{$$dispatchtable{$node_type}}($_);
132
        } elsif (exists $$dispatchtable{"UNKNOWN"}) {
133
            &{$$dispatchtable{"UNKNOWN"}}($_);
134
        } else {
135
            asmerror "Unknown IR type $node_type";
136
        }
137
    }
138
}
139
 
140
# Labels support
141
 
142
my %labels = ( );  # Label -> PC hash
143
 
144
sub label_exists {
145
    my $label = shift;
146
    $label = lc $label;
147
    return ((exists $labels{$label}) || ($label eq "^"));
148
}
149
 
150
sub label_value {
151
    my $label = shift;
152
    if ($label eq "^") {
153
        return $pc;
154
    } else {
155
        $label = lc $label;
156
        return $labels{$label};
157
    }
158
}
159
 
160
sub set_label {
161
    my ($label, $value) = @_;
162
    $label = lc $label;
163
    $labels{$label} = $value;
164
}
165
 
166
sub defined_labels { return keys %labels; }
167
 
168
# Lexer: breaks lines into tokens
169
 
170
my $instrs="-adc-and-asl-bcc-bcs-beq-bit-bmi-bne-bpl-brk-bvc-bvs-clc-cld-cli-clv-cmp-cpx-cpy-dec-dex-dey-eor-inc-inx-iny-jmp-jsr-lda-ldx-ldy-lsr-nop-ora-pha-php-pla-plp-rol-ror-rti-rts-sbc-sec-sed-sei-sta-stx-sty-tax-tay-tsx-txa-txs-tya-";
171
 
172
my $instrs_6510="slo-rla-sre-rra-sax-lax-dcp-isb-anc-asr-arr-ane-ane-lxa-sbx-sha-shs-las-shx-";
173
 
174
sub is_opcode {
175
    my $id = shift;
176
    return $instrs =~ /-$id-/;
177
}
178
 
179
sub interpret_token {
180
    my $tok = shift;
181
    my $firstchar = substr($tok, 0, 1);
182
    my $rest = substr($tok, 1);
183
    if ($tok eq "") {
184
        return ();
185
    } elsif ($firstchar eq '"') {
186
        return ["STRING", $rest];
187
    } elsif ($firstchar eq "\$") {
188
        if ($rest =~ /^[0-9a-f]+$/i) {
189
            my $result = hex $rest;
190
            return ["NUM", $result];
191
        } else {
192
            asmerror("Expected a hex value, not '$rest'");
193
            return ["NUM", 0];
194
        }
195
    } elsif ($firstchar eq "\%") {
196
        if ($rest =~ /^[01]+$/) {
197
            my $result = 0;
198
            my @bits = split //, $rest;
199
            for (@bits) {
200
                $result *= 2;
201
                $result += $_;
202
            }
203
            return ["NUM", $result];
204
        } else {
205
            asmerror("Expected a binary value, not '$rest'");
206
            return ["NUM", 0];
207
        }
208
    } elsif ($firstchar eq "0") {
209
        if ($tok =~ /^[0-7]+$/i) {
210
            my $result = oct $tok;
211
            return ["NUM", $result];
212
        } else {
213
            asmerror("Expected an octal value, not '$rest'");
214
            return ["NUM", 0];
215
        }
216
    } elsif ($firstchar =~ /[1-9]/) {
217
        if ($tok =~ /^[0-9]+$/i) {
218
            my $result = int $tok;
219
            return ["NUM", $result];
220
        } else {
221
            asmerror("Expected a decimal value, not '$rest'");
222
            return ["NUM", 0];
223
        }
224
    } elsif ($firstchar eq "'") {
225
        if (substr($rest,1) eq "") {
226
            return ["NUM", ord $rest];
227
        } else {
228
            asmerror("Expected a character, not '$rest'");
229
            return ["NUM", 0];
230
        }
231
    } elsif ($firstchar =~ /[\#,<>():.+\-^*]/) {
232
        if ($rest ne "") { asmerror("lexer error: $tok can't happen"); }
233
        if ($firstchar eq "^") {
234
            return ["LABEL", "^"];
235
        } else {
236
            return [$firstchar];
237
        }
238
    } else {  # Label or opcode.
239
        my $id = lc($tok);
240
        if (is_opcode($id)) {
241
            return (["OPCODE", $id]);
242
        } elsif ($id eq "x") {
243
            return (["X"]);
244
        } elsif ($id eq "y") {
245
            return (["Y"]);
246
        } else {
247
            return (["LABEL", $id]);
248
        }
249
    }
250
}
251
 
252
sub interpret_EOL {
253
    return ["EOL"];
254
}
255
 
256
sub lex {
257
    my $input = shift;
258
    my @result = ();
259
    my $value = "";
260
    my ($quotemode, $backspacemode) = (0, 0);
261
 
262
    my @chars = split //, $input;
263
 
264
    for (@chars) {
265
        if ($backspacemode) {
266
            $backspacemode = 0;
267
            $value .= $_;
268
        } elsif ($_ eq "\\") {
269
            $backspacemode = 1;
270
        } elsif ($quotemode) {
271
            if ($_ eq '"') {
272
                $quotemode = 0;
273
            } else {
274
                $value .= $_;
275
            }
276
        } else {
277
            if ($_ eq ";") {
278
                push @result, interpret_token($value);
279
                $value = "";
280
                last;
281
            } elsif ($_ =~ /\s/) {
282
                push @result, interpret_token($value);
283
                $value = "";
284
            } elsif ($_ =~ /[\#<>,():.+\-^*]/) {
285
                push @result, interpret_token($value);
286
                push @result, interpret_token($_);
287
                $value = "";
288
            } elsif ($_ eq '"') {
289
                push @result, interpret_token($value);
290
                $value = '"';
291
                $quotemode = 1;
292
            } else {
293
                $value .= $_;
294
            }
295
        }
296
    }
297
    if ($backspacemode) { asmerror("Cannot end a line with a backspace"); }
298
    if ($quotemode) { asmerror("Unterminated string constant"); }
299
 
300
    push @result, interpret_token($value);
301
    push @result, interpret_EOL();
302
 
303
    return @result;
304
}
305
 
306
# Opcode interpretation routines
307
 
308
my %opcodes = (
309
               adc_imm  => 0x69,
310
               adc_zp   => 0x65,
311
               adc_zpx  => 0x75,
312
               adc_abs  => 0x6D,
313
               adc_absx => 0x7D,
314
               adc_absy => 0x79,
315
               adc_indx => 0x61,
316
               adc_indy => 0x71,
317
               and_imm  => 0x29,
318
               and_zp   => 0x25,
319
               and_zpx  => 0x35,
320
               and_abs  => 0x2D,
321
               and_absx => 0x3D,
322
               and_absy => 0x39,
323
               and_indx => 0x21,
324
               and_indy => 0x31,
325
               asl_imp  => 0x0A,
326
               asl_zp   => 0x06,
327
               asl_zpx  => 0x16,
328
               asl_abs  => 0x0E,
329
               asl_absx => 0x1E,
330
               bcc_rel  => 0x90,
331
               bcs_rel  => 0xB0,
332
               beq_rel  => 0xF0,
333
               bit_zp   => 0x24,
334
               bit_abs  => 0x2C,
335
               bmi_rel  => 0x30,
336
               bne_rel  => 0xD0,
337
               bpl_rel  => 0x10,
338
               brk_imp  => 0x00,
339
               bvc_rel  => 0x50,
340
               bvs_rel  => 0x70,
341
               clc_imp  => 0x18,
342
               cld_imp  => 0xD8,
343
               cli_imp  => 0x58,
344
               clv_imp  => 0xB8,
345
               cmp_imm  => 0xC9,
346
               cmp_zp   => 0xC5,
347
               cmp_zpx  => 0xD5,
348
               cmp_abs  => 0xCD,
349
               cmp_absx => 0xDD,
350
               cmp_absy => 0xD9,
351
               cmp_indx => 0xC1,
352
               cmp_indy => 0xD1,
353
               cpx_imm  => 0xE0,
354
               cpx_zp   => 0xE4,
355
               cpx_abs  => 0xEC,
356
               cpy_imm  => 0xC0,
357
               cpy_zp   => 0xC4,
358
               cpy_abs  => 0xCC,
359
               dec_zp   => 0xC6,
360
               dec_zpx  => 0xD6,
361
               dec_abs  => 0xCE,
362
               dec_absx => 0xDE,
363
               dex_imp  => 0xCA,
364
               dey_imp  => 0x88,
365
               eor_imm  => 0x49,
366
               eor_zp   => 0x45,
367
               eor_zpx  => 0x55,
368
               eor_abs  => 0x4D,
369
               eor_absx => 0x5D,
370
               eor_absy => 0x59,
371
               eor_indx => 0x41,
372
               eor_indy => 0x51,
373
               inc_zp   => 0xE6,
374
               inc_zpx  => 0xF6,
375
               inc_abs  => 0xEE,
376
               inc_absx => 0xFE,
377
               inx_imp  => 0xE8,
378
               iny_imp  => 0xC8,
379
               jmp_abs  => 0x4C,
380
               jmp_ind  => 0x6C,
381
               jsr_abs  => 0x20,
382
               lda_imm  => 0xA9,
383
               lda_zp   => 0xA5,
384
               lda_zpx  => 0xB5,
385
               lda_abs  => 0xAD,
386
               lda_absx => 0xBD,
387
               lda_absy => 0xB9,
388
               lda_indx => 0xA1,
389
               lda_indy => 0xB1,
390
               ldx_imm  => 0xA2,
391
               ldx_zp   => 0xA6,
392
               ldx_zpy  => 0xB6,
393
               ldx_abs  => 0xAE,
394
               ldx_absy => 0xBE,
395
               ldy_imm  => 0xA0,
396
               ldy_zp   => 0xA4,
397
               ldy_zpx  => 0xB4,
398
               ldy_abs  => 0xAC,
399
               ldy_absx => 0xBC,
400
               lsr_imp  => 0x4A,
401
               lsr_zp   => 0x46,
402
               lsr_zpy  => 0x56,
403
               lsr_abs  => 0x4E,
404
               lsr_absy => 0x5E,
405
               nop_imp  => 0xEA,
406
               ora_imm  => 0x09,
407
               ora_zp   => 0x05,
408
               ora_zpx  => 0x15,
409
               ora_abs  => 0x0D,
410
               ora_absx => 0x1D,
411
               ora_absy => 0x19,
412
               ora_indx => 0x01,
413
               ora_indy => 0x11,
414
               pha_imp  => 0x48,
415
               php_imp  => 0x08,
416
               pla_imp  => 0x68,
417
               plp_imp  => 0x28,
418
               rol_imp  => 0x2A,
419
               rol_zp   => 0x26,
420
               rol_zpx  => 0x36,
421
               rol_abs  => 0x2E,
422
               rol_absx => 0x3E,
423
               ror_imp  => 0x6A,
424
               ror_zp   => 0x66,
425
               ror_zpx  => 0x76,
426
               ror_abs  => 0x6E,
427
               ror_absx => 0x7E,
428
               rti_imp  => 0x40,
429
               rts_imp  => 0x60,
430
               sbc_imm  => 0xE9,
431
               sbc_zp   => 0xE5,
432
               sbc_zpx  => 0xF5,
433
               sbc_abs  => 0xED,
434
               sbc_absx => 0xFD,
435
               sbc_absy => 0xF9,
436
               sbc_indx => 0xE1,
437
               sbc_indy => 0xF1,
438
               sec_imp  => 0x38,
439
               sed_imp  => 0xF8,
440
               sei_imp  => 0x78,
441
               sta_zp   => 0x85,
442
               sta_zpx  => 0x95,
443
               sta_abs  => 0x8D,
444
               sta_absx => 0x9D,
445
               sta_absy => 0x99,
446
               sta_indx => 0x81,
447
               sta_indy => 0x91,
448
               stx_zp   => 0x86,
449
               stx_zpy  => 0x96,
450
               stx_abs  => 0x8E,
451
               sty_zp   => 0x84,
452
               sty_zpx  => 0x94,
453
               sty_abs  => 0x8C,
454
               tax_imp  => 0xAA,
455
               tay_imp  => 0xA8,
456
               tya_imp  => 0x98,
457
               tsx_imp  => 0xBA,
458
               txa_imp  => 0x8A,
459
               txs_imp  => 0x9A,
460
               tya_imp  => 0x98
461
               );
462
 
463
my %opcodes_6510 = (
464
                    slo_indx => 0x03,
465
                    rla_indx => 0x23,
466
                    sre_indx => 0x43,
467
                    rra_indx => 0x63,
468
                    sax_indx => 0x83,
469
                    lax_indx => 0xa3,
470
                    dcp_indx => 0xc3,
471
                    isb_indx => 0xe3,
472
                    slo_zp   => 0x07,
473
                    rla_zp   => 0x27,
474
                    sre_zp   => 0x47,
475
                    rra_zp   => 0x67,
476
                    sax_zp   => 0x87,
477
                    lax_zp   => 0xa7,
478
                    dcp_zp   => 0xc7,
479
                    isb_zp   => 0xe7,
480
                    anc_imm  => 0x0b,
481
                    asr_imm  => 0x4b,
482
                    arr_imm  => 0x6b,
483
                    ane_imm  => 0x8b,
484
                    lxa_imm  => 0xab,
485
                    sbx_imm  => 0xcb,
486
                    slo_abs  => 0x0f,
487
                    rla_abs  => 0x2f,
488
                    sre_abs  => 0x4f,
489
                    rra_abs  => 0x6f,
490
                    sax_abs  => 0x8f,
491
                    lax_abs  => 0xaf,
492
                    dcp_abs  => 0xcf,
493
                    isb_abs  => 0xef,
494
                    slo_indy => 0x13,
495
                    rla_indy => 0x33,
496
                    sre_indy => 0x53,
497
                    rra_indy => 0x73,
498
                    sha_indy => 0x93,
499
                    lax_indy => 0xb3,
500
                    dcp_indy => 0xd3,
501
                    isb_indy => 0xf3,
502
                    slo_zpx  => 0x17,
503
                    rla_zpx  => 0x37,
504
                    sre_zpx  => 0x57,
505
                    rra_zpx  => 0x77,
506
                    sax_zpy  => 0x97,
507
                    lax_zpy  => 0xb7,
508
                    dcp_zpx  => 0xd7,
509
                    isb_zpx  => 0xf7,
510
                    slo_absy => 0x1b,
511
                    rla_absy => 0x3b,
512
                    sre_absy => 0x5b,
513
                    rra_absy => 0x7b,
514
                    shs_absy => 0x9b,
515
                    las_absy => 0xbb,
516
                    dcp_absy => 0xdb,
517
                    isb_absy => 0xfb,
518
                    shx_absy => 0x7e,
519
                    slo_absx => 0x1f,
520
                    rla_absx => 0x3f,
521
                    sre_absx => 0x5f,
522
                    rra_absx => 0x7f,
523
                    sha_absy => 0x9f,
524
                    lax_absy => 0xbf,
525
                    dcp_absx => 0xdf,
526
                    isb_absx => 0xff,
527
                    );
528
 
529
sub has_mode {
530
    my ($opcode, $mode) = @_;
531
    return exists $opcodes{"${opcode}_$mode"};
532
}
533
 
534
sub get_opcode {
535
    my ($opcode, $mode) = @_;
536
    return $opcodes{"${opcode}_$mode"};
537
}
538
 
539
# The parser
540
 
541
my @line;
542
my $temp_label_count;
543
 
544
# Pragma dispatch table
545
 
546
my %pragmas = (
547
               address => \&pragma_word,
548
               advance => \&pragma_advance,
549
               alias   => \&pragma_alias,
550
               ascii   => \&pragma_ascii,
551
               byte    => \&pragma_byte,
552
               word    => \&pragma_word,
553
               checkpc => \&pragma_checkpc,
554
               include => \&pragma_include,
555
               incbin  => \&pragma_incbin,
556
               link    => \&pragma_link,
557
               org     => \&pragma_org,
558
               segment => \&pragma_segment,
559
               code    => \&pragma_code,
560
               text    => \&pragma_code,
561
               data    => \&pragma_data,
562
               space   => \&pragma_space,
563
               );
564
 
565
sub token_type {
566
    my $tok = shift;
567
    if ($tok) { return lc $$tok[0] };
568
}
569
 
570
sub token_value {
571
    my $tok = shift;
572
    if ($tok) { return $$tok[1] };
573
}
574
 
575
sub typematch {
576
    my ($token, $target) = @_;
577
    return (token_type($token) eq lc($target));
578
}
579
 
580
sub expect {
581
    my $actual = shift @line;
582
    for (@_) { if (typematch($actual, $_)) { return $actual; } }
583
    my $expected = join '", "', @_;
584
    asmerror "Expected \"$expected\"";
585
    return ["ERROR", 0];
586
}
587
 
588
sub lookahead {
589
    my ($range, @targets) = @_;
590
    my $result = 0;
591
 
592
    if (@line > $range) {
593
        my $actual = $line[$range];
594
        for (@targets) {
595
            if (typematch($actual, $_)) { return $actual; }
596
        }
597
    }
598
}
599
 
600
sub add_IR {
601
    push @IR, [$linenum, $currentfile, @_];
602
}
603
 
604
sub parse_line {
605
    if (lookahead(0, "EOL")) {
606
        return;
607
    } elsif (lookahead(1, ":")) {
608
        my $newlabel = token_value(expect("label"));
609
        expect ":";
610
        add_IR("LABEL", $newlabel, create_arg("","label","^",0));
611
        parse_line();
612
        return;
613
    } elsif (lookahead(0, ".")) {
614
        parse_pragma();
615
    } elsif (lookahead(0, "*")) {
616
        $temp_label_count++;
617
        expect "*";
618
        add_IR("LABEL", "\*$temp_label_count", create_arg("","label","^",0));
619
        parse_line();
620
    } else {
621
        parse_instr();
622
    }
623
    return;
624
}
625
 
626
sub parse_pragma {
627
    expect(".");
628
    my $pragma = token_value(expect("label"));
629
    if (exists $pragmas{$pragma}) {
630
        &{$pragmas{$pragma}}();
631
    } else {
632
        asmerror "Unknown pragma .$pragma";
633
    }
634
}
635
 
636
sub pragma_ascii {
637
    my $str = token_value(expect("string"));
638
    expect("EOL");
639
    my @data = map ord, split (//, $str);
640
    add_IR("BYTE", map {create_arg("","num",$_,0);} @data);
641
}
642
 
643
sub pragma_advance {
644
    my $target = parse_arg();
645
    expect("EOL");
646
    add_IR("ADVANCE", $target);
647
}
648
 
649
sub pragma_alias {
650
    my $newlabel = token_value(expect("label"));
651
    my $target = parse_arg();
652
    expect("EOL");
653
    add_IR("LABEL", $newlabel, $target);
654
}
655
 
656
sub segment_value {
657
    my $newsegment = shift;
658
    if (!exists($segments{$newsegment})) {
659
        return create_arg("", "num", 0, 0);
660
    }
661
    my $segcount = $segments{$newsegment};
662
    return (create_arg("", "label", "\*${newsegment}\*$segcount", 0));
663
}
664
 
665
sub set_segment {
666
    my $newsegment = shift;
667
    my $oldsegcount = $segments{$segment}+1;
668
    $segments{$segment} = $oldsegcount;
669
    add_IR("LABEL", "\*${segment}\*$oldsegcount", create_arg("","label","^",0));
670
    add_IR("SETPC", segment_value($newsegment));
671
    $segment = $newsegment;
672
}
673
 
674
sub pragma_segment {
675
    my $newsegment = token_value(expect("label"));
676
    expect("EOL");
677
    set_segment($newsegment);
678
}
679
 
680
sub pragma_code {
681
    expect("EOL");
682
    set_segment("text");
683
}
684
 
685
sub pragma_data {
686
    expect("EOL");
687
    set_segment("data");
688
}
689
 
690
sub pragma_byte {
691
    my $sep = ",";
692
    my @vals;
693
    while ($sep eq ",") {
694
        my $val = parse_arg();
695
        push @vals, $val;
696
        $sep = token_type(expect(",", "eol"));
697
    }
698
    add_IR("BYTE", @vals);
699
}
700
 
701
sub pragma_word {
702
    my $sep = ",";
703
    my @vals;
704
    while ($sep eq ",") {
705
        my $val = parse_arg();
706
        push @vals, $val;
707
        $sep = token_type(expect(",", "eol"));
708
    }
709
    add_IR("WORD", @vals);
710
}
711
 
712
sub pragma_include {
713
    my $file = token_value(expect("string"));
714
    expect("EOL");
715
 
716
    parsefile($file);
717
}
718
 
719
sub pragma_incbin {
720
    my $file = token_value(expect("string"));
721
    expect("EOL");
722
 
723
    local *INPUT;
724
 
725
    open INPUT, $file or die "Cannot open $file.  Dying painful death";
726
    binmode INPUT;
727
    my $line = "";
728
    my @bytes = ();
729
    while (read INPUT, $line, 1) {
730
        push @bytes, create_arg("", "num", unpack("C", $line), 0);
731
    }
732
    add_IR("BYTE", @bytes);
733
    close INPUT;
734
}
735
 
736
sub pragma_org {
737
    my $target = parse_arg();
738
    expect("EOL");
739
    add_IR("SETPC", $target);
740
}
741
 
742
sub pragma_checkpc {
743
    my $bound = parse_arg();
744
    expect("EOL");
745
    add_IR("CHECKPC", $bound);
746
}
747
 
748
sub pragma_link {
749
    my $file = token_value(expect("string"));
750
    my $target = parse_arg();
751
    expect("EOL");
752
 
753
    add_IR("SETPC", $target);
754
    parsefile($file);
755
}
756
 
757
sub pragma_space {
758
    my $newlabel = token_value(expect("label"));
759
    my $size = token_value(expect("num"));
760
    expect("EOL");
761
 
762
    add_IR("LABEL", $newlabel, create_arg("","label","^",0));
763
    add_IR("SETPC", create_arg("", "label", "^", $size));
764
}
765
 
766
sub parse_arg {
767
    my ($prefix, $arg, $offset) = ("", "", 0);
768
    if (lookahead(0, "<", ">")) {
769
        $prefix = token_type(expect("<", ">"));
770
    }
771
    my ($arg_type, $arg_val);
772
    if (lookahead(0, "+")) {
773
        my $target = $temp_label_count;
774
        $arg_type = "label";
775
        while(lookahead(0, "+") && !lookahead(1, "num")) {
776
            expect("+");
777
            $target++;
778
        }
779
        $arg_val = "\*$target";
780
    } elsif(lookahead(0, "-")) {
781
        my $target = $temp_label_count+1;
782
        $arg_type = "label";
783
        while(lookahead(0, "-") && !lookahead(1, "num")) {
784
            expect("-");
785
            $target--;
786
        }
787
        $arg_val = "\*$target";
788
    } else {
789
        my $arg = expect("num", "label");
790
        ($arg_type, $arg_val) = (token_type($arg), token_value($arg));
791
    }
792
    if (lookahead(0, "+", "-")) {
793
        my $sign = token_type(expect("+", "-"));
794
        my $val = token_value(expect("num"));
795
        $offset = ($sign eq "+") ? $val : -$val;
796
    }
797
    return create_arg($prefix, $arg_type, $arg_val, $offset);
798
}
799
 
800
sub parse_instr {
801
    my $opcode = token_value(expect("opcode"));
802
    my ($mode, $arg);
803
 
804
    if (lookahead(0, "#")) {
805
        $mode = ("IMMEDIATE");
806
        expect("#");
807
        $arg = parse_arg;
808
        expect("EOL");
809
    } elsif (lookahead(0, "(")) {
810
        # Some indirect mode.
811
        expect("(");
812
        $arg = parse_arg;
813
        if (lookahead(0, ",")) {
814
            $mode = ("INDIRECT-X");
815
            expect(","); expect("X"); expect(")"); expect("EOL");
816
        } else {
817
            expect(")");
818
            my $tok = token_type(expect(",", "EOL"));
819
            if ($tok eq "eol") {
820
                $mode = ("INDIRECT");
821
            } else {
822
                $mode = ("INDIRECT-Y");
823
                expect("Y"); expect("EOL");
824
            }
825
        }
826
    } elsif (lookahead(0, "EOL")) {
827
        $mode = ("IMPLIED");
828
        expect("EOL");
829
    } else { # Zero page or absolute (possibly indexed) or relative.
830
        $arg = parse_arg;
831
        my $tok = token_type(expect("EOL", ","));
832
        if ($tok eq ",") {
833
            $tok = token_type(expect("x", "y"));
834
            if ($tok eq "x") {
835
                $mode = "MEMORY-X";
836
            } else {
837
                $mode = "MEMORY-Y";
838
            }
839
            expect("EOL");
840
        } else {
841
            $mode = "MEMORY";
842
        }
843
    }
844
 
845
    add_IR($mode, $opcode, $arg);
846
}
847
 
848
sub parsefile {
849
    my $filename = shift;
850
    local *INPUT;
851
 
852
    my $oldfilename = $currentfile;
853
    my $oldlinenum = $linenum;
854
 
855
    $currentfile = $filename;
856
    $linenum = 0;
857
 
858
    open INPUT, $filename or die "Cannot open $filename.  Dying painful death";
859
    while () {
860
        $linenum++;
861
        @line = lex($_);
862
        parse_line;
863
    }
864
    close INPUT;
865
    $linenum = $oldlinenum;
866
    $currentfile = $oldfilename;
867
}
868
 
869
sub parse {
870
    my $basefile = shift;
871
 
872
    $temp_label_count = 0;
873
 
874
    parsefile($basefile);
875
}
876
 
877
# The various passes that walk over the IR
878
 
879
my $instructions_collapsed;
880
 
881
sub verify_IR {
882
    if ($verbose) { print "Commencing IR Verification phase.\n"; }
883
    init_labels();
884
    check_labels();
885
}
886
 
887
sub instruction_select {
888
    if ($verbose) { print "Commencing instruction selection phase.\n"; }
889
    $instructions_collapsed = 1;
890
    while ($instructions_collapsed)
891
    {
892
        update_labels();
893
        select_zero_page();
894
    }
895
    normalize_modes();
896
}
897
 
898
my %easy_dispatch = (
899
                  "MEMORY" => \&easy_flat,
900
                  "MEMORY-X" => \&easy_x,
901
                  "MEMORY-Y" => \&easy_y,
902
                  "UNKNOWN" => \&no_op
903
                  );
904
 
905
sub find_easy_addr_modes {
906
    if ($verbose) { print "Finding hardcoded addresses\n"; }
907
    walk(\%easy_dispatch);
908
}
909
 
910
my %init_dispatch = (
911
                  "SETPC" => \&init_setpc,
912
                  "CHECKPC" => \&init_checkpc,
913
                  "LABEL" => \&init_label,
914
                  "ADVANCE" => \&init_advance,
915
                  "UNKNOWN" => \&no_op
916
                  );
917
 
918
 
919
sub init_labels {
920
    if ($verbose) { print "Verifying label definitions\n"; }
921
    walk(\%init_dispatch);
922
}
923
 
924
my %check_dispatch = (
925
                   "SETPC" => \&no_op,
926
                   "CHECKPC" => \&no_op,
927
                   "LABEL" => \&no_op,
928
                   "ADVANCE" => \&no_op,
929
                   "IMPLIED" => \&no_op,
930
                   "BYTE" => \&check_data,
931
                   "WORD" => \&check_data,
932
                   "UNKNOWN" => \&check_inst
933
                   );
934
 
935
sub check_labels {
936
    if ($verbose) { print "Verifying all expressions\n"; }
937
    walk(\%check_dispatch);
938
}
939
 
940
my %update_dispatch = (
941
                    "SETPC" => \&update_setpc,
942
                    "CHECKPC" => \&no_op,
943
                    "LABEL" => \&update_setlabel,
944
                    "ADVANCE" => \&update_setpc,
945
                    "BYTE" => \&update_byte,
946
                    "WORD" => \&update_word,
947
                    "IMMEDIATE" => \&update_2,
948
                    "IMPLIED" => \&update_1,
949
                    "INDIRECT" => \&update_3,
950
                    "INDIRECT-X" => \&update_2,
951
                    "INDIRECT-Y" => \&update_2,
952
                    "MEMORY-X" => \&update_3,
953
                    "MEMORY-Y" => \&update_3,
954
                    "MEMORY" => \&update_3,
955
                    "ABSOLUTE-X" => \&update_3,
956
                    "ABSOLUTE-Y" => \&update_3,
957
                    "ABSOLUTE" => \&update_3,
958
                    "ZERO-PAGE-X" => \&update_2,
959
                    "ZERO-PAGE-Y" => \&update_2,
960
                    "ZERO-PAGE" => \&update_2,
961
                    "RELATIVE" => \&update_2
962
                    );
963
 
964
sub update_labels {
965
    if ($verbose) { print "Computing label locations\n"; }
966
    walk(\%update_dispatch);
967
}
968
 
969
my %zp_dispatch = (
970
                   "MEMORY" => \&zp_collapse,
971
                   "MEMORY-X" => \&zp_collapse_x,
972
                   "MEMORY-Y" => \&zp_collapse_y,
973
                   "UNKNOWN" => \&no_op
974
                   );
975
 
976
sub select_zero_page {
977
    $instructions_collapsed = 0;
978
    if ($verbose) { print "Searching for zero page instructions\n"; }
979
    walk(\%zp_dispatch);
980
    if ($verbose) { print "$instructions_collapsed instructions found.\n"; }
981
}
982
 
983
my %norm_dispatch = (
984
                    "MEMORY" => \&norm_mode,
985
                    "MEMORY-X" => \&norm_mode_x,
986
                    "MEMORY-Y" => \&norm_mode_y,
987
                    "UNKNOWN" => \&no_op
988
                    );
989
 
990
sub normalize_modes {
991
    if ($verbose) { print "Canonicalizing addressing modes.\n"; }
992
    walk(\%norm_dispatch);
993
}
994
 
995
sub easy_flat {
996
    my $node = shift;
997
    my (undef, undef, undef, $opcode, $arg) = @$node;
998
    if (has_mode($opcode, "rel")) {
999
        $$node[2] = "RELATIVE";
1000
    } elsif (hardcoded_arg($arg)) {
1001
        my $target = eval_arg($arg);
1002
        if (($target < 256) && has_mode($opcode, "zp")) {
1003
            $$node[2] = "ZERO-PAGE";
1004
        } else {
1005
            $$node[2] = "ABSOLUTE";
1006
        }
1007
    }
1008
}
1009
 
1010
sub easy_x {
1011
    my $node = shift;
1012
    my (undef, undef, undef, $opcode, $arg) = @$node;
1013
 
1014
    if (hardcoded_arg($arg)) {
1015
        my $target = eval_arg($arg);
1016
        if (($target < 256) && has_mode($opcode, "zpx")) {
1017
            $$node[2] = "ZERO-PAGE-X";
1018
        } else {
1019
            $$node[2] = "ABSOLUTE-X";
1020
        }
1021
    }
1022
}
1023
 
1024
sub easy_y {
1025
    my $node = shift;
1026
    my (undef, undef, undef, $opcode, $arg) = @$node;
1027
 
1028
    if (hardcoded_arg($arg)) {
1029
        my $target = eval_arg($arg);
1030
        if (($target < 256) && has_mode($opcode, "zpy")) {
1031
            $$node[2] = "ZERO-PAGE-Y";
1032
        } else {
1033
            $$node[2] = "ABSOLUTE-Y";
1034
        }
1035
    }
1036
}
1037
 
1038
sub no_op {
1039
}
1040
 
1041
sub init_advance {
1042
    my $node = shift;
1043
    my $target;
1044
    (undef, undef, undef, $target) = @$node;
1045
    if (!can_evaluate($target)) {
1046
        asmerror("Undefined or forward reference in .advance");
1047
    }
1048
}
1049
 
1050
sub init_setpc {
1051
    my $node = shift;
1052
    my $target;
1053
    (undef, undef, undef, $target) = @$node;
1054
    if (!can_evaluate($target)) {
1055
        asmerror("Undefined or forward reference on program counter assign");
1056
    }
1057
}
1058
 
1059
sub init_checkpc {
1060
    my $node = shift;
1061
    my $target;
1062
    (undef, undef, undef, $target) = @$node;
1063
    if (!can_evaluate($target)) {
1064
        asmerror("Undefined or forward reference on program counter check");
1065
    }
1066
}
1067
 
1068
sub init_label {
1069
    my $node = shift;
1070
    my (undef, undef, undef, $labelname, $labeltarget) = @$node;
1071
    if (!can_evaluate($labeltarget)) {
1072
        asmerror("Undefined or forward reference in .alias");
1073
    }
1074
    if (label_exists($labelname)) {
1075
        asmerror("Duplicate label definition: $labelname");
1076
    }
1077
    set_label($labelname, 0);
1078
}
1079
 
1080
sub check_inst {
1081
    my $node = shift;
1082
    my $arg = $$node[4];
1083
    if (!can_evaluate($arg)) {
1084
        my $badlabel = $$arg[2];
1085
        asmerror("Undefined label '$badlabel'");
1086
    }
1087
}
1088
 
1089
sub check_data {
1090
    my $node = shift;
1091
    my @data;
1092
    (undef, undef, undef, @data) = @$node;
1093
    for (@data) {
1094
        if (!can_evaluate($_)) {
1095
            my $badlabel = $$_[2];
1096
            asmerror("Undefined label '$badlabel'");
1097
        }
1098
    }
1099
}
1100
 
1101
sub update_setpc {
1102
    my $node = shift;
1103
    my (undef, undef, undef, $target) = @$node;
1104
    $pc = eval_arg($target);
1105
}
1106
 
1107
sub update_byte {
1108
    my $node = shift;
1109
    my (undef, undef, undef, @data) = @$node;
1110
    $pc += @data;
1111
}
1112
 
1113
sub update_word {
1114
    my $node = shift;
1115
    my (undef, undef, undef, @data) = @$node;
1116
    $pc += (@data*2);
1117
}
1118
 
1119
sub update_1 {
1120
    $pc++;
1121
}
1122
 
1123
sub update_2 {
1124
    $pc += 2;
1125
}
1126
 
1127
sub update_3 {
1128
    $pc += 3;
1129
}
1130
 
1131
sub update_setlabel {
1132
    my $node = shift;
1133
    my (undef, undef, undef, $labelname, $labeltarget) = @$node;
1134
 
1135
    set_label($labelname, eval_arg($labeltarget));
1136
}
1137
 
1138
sub zp_collapse {
1139
    my $node = shift;
1140
    my (undef, undef, undef, $opcode, $arg) = @$node;
1141
    my $target = eval_arg($arg);
1142
    if (($target < 256) && has_mode($opcode, "zp")) {
1143
        $instructions_collapsed++;
1144
        if ($trace) { print "--> Collapsed instruction at $currentfile:$linenum.\n"; }
1145
        $$node[2] = "ZERO-PAGE";
1146
    }
1147
}
1148
 
1149
sub zp_collapse_x {
1150
    my $node = shift;
1151
    my (undef, undef, undef, $opcode, $arg) = @$node;
1152
    my $target = eval_arg($arg);
1153
    if (($target < 256) && has_mode($opcode, "zpx")) {
1154
        $instructions_collapsed++;
1155
        if ($trace) { print "--> Collapsed instruction at $currentfile:$linenum.\n"; }
1156
        $$node[2] = "ZERO-PAGE-X";
1157
    }
1158
}
1159
 
1160
sub zp_collapse_y {
1161
    my $node = shift;
1162
    my (undef, undef, undef, $opcode, $arg) = @$node;
1163
    my $target = eval_arg($arg);
1164
    if (($target < 256) && has_mode($opcode, "zpy")) {
1165
        $instructions_collapsed++;
1166
        if ($trace) { print "--> Collapsed instruction at $currentfile:$linenum.\n"; }
1167
        $$node[2] = "ZERO-PAGE-Y";
1168
    }
1169
}
1170
 
1171
sub norm_mode {
1172
    my $node = shift;
1173
    $$node[2] = "ABSOLUTE";
1174
}
1175
 
1176
sub norm_mode_x {
1177
    my $node = shift;
1178
    $$node[2] = "ABSOLUTE-X";
1179
}
1180
 
1181
sub norm_mode_y {
1182
    my $node = shift;
1183
    $$node[2] = "ABSOLUTE-Y";
1184
}
1185
 
1186
# Assembler
1187
 
1188
my %assemble_dispatch = (
1189
                         "BYTE" => \&assemble_byte,
1190
                         "WORD" => \&assemble_word,
1191
                         "SETPC" => \&assemble_setpc,
1192
                         "CHECKPC" => \&assemble_checkpc,
1193
                         "ADVANCE" => \&assemble_advance,
1194
                         "IMMEDIATE" => \&assemble_inst_2,
1195
                         "IMPLIED" => \&assemble_inst_1,
1196
                         "INDIRECT" => \&assemble_inst_3,
1197
                         "INDIRECT-X" => \&assemble_inst_2,
1198
                         "INDIRECT-Y" => \&assemble_inst_2,
1199
                         "ABSOLUTE-X" => \&assemble_inst_3,
1200
                         "ABSOLUTE-Y" => \&assemble_inst_3,
1201
                         "ABSOLUTE" => \&assemble_inst_3,
1202
                         "ZERO-PAGE-X" => \&assemble_inst_2,
1203
                         "ZERO-PAGE-Y" => \&assemble_inst_2,
1204
                         "ZERO-PAGE" => \&assemble_inst_2,
1205
                         "RELATIVE" => \&assemble_inst_rel,
1206
                         "LABEL" => \&no_op
1207
                         );
1208
 
1209
my %addrmodes = (
1210
                  "IMMEDIATE" => "imm",
1211
                  "IMPLIED" => "imp",
1212
                  "INDIRECT" => "ind",
1213
                  "INDIRECT-X" => "indx",
1214
                  "INDIRECT-Y" => "indy",
1215
                  "ABSOLUTE-X" => "absx",
1216
                  "ABSOLUTE-Y" => "absy",
1217
                  "ABSOLUTE" => "abs",
1218
                  "ZERO-PAGE-X" => "zpx",
1219
                  "ZERO-PAGE-Y" => "zpy",
1220
                  "ZERO-PAGE" => "zp",
1221
                  "RELATIVE" => "rel"
1222
                  );
1223
 
1224
sub assemble {
1225
    if ($verbose) { print "Producing binary\n"; }
1226
    $codecount = $datacount = $fillercount = 0;
1227
    walk(\%assemble_dispatch);
1228
}
1229
 
1230
sub assemble_byte {
1231
    my @data;
1232
    my $node = shift;
1233
    (undef, undef, undef, @data) = @$node;
1234
    for (@data) {
1235
        my $arg = eval_arg($_);
1236
        if (($arg < 0) || ($arg > 0xff)) {
1237
            my $argstr = arg_as_string($arg);
1238
            asmerror "Constant $argstr out of range";
1239
        } else {
1240
            push @code, $arg;
1241
        }
1242
    }
1243
 
1244
    $pc += @data;
1245
    $datacount += @data;
1246
}
1247
 
1248
sub assemble_word {
1249
    my @data;
1250
    my $node = shift;
1251
    (undef, undef, undef, @data) = @$node;
1252
    for (@data) {
1253
        my $arg = eval_arg($_);
1254
        if (($arg < 0) || ($arg > 0xffff)) {
1255
            my $argstr = arg_as_string($arg);
1256
            asmerror "Constant $argstr out of range";
1257
        } else {
1258
            push @code, ($arg % 256), int($arg / 256);
1259
        }
1260
    }
1261
 
1262
    $pc += (2 * @data);
1263
    $datacount += (2 * @data);
1264
}
1265
 
1266
sub assemble_setpc {
1267
    my $node = shift;
1268
    my (undef, undef, undef, $target) = @$node;
1269
    $pc = eval_arg($target);
1270
}
1271
 
1272
sub assemble_checkpc {
1273
    my $node = shift;
1274
    my (undef, undef, undef, $arg) = @$node;
1275
    my $target = eval_arg($arg);
1276
 
1277
    if ($pc > $target) {
1278
        my $error = sprintf "Program counter assertion failed: \$%04x > \$%04x", $pc, $target;
1279
        asmerror $error;
1280
    }
1281
}
1282
 
1283
sub assemble_advance {
1284
    my $node = shift;
1285
    my (undef, undef, undef, $arg) = @$node;
1286
    my $target = eval_arg($arg);
1287
 
1288
    if ($target < $pc) {
1289
        asmerror "Attempted to .advance backwards, from $pc to $target.";
1290
    } else {
1291
        push @code, (0) x ($target-$pc);
1292
        $fillercount += $target-$pc;
1293
    }
1294
    $pc = $target;
1295
}
1296
 
1297
sub assemble_inst_1 {
1298
    my $node = shift;
1299
    my (undef, undef, $mode, $opcode) = @$node;
1300
 
1301
    my $modecode = $addrmodes{$mode};
1302
 
1303
    if(has_mode($opcode, $modecode)) {
1304
        push @code, get_opcode($opcode, $modecode);
1305
    } else {
1306
        asmerror ("$opcode does not have addressing mode $mode");
1307
    }
1308
    $pc++;
1309
    $codecount++;
1310
}
1311
 
1312
sub assemble_inst_2 {
1313
    my $node = shift;
1314
    my (undef, undef, $mode, $opcode, $arg) = @$node;
1315
    my $target = eval_arg($arg);
1316
    my $modecode = $addrmodes{$mode};
1317
 
1318
    if(has_mode($opcode, $modecode)) {
1319
        push @code, get_opcode($opcode, $modecode);
1320
        if (($target < 0) || ($target > 0xff)) {
1321
            asmerror("Argument out of range (0-\$FF)");
1322
        }
1323
        push @code, $target;
1324
    } else {
1325
        asmerror ("$opcode does not have addressing mode $mode");
1326
    }
1327
    $pc += 2;
1328
    $codecount+=2;
1329
}
1330
 
1331
sub assemble_inst_3 {
1332
    my $node = shift;
1333
    my (undef, undef, $mode, $opcode, $arg) = @$node;
1334
    my $target = eval_arg($arg);
1335
    my $modecode = $addrmodes{$mode};
1336
 
1337
    if(has_mode($opcode, $modecode)) {
1338
        push @code, get_opcode($opcode, $modecode);
1339
        if (($target < 0) || ($target > 0xffff)) {
1340
            asmerror("Argument out of range (0-\$FFFF)");
1341
        }
1342
        push @code, $target % 256, int($target / 256);
1343
    } else {
1344
        asmerror ("$opcode does not have addressing mode $mode");
1345
    }
1346
    $pc += 3;
1347
    $codecount+=3;
1348
}
1349
 
1350
sub assemble_inst_rel {
1351
    my $node = shift;
1352
    my (undef, undef, $mode, $opcode, $arg) = @$node;
1353
    my $target = eval_arg($arg);
1354
    my $modecode = $addrmodes{$mode};
1355
 
1356
    if(has_mode($opcode, $modecode)) {
1357
        push @code, get_opcode($opcode, $modecode);
1358
        if (($target < 0) || ($target > 0xffff)) {
1359
            asmerror("Argument out of range (0-\$FFFF)");
1360
        } else {
1361
            my $reltarget = $target - ($pc + 2);
1362
            if ($reltarget < -128 or $reltarget > 127) {
1363
                asmerror "Branch out of range";
1364
            }
1365
            push @code, ($reltarget < 0) ? 256 + $reltarget : $reltarget;
1366
        }
1367
    } else {
1368
        asmerror ("$opcode does not have addressing mode $mode");
1369
    }
1370
    $pc += 2;
1371
    $codecount+=2;
1372
}
1373
 
1374
my ($infile, $outfile);
1375
 
1376
 
1377
sub parse_args {
1378
    my $count = 0;
1379
    $verbose = $trace = $printbin = 0;
1380
    for (@ARGV) {
1381
        if ($_ eq "-v") {
1382
            $verbose = 1;
1383
        } elsif ($_ eq "-t") {
1384
            $trace = $verbose = 1;
1385
        } elsif ($_ eq "-b") {
1386
            $printbin = 1;
1387
        } elsif ($_ eq "-6510") {
1388
            %opcodes = (%opcodes, %opcodes_6510);
1389
            $instrs .= $instrs_6510;
1390
        } elsif ($_ =~ /^-/) {
1391
            usage();
1392
        } elsif ($count == 0) {
1393
            $infile = $_;
1394
            $count++;
1395
        } elsif ($count == 1) {
1396
            $outfile = $_;
1397
            $count++;
1398
        } else {
1399
            usage();
1400
        }
1401
    }
1402
    if ($count != 2) { usage(); }
1403
}
1404
 
1405
sub usage() {
1406
    print "\nUsage:\n    $0 [options] basefile outfile\n";
1407
    print "\n        basefile: Top-level source file";
1408
    print "\n        outfile: Binary output file\n\n";
1409
    print "\n    Options:\n";
1410
    print "\n        -v:    Verbose mode: give statistics and announce passes";
1411
    print "\n        -t:    Trace mode: list important, specific steps";
1412
    print "\n        -b:    Print binary as hex dump to screen before writing";
1413
    print "\n        -6510: Allow undocumented opcodes for the 6510 chip";
1414
    print "\n\n";
1415
    exit;
1416
}
1417
 
1418
sub write_file() {
1419
    if ($verbose) {
1420
        my $codesize = @code;
1421
        print "Writing $codesize bytes: $codecount code, $datacount data, $fillercount filler.\n";
1422
    }
1423
    open OUTPUT, ">$outfile" or die "Failed to create $outfile";
1424
    binmode OUTPUT;
1425
    print OUTPUT pack "c*", @code;
1426
}
1427
 
1428
sub print_binary() {
1429
    if ($printbin) {
1430
        my $count = 0;
1431
        foreach (@code) {
1432
            printf "%02x", $_;
1433
            $count = ($count+1) % 16;
1434
            if ($count == 8) { print '-'; }
1435
            elsif ($count == 0) { print "\n"; }
1436
            else { print ' '; }
1437
        }
1438
        print "\n";
1439
    }
1440
}
1441
 
1442
# Main routine.
1443
 
1444
my @passes = (\&find_easy_addr_modes, \&verify_IR, \&instruction_select,
1445
              \&assemble, \&print_binary, \&write_file);
1446
 
1447
parse_args();
1448
 
1449
parse($infile);
1450
 
1451
for (@passes) {
1452
    if (num_errors == 0) {
1453
        &$_();
1454
    }
1455
}
1456
 
1457
report_errors;
1458
 
1459
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.