The YACC1 cross assembler (RC/asm, YACC1 port)¶
How to assemble YACC1 programs on the Mac with software/assembler, what it accepts, what it produces, and its
traps. Written 2026-09-23 from the YACC1-D tree. Since 2026-09-25 the same dialect assembles on the machine too:
/BIN/ASM under Y1/OS, section 10.
Sources: software/assembler/README.md, asm.txt (Michael H. Riley's RC/asm 2.2 manual, the .def format and the
output formats), asm.c / asmcmds.c (option handling, the .prg/.img switch, the 2026-09-22 DS fix),
yacc1.def (the YACC1 instruction table), Makefile, rcasm.rc, software/compiler/README.md and
software/compiler/y1cc.py (the quirks the compiler had to work around), tests/assembler/{ledcount,brur,romcount,
romdiag}/, tests/compiler/run.py, firmware/monitor/monitor.asm (the largest source). Encodings quoted below
were checked with a scratch assembly on 2026-09-23.
1. What it is¶
RC/asm is a table-driven assembler: the program asm knows nothing about the YACC1; yacc1.def tells it, one
pattern per instruction, how a source line maps to bytes. The YACC1 port (2020-08 → 2021-07) changed the C sources
and added the table; upstream/rcasm-2.2/ is the untouched original. Build it with make in software/assembler
(also built by the top-level make). make check there re-assembles the monitor and BASIC and diffs the images
against the committed ones (the burned ROM); this is the tree's proof that the assembler and the table still
reproduce what is in the machine.
2. Invocation¶
cd <a folder holding NAME.asm, yacc1.def and rcasm.rc>
../software/assembler/asm NAME -d=yacc1 > NAME.lst
- The source name comes first, without
.asm, and-d=yacc1after it. The-dhandler inreadOptions()(asm.c) skips the argument that follows it, soasm -d=yacc1 NAMEloses the file name (firmware/README.md,Makefile).-d=yacc1means "readyacc1.def"; it is looked for in the current directory and then in the compiled-inDEF_DIR(asm.cRead_Def_File). rcasm.rcin the current directory is read before the command line, one option per line (asm.txt). The tree's copy insoftware/assembler/holds-l -x -h(listing, cross-reference, Intel hex). Every test runner creates one with just-h(tests/compiler/run.py,tests/assembler/romcount/run.py,os/Makefile:echo -h > rcasm.rc).- The listing (and the summary
N Lines assembled / N Errors / N Labels / Object Code:N bytes) goes to stdout, so redirect it toNAME.lst.tests/compiler/run.pygrepsObject Code:(\d+) bytesand^0 Errorsfrom it. - Options (
asm.txt,asm.c):-hIntel hex output;-llisting;-ssymbols;-xcross-reference;-tno trailer;-Sl/-Sasort the label reference by line/alphabetically;-rshow the matching rule (debugging a.def);-vversion;-Dsym=valdefine a preprocessor symbol;-d namethe definition file. - The assembler wants all three files in the working directory; the runners copy
yacc1.defand the source into a scratch folder first (tests/compiler/run.pyrun_one,tests/assembler/brur/README.md).
3. Output files¶
With -h the object file is NAME.img, Intel hex (asm.c: emucode='H' → .img), the format both emulators
load (emulator -f NAME.img, y1ucemu -f NAME.img), that tools/img2bin.py flattens for the ROM programmer and
for tools/p8xfs.py, and that the compiler tests use. Example (tests/assembler/romcount/romcount.img):
:10f00000a0f003190eff0270019161708061a4f0fd
:10f0100007010b0c7001617080611b20005b2ba24b
:09f02000f01d0cb0010ba0f0136f
:00000001ff
Each record is : + byte count + address + type (00 data, 01 end) + bytes + checksum; records hold up to 16
bytes and start a new line at every ORG and (since 2026-09-22) every DS.
Without -h the object file is NAME.prg in RC/asm's own text format (asm.c: emucode='I' → .prg):
:AAAA b b b ... lines and a *AAAA start-address line (asm.txt "Output file"; example
tests/assembler/ledcount/ledcount.prg: :0000 0e 00 70 01 61 b0 01 a0 00 02 / *0000). BACKLOG.md's planned
monitor loader reads that format. Nothing in the tree consumes .obj.
The listing (-l) is one line per source line: line number, address, the bytes, the source; then the label
table with -x. firmware/monitor/monitor.lst is the reference example (f05b: cmdloop: etc.).
4. Source syntax¶
- A line is
[label:] mnemonic [operands] [; comment]. Labels end with:; the whole line is upper-cased before matching (see quirks), socmdloop,CMDLOOPandCmdLoopare one label. - Numbers: decimal (
512), hex with a trailingH(0EFFH,0F000H— a leading digit is required, so$F000-style is not accepted and0F000His the form), characters in single quotes ('A').0xhex is not in the manual. - Operands are matched literally against the
.defpatterns:MVIW R1,0EFFH,OUTI P0,SWITCHLED,MOVRR R0,R7,LDIVR R3,5. Register names areR0..R7(the class also listsR8/R9, which silently produce wrong opcodes — see the ISA reference), portsP0..P9,PA..PF. - Expressions (
asm.txt, precedence high to low):.0/.1(low/high byte),* /,+ -,<< >>,&,| ^. Parentheses group:(label+4).0.HIGH expris aDBform (DB HIGH \W→ the high byte). Checked 2026-09-23:LDAI lab+4→0E 14withlab = 10H;LDAI lab*2→0E 20;DB (1234H).0→34;DB (1234H).1→12;DB HIGH 1234H→12.label+4.0does not work (software/assembler/README.md); write(label+4).0. The monitor writes OR as!:OUTI P0,(UARTA3!UARTCS)assembles to70 58= $18 | $40 (monitor.lstline 77).!is a YACC1-port addition to the tokenizer (support.cline 188, commentken add | &), not inasm.txt. - Preprocessor:
#DEFINE symbol value,#UNDEF,#IFDEF,#IFNDEF,#ELSE,#ENDIF(asm.txt); none of the tree's sources use them.
5. Directives (all defined in yacc1.def, not in the assembler)¶
| Directive | Effect | .def line |
|---|---|---|
ORG addr |
set the assembly address; flushes the hex record | ORG \W → \O1 |
END addr |
set the start address (the *AAAA line of a .prg; ignored by the hex loaders) |
END \W → \S1 |
EQU value / .EQU / equ |
define the label on this line as a constant (a register name or a word) | EQU \{regs}, EQU \W → \E1 |
DB byte[,byte...] / DB "text" |
emit bytes | DB \L → \1 |
DB HIGH word |
emit the high byte of a word | DB HIGH \W → hi(1) |
DW word[,word...] |
emit words, high byte first | DW \W → hi(1) lo(1); DW \M |
DS n |
reserve n bytes (nothing emitted; the next record starts after them); n may use backward labels (quirk 11) | DS \W → \B1 |
PUBLIC, EXTERN, LIB PROC, LIB ENDP |
RC/asm library markers; unused in the tree | \P \X \R \Q |
ORG takes the address in the assembler's expression syntax; the compiler emits it as decimal (ORG 12288).
END is optional in practice (ledcount.asm has none).
6. Quirks (verified, with the evidence)¶
- Every source line is upper-cased before matching, so labels are case-insensitive and
DB "text"emits upper-case text (DB "Ab"→41 42, checked). Emit strings as numeric bytes when case matters: the compiler writesDB 115,117,109,...(y1cc.py). The monitor's banner is written"YACC 2020: hello world "inmonitor.asmbut the listing shows the bytes4f 52 4c 44(ORLD,monitor.lstaround line 1160): the machine printsYACC 2020: HELLO WORLD. - Labels are at most 29 characters (
header.h:char labels[MAX_LABELS][30], "KEN CHANGED FROM 16 TO 30";y1cc.pyLABEL_MAX = 29; the compiler mangles long C names). A longer label used to overwrite memory; since 2026-09-24 it stops the assembly withERR - Label longer than 29 characters. The label table holds 8,191 labels (MAX_LABELS8192, was 1,000 with no check: 3,817 crashed it); a full table is the errorToo many labels. Also fixed 2026-09-24: a space or tab inside an operand (DW A, B) made the assembler loop forever. - A leading
-on a number is silently dropped:LDAI -1assembles to0E 01(checked). There are no negative numbers; write two's complement by hand (0FFH). - A
\Boperand over 255 is an error ("out of range");\Woperands over 65535 likewise. The compiler rejects integer literals over 65535 at its own level. DSused to corrupt the following bytes' addresses: bytes after aDSwere appended to the Intel-hex record that began before it and so loaded at the wrong address. Fixed 2026-09-22 inasmcmds.c(write_line()after\B, "flush the pending hex record so bytes after a DS start a new record");tools/patched_files.txtrecords it. The firmware never usesDS, so the burned images are unchanged (make checkproves it).- (Fixed 2026-09-23 in
yacc1.def, commit 69db58d.)IADDRwith an odd operand assembled to opcode $FF because its pattern wasFE|1 hi(1) lo(1)(|1ORs the operand into the opcode byte):IADDR 1235HgaveFF 12 35. The line isFE hi(1) lo(1)now:IADDR 1235H→FE 12 35(checked 2026-09-25). - Two labels differing only in case collide (quirk 1); the compiler uniquifies its labels for that reason.
- The
-doption consumes the following argument (section 2). - Missing
yacc1.defor a missing source name used to crash; since 2026-09-20 they print a message (asm.c). - There is no
.0/.1on a barelabel+4(write(label+4).0); the compiler always parenthesises. - Pass 1 used to read every label as 1 (
support.cfind_label), so anORG,DSorEQUwhose operand named a label placed what followed at the wrong address (DS 256-(y).0reserved 255 bytes). Since 2026-09-24 a label already defined earlier in the source (a backward reference) has its value in pass 1 too, andEQUsets its value in pass 1 (asmcmds.c); a forward reference is still 1 in pass 1, so keep label expressions inORG/DS/EQUbackward. y1cc--xisaaligns its variable page withzpad: DS (256-(zpad).0)&255(the label on the same line counts as defined). Every firmware image, bench image and test program assembles to the same bytes as before (tools/verify_firmware.py,tests/bench,tests/os,tests/compiler/passes.py). - A line of 100 characters or more aborts the assembler (found 2026-09-25):
trim()andparse()copy the text after the leading blanks / after the label, up to the comment, intochar tmp[100], and macOS's fortifiedstrcpystops the program (exit status 133, no output). y1cc's instructions are far shorter (its longest lines, 109 characters, are comments, cut before the copy); the native assembler (section 10) takes 254. BACKLOG has the fix.
7. The four worked examples in tests/assembler/¶
Each folder has a README with the byte listing and what a failure means; here is what each program teaches.
ledcount/ — the 16-byte switch-ROM program (10 bytes at $0000) for the Mem Switch bring-up card:
SWITCHLED: EQU 001H
ORG 0000H
LDAI 0 ; count = 0
loop: OUTI P0,SWITCHLED ; select the LED board
OUTA P1 ; LEDs = count
ADDI 1 ; count += 1
BR loop ; forever
Bytes 0E 00 70 01 61 B0 01 A0 00 02. It shows the I/O card's two-step port idiom (OUTI P0,select / OUTA P1),
an immediate ALU op and an absolute branch, and it uses only PC-relative fetches (no stack, no R2), which is why it
ran on 2026-09-21 before any of the register/stack findings mattered (docs/system/MACHINE.md). Its output is the
.prg text format (no -h).
brur/ — the test of BRUR Rn ($AD, added 2026-09-22): a direct BRUR R5, a jump through an address fetched
from a DW table (LDAVR R6 / MVARH R5 / INCR R6 / LDAVR R6 / MVARL R5 / BRUR R5: the two bytes of a big-endian word
into a register by hand), and a four-way dispatch indexed by a counter (MVRLA R7 / MVAT / MVRLA R6 / ADDT / MVARL R6,
the table kept inside one page so only the low byte is added). Expected console output ABC0123, then HALT. The
program starts at $3000 and carries the same boot stub the compiler's --boot emits at $F000 (BR 0F003H / MVIW
R1,0EFFH / JSR main / HALT): the first branch presents an A15-high address to end FORCE-ROM. Run:
emulator -x -f brur.img; tests/ucemu/run.py runs it on the microcode emulator too.
romcount/ — a 41-byte program burned in place of the monitor to prove the EPROM tool chain: mirror the
switches to the LED board and the TIL311 displays while the input line is low, then count up from the switch value
with a DECR R3 / MVRHA R3 / BRNZ delay loop and MVAT/MVTA keeping the count in TMP. It uses only R1, R3 and TMP
after the first build (R6/R7) exposed the missing second register card on the bench. Build chain: asm romcount
-d=yacc1 → romcount.img → python3 tools/img2bin.py romcount.img romcount.bin --base 0xE000 --end 0x10000 --fill
0xFF --size 8192 (offset $1000 in the 8K image = $F000). run.py re-assembles, compares both files with the
committed ones and runs the image on y1ucemu -s 0x25 -i 0/1 -L. It ran overnight on the machine 2026-09-22/23.
romdiag/ — the instruction check paced by the input switch: twelve stages, each waiting for the line to
change (BRINH wlo1 / BRINL whi2), each showing a byte on the LEDs through a show subroutine (JSR/RET, so
stage 1 also proves the stack): LDAI, MVRHA, MVRLA, DECR, BRNZ both ways, ADDI, MVAT/MVTA, a probe of
R7 (stage 9: FF means register card 1 is absent), the delay loop, then a free-running count. Expected LED sequence
25 ON AA 20 11 03 01 02 FF 33 20 55 00 01 02. run.py runs it on ucemu with -I 100000 flipping the line and
-R 2/-R 1 for both card configurations.
8. The .def file, so a new instruction can be added¶
yacc1.def (format from asm.txt, "Definition files"):
- Line 1 is a comment (
yacc1 -> Native). CLASS regs/CLASS portsdefine operand classes:text=valuepairs.\{regs}in a pattern matches one of them and yields its value.*starts the translations. Each instruction is two lines: the pattern (MNEMONIC<tab>operand pattern) and the construction line.- Pattern wildcards:
\Ba byte (0–255),\Wa word,\Na nibble,\{class}a class member,\Lthe rest of the line as a byte list (forDB),\Mas a word list (DW),\Da displacement (unused here). The k-th wildcard becomes replacement variable k. - Construction: hex digits build a byte in an accumulator; a blank outputs it and starts the next byte.
\ninserts variable n;hi(n)/lo(n)its high/low byte;|nORs variable n into the current byte without shifting,|n<vafter shifting it left v bits,|n>vright;&nnANDs the byte with hex nn;%swaps nibbles. Directive builders:\OnORG,\EnEQU,\SnEND,\BnDS,\P \Q \R \Xlibrary marks.
Reading the tree's entries with that key:
| Entry | Meaning |
|---|---|
LDAI \B / 0E \1 |
opcode $0E, then the byte |
BR \W / A0 hi(1) lo(1) |
$A0, high byte, low byte — every address operand is big-endian |
MVIB \{regs},\B / 10\|1 \2 |
$10 OR the register number in the opcode, then the byte |
MVIW \{regs},\W / 18\|1 hi(2) lo(2) |
$18 OR reg, then the word |
MOVRR \{regs},\{regs} / 0F \|2<4\|1 |
$0F, then (second reg << 4) OR first reg: source first, destination second |
POPR \{regs} / 08 \1<4&f0 |
$08, then reg << 4 |
PUSHR/JSRUR/BRUR \{regs} / 07 \1, 06 \1, AD \1 |
opcode, then the register number in the low nibble |
BRVR \{regs} / D8\|1 |
one byte, register in the opcode |
OUTI \{ports},\B / 70\|1 \2 |
$70 OR port, then the byte |
IADDR \W / FE hi(1) lo(1) |
$FE, then the word (it was FE\|1, quirk 6) |
To add an instruction: give it a number in software/opcodes.h (the generator, both emulators and the disassembler
include it), microcode in the generator (firmware/microcode/README.md: make regen, then load the EEPROM), a case
in software/emulator/main.c, and two lines here. make -C os then regenerates the native assemblers' tables and
the disassembler's (/BIN/DISASM, os/dis_optab.c by tools/gen_y1_distab.py, 2026-09-29), which also checks the
new opcode against software/opcodes.h; disasm prints it in this dialect, so disasm -s output assembles back. BRUR on 2026-09-22 is the worked example of exactly that
(tools/patched_files.txt: opcodes.h, branch.c, yacc1.def, main.c, test.hex); tests/assembler/brur
is its test. Keep the mnemonic order in the file irrelevant (patterns are matched, not searched in order) but avoid
a pattern that is a prefix of another with the same operand shape.
9. Putting the output to use¶
- Run on the interpreter:
software/emulator/emulator -x -f NAME.img(with a--boot-style stub at $F000 and aHALT), oremulator -m -f NAME.imgandG3000at the monitor prompt for a program assembled at $3000 that ends withRET. - Run on the microcode emulator:
software/ucemu/y1ucemu -x -m -f NAME.img(the ROM is needed for the console path). - Burn:
tools/img2bin.pywith--base 0xE000 --end 0x10000 --fill 0xFF --size 8192for a whole 28C64 image (tests/assembler/romcount/README.md); see TOOLCHAIN.md. - Put on a CF image for Y1/OS:
img2bin.py NAME.img NAME.bin --base 0x5000, thenp8xfs.py put disk.img NAME.bin --name /BIN/NAME --load 0x5000 --exec 0x5000(os/Makefile). - Loading RAM on the real machine has no path yet: the monitor has no hex loader (
BACKLOG.md:tools/monload.pythrough theEcommand is planned), so today a program reaches the machine only in the ROM socket or, once the card exists, from the CF card.
10. The native assembler, /BIN/ASM (2026-09-25; in YACC1 assembly since 2026-09-26)¶
The same dialect on the machine, in two versions that behave identically (man asm, man asmc, os/README.md
"asm"), written after RC/asm's own code (not the P8X assembler, whose syntax is another) so that they make the same
bytes of the same source, quirks and all:
/BIN/ASM |
/BIN/ASMC |
|
|---|---|---|
| source | os/commands-asm/asm.asm, hand-written YACC1 assembly (2026-09-26) |
os/commands/asm.c, compiled by y1cc (2026-09-25) |
| role | the assembler everything uses (cc's users, tests/native, the self-host) |
the specification: asm.asm was written from it routine by routine; any change of behaviour goes into both |
| instruction table | os/commands-asm/asmtab.inc (INCLUDEd) |
os/asm_optab.c (#included) |
| size | 9,239 bytes of program file | 13,186 bytes + 19,569 of data |
| symbol table | 20,292 bytes (about 1,660 of y1cc's labels) | 17,088 bytes (about 1,400) |
| speed (instructions, cat's 1,504 lines) | 2.75M: 1 min 29 s at 1 MHz | 12.8M: 6 min 55 s |
| needs | the 2026-09-24 microcode (ADDIW, SHL16; so does the native compiler) | any microcode (compiled without --xisa, as every /BIN command) |
asm HELLO.ASM HELLO: a program file (first to last address, gaps zero; load, exec = END or load)
asm -h HELLO.ASM HELLO.IMG: Intel hex, the text of `asm HELLO -d=yacc1` with -h on the Mac
asm -h MONITOR.ASM M.IMG an output name; a source name without '.' gets .ASM
- The table is
os/asm_optab.c(asm.c) andos/commands-asm/asmtab.inc(asm.asm), both generated fromyacc1.defby one run oftools/gen_y1_optab.py(theos/Makefilerules regenerate them;tests/asm/run.pyfails if either is stale). The assembly table is laid out for its reader: 64 mnemonic chains under the hashh = rotl8(h) + cfrom a seed the generator picks (at most 3 records a chain), each name followed by a 127, and a register or port class as a direct table by the name's second character. The generator reads the file asRead_Def_Filedoes and compiles each construction line with a copy ofTranslate()into a few operations (a run of hex digits is one constant), checked against a model ofTranslate()on random arguments; the patterns keep their order, grouped by mnemonic in 64 hash chains. A construction it cannot express (\N,\D,%,|n>s,OPTION 16BIT) stops the generator rather than producing a different assembler. To add an instruction, section 8 is all there is to do, thenmake -C os. - What is copied: the line read 254 bytes at a time (
fgets(buffer, 255));makeupper(upper case outside single quotes);parse(the comment at the first;outside quotes, the label before the first:outside quotes, with RC/asm's rule that a second kind of quote inside a quote takes over);WildMatch/Class_Match(first word literal, whitespace,\B/\Wup to a,or space outside quotes with theabs((int)v)range test, a class name as a prefix,\L/\Mthe rest);get_numwithbuildTokens/process_tokensin 32 bits, token records shifted exactly as RC/asm shifts them - including the tokens that slide in from beyond a parenthesis' range and the stale ones past the count, the dropped leading minus,.1as C's/ 256,12H3as hex 123; pass 1 reading a forward reference as 1 and EQU setting its value in pass 1; the Intel-hex records (16 bytes, a new record at ORG and DS); INCLUDE's file name taken from the raw line (two levels here). - Not supported (an error in /BIN/ASM, accepted by RC/asm; nothing in the tree uses them): MACRO/ENDM, PUBLIC,
EXTERN, LIB PROC/ENDP;
/on values beyond 16 bits; an EQU value outside -65535..65535 (labels are 16 bits + a sign); code or a label past $FFFF (RC/asm counts on into 17 bits); INCLUDE more than two deep (four OS handles: the source, two includes, the output). RC/asm's.prgoutput and its listing, cross-reference and symbol options are not there: the summary line gives the size, the label count and the addresses. - Other differences: errors are counted once per line (RC/asm can print the same undefined label for every pattern it tries) and name the line in its own file; after an error the output file is deleted (RC/asm writes it anyway); a missing INCLUDE file is an error (RC/asm prints a message and goes on); lines of 100-254 characters work (quirk 12).
- Limits: the symbol table is 20,292 bytes in
/BIN/ASM(17,088 in/BIN/ASMC: 16,640 until 2026-09-25, when cc8's buffered I/O outgrew it), 5 + the name's length a label (y1cc's labels average 7 characters: about 1,660 labels, 1,400 in ASMC; the biggest compiler pass, cc8, has 1,383 in 16,925 bytes, 83% of ASM's table); 29 characters a label, 254 a line, 45 tokens an expression, 32 characters a token; source files up to 16M (Y1/OS's positions are 24 bits since 2026-09-25); the program file needs the code to go up in address (else-h). A source that fills ASMC's table can fill ASM's too a little later, with its own error on its own line: y1cc.c itself compiled (4,186 labels) is refused by both (tests/asm). The output takes Y1/OS's one write handle, so neither works inside a>or a pipe ("cannot create"). - asm.asm (
os/commands-asm/asm.asm, 2026-09-26; its header has the conventions): every routine names the asm.c function it implements. What makes it fast: the source is read a sector at a time with READ straight into its buffer and each line scanned where it lies, 8 instructions a byte, with a translation table that upper-cases and maps the charactersparse()must see to 0, and two 0 sentinels (the end of the data read, the line's 254th byte), so the loop tests nothing else; a line that runs past the data is moved into the page before the buffer, so the raw line (for the error messages and INCLUDE's name) is always the bytes in the buffer; a comment is scanned only for its line feed; leading blanks are skipped (put intolnonly when the line has a label). Tokens are cut in place inln(a token with a blank inside - RC/asm skips blanks inside a token - sends the expression to asm.c's copying tokenizer, which writes the same records from the start); the records are asm.c's, 8 bytes apart, shifted exactly as there, stale ones included;(label).0and(label).1, y1cc's--xisapage offsets, are computed at once when the five records are the usual ones, with the same records left behind (the general algorithm runs when the stale record after them is an operator:tests/asm/src/err_stale.asm). Labels: 256 chains oflen|flags next value namerecords, the name stored backwards so the compare starts where y1cc's labels differ. An INCLUDE keeps the outer file's position and SEEKs back to it. It uses ADDIW and SHL16 (the 2026-09-24 microcode), never R2, and the shell's stack (~20 bytes deep); the page-aligned tables and buffers are at $C400-$CFFF, its variables after the code, the symbol table between. - Speed (instructions on the instruction-level emulator, program file; the monitor to hex; each figure includes the OS's and the ROM's work for the file I/O, 15-25%; at 1 MHz with 32.4 clocks an instruction):
| source | lines | bytes | /BIN/ASMC |
/BIN/ASM |
faster | an instruction a byte | at 1 MHz |
|---|---|---|---|---|---|---|---|
| hello (y1cc) | 82 | 1,652 | 0.88M | 0.20M | 4.3x | 124 | 28 s -> 6 s |
| cat (y1cc) | 1,504 | 27,706 | 12.8M | 2.75M | 4.7x | 99 | 6 min 55 s -> 1 min 29 s |
| the ROM monitor (-h) | 1,972 | 46,179 | 17.3M | 3.96M | 4.4x | 86 | 9 min 20 s -> 2 min 08 s |
cc4 (y1cc --xisa) |
3,404 | 65,973 | 37.8M | 7.18M | 5.3x | 109 | 20 min 25 s -> 3 min 52 s |
cc8 (y1cc --xisa, the biggest pass) |
11,897 | 235,482 | 129.7M | 26.4M | 4.9x | 112 | 1 h 10 min -> 14 min 15 s |
The microcode emulator (cat, each in a session of its own less an empty one): ASM 2.85M instructions and 83.9M
clocks (29.4 an instruction: hand-written code has fewer of the long instructions), ASMC 12.9M and 435.5M
(33.7): 5.2x in real time, no bus fights. A native compile (tests/native/run.py's 27 programs): assembling
217M -> 46.5M instructions, compile and assemble 687M -> 517M (6 h 11 min -> 4 h 39 min at 1 MHz). The self-host
(tests/native/selfhost.py, one stage): assembling 764M -> 153M, the eleven C programs 21 h 17 min -> 15 h 47 min,
15 h 52 min with asm.asm's own assembly (software/compiler/README.md). Where ASM's time goes now
(tests/native/profile.py): reading lines 20%, the ROM's sector reads 12%, the mnemonic 12%, tokens 15%,
matching 10%, labels 9%, the operations 8%, output 5%.
- Tests: tests/asm/run.py (in make check, make asm-test) builds asm.c for the Mac with the YACC1's
integer types against an emulation of the Y1/OS syscalls (tests/asm/host_asm.c, host_sys.c) and compares it
with RC/asm on every source in the tree; then it runs /BIN/ASM (asm.asm) under Y1/OS on the instruction-level
emulator on the same 336 sources, with -h and to a program file (disks of 72 sources, the sessions in parallel,
20 seconds), and its messages, summary and files must be what asm.c gives for the same command line: 334
identical, 2 refused by both where ASMC's table is full (--uc: 27 of them on the microcode emulator too, 2
minutes) - each y1cc compile of tests/compiler/corpus.py plain and --xisa (262,
the nine compiler passes and asm.c itself among them), the monitor and BASIC with their candidates, monnew,
tests/assembler, isa.asm, y1os.asm with its INCLUDE, and tests/asm/src (quirks.asm: the corners above;
six sources both must refuse): 283 identical in hex and as program files, 1 identical in hex (yacc1test.asm
goes back in address: its program file is refused, as it must be), 13 refused by both. --target
(tests/asm/target.py) runs asm under Y1/OS on both emulators: y1cc programs assembled and run, the monitor
assembled to firmware/monitor/monitor.img, isa.asm, the quirks with INCLUDE, compiler pass cc4, an error;
every file written compared with RC/asm's output.