7 ! This program is an EM interpreter for the Z80.
8 ! Register pair bc is used to hold lb.
9 ! Register ix is used to hold the EM program counter.
10 ! The interpreter assumes 16-bit words and 16-bit pointers.
15 zone = 8 ! size of subroutine call block (address + old lb)
16 bdos = 5 ! standard entry into I/O-routines
18 fcb = 0x5c ! file descriptor of EM-1 file (5C hex)
37 RAW=0 !0 for cooked,1 for raw io
62 !--------------------------- Initialization ---------------------------
64 jp init ! 3 byte instruction.
66 !------------------------- MAIN DISPATCH ------------------------------
68 ! must be put in a suitable place in memory to reduce memory usage
69 ! must be put on a page boundary
72 .data1 0 ! fourth byte
73 dispat = . - 4 ! base of dispatch table
331 !----------------- END OF MAIN DISPATCH -------------------------------
337 ld sp,(bdos+1) ! address of fbase
344 ld sp,(bdos+1) ! address of fbase
358 jr nz,bademfile ! no file
360 ld hl,0x90 ! start of 2nd half of header
361 ld bc,10 ! we copy only first 5 words
363 ld de,(ntext) ! size of program text in bytes
367 ld sp,hl ! save space for program
368 ld (pb),hl ! set procedure base
387 ! now program text has been read,so start read-
388 ld iy,0 ! ing data descriptors, (nextp) (was hl) is
389 ld ix,eb ! pointer into DMA,ix is pointer into global
390 ! data area,iy is #bytes pushed in last instr (used for repeat)
391 rddata: ld hl,(ndata)
394 jr z,prdes ! no data left
397 call getb ! read 1 byte (here:init type) into register c
405 jr z,5f ! size of block is zero, so no work
427 5: ld iy,0 ! now last instruction = repeat = type 0
429 2: ld b,c ! here other types come
430 jr nz,2f ! Z-flag was (re-)set when decrementing c
431 call getb ! uninitialized words, fetch #words
438 2: call getb ! remaining types, first fetch #bytes/words
442 jp p,bademfile ! floats are not accepted,nor are illegal types
448 2: ld iy,0 ! initialized bytes, simply copy from EM-1 file
466 4: ld (ntext),hl ! ntext is used here to hold base address of
467 ld iy,0 ! correct type: data,instr or 0 (plain numbers)
473 ex de,hl ! save e into l
513 1: .ascii 'load file error\r\n$'
515 ! now all data has been read,so on with the procedure descriptors
517 ld (hp),ix ! initialize heap pointer
523 add hl,hl ! 4 bytes per proc-descriptor
525 ld sp,hl ! save space for procedure descriptors
528 ld (pd),hl ! initialize base
551 ld de,(entry) ! get ready for start of program
552 ld ix,0 ! reta, jumping here will stop execution
558 jr cal ! call EM-1 main program
567 getb: push hl ! getb reads 1 byte in register c from standard
569 ld a,(nextp) ! DMA buffer and refills if necessary
589 !------------------------- Main loop of the interpreter ---------------
592 loop: ld e,(ix) ! e = opcode byte
593 inc ix ! advance EM program counter to next byte
594 ld hl,dispat ! hl = address of dispatching table
597 add hl,de ! compute address of routine for this opcode
598 add hl,de ! hl = address of routine to dispatch to
599 ld d,(hl) ! e = low byte of routine address
600 inc hl ! hl now points to 2nd byte of routine address
601 ld h,(hl) ! h = high byte of routine address
602 ld l,d ! hl = address of routine
604 jp (hl) ! go execute the routine
606 loop1: ld e,(ix) ! e = opcode byte
607 inc ix ! advance EM program counter to next byte
608 ld hl,dispat1 ! hl = address of dispatching table
611 add hl,de ! compute address of routine for this opcode
612 add hl,de ! hl = address of routine to dispatch to
613 ld d,(hl) ! e = low byte of routine address
614 inc hl ! hl now points to 2nd byte of routine address
615 ld h,(hl) ! h = high byte of routine address
616 ld l,d ! hl = address of routine
618 jp (hl) ! go execute the routine
620 loop2: ld e,(ix) ! e = opcode byte
621 inc ix ! advance EM program counter to next byte
622 ld hl,dispat2 ! hl = address of dispatching table
625 add hl,de ! compute address of routine for this opcode
626 add hl,de ! hl = address of routine to dispatch to
627 ld d,(hl) ! e = low byte of routine address
628 inc hl ! hl now points to 2nd byte of routine address
629 ld h,(hl) ! h = high byte of routine address
630 ld l,d ! hl = address of routine
632 jp (hl) ! go execute the routine
634 ! Note that d and a are both still 0, and the carry bit is cleared.
635 ! The execution routines make heavy use of these properties.
636 ! The reason that the carry bit is cleared is a little subtle, since the
637 ! two instructions add hl,de affect it. However, since dispat is being
638 ! added twice a number < 256, no carry can occur.
642 !---------------------- Routines to compute addresses of locals -------
644 ! There are four addressing routines, corresponding to four ways the
645 ! offset can be represented:
646 ! loml: 16-bit offset. Codes 1-32767 mean offsets -2 to -65534 bytes
647 ! loms: 8-bit offset. Codes 1-255 mean offsets -2 to -510 bytes
648 ! lopl: 16-bit offset. Codes 0-32767 mean offsets 0 to +65534 bytes
649 ! lops: 8-bit offset. Codes 0-255 mean offsets 0 to +510 bytes
651 loml: ld d,(ix) ! loml is for 16-bit offsets with implied minus
656 1: ld e,(ix) ! loms is for 8-bit offsets with implied minus
661 add hl,de ! hl now equals lb - byte offset
664 lopl: ld d,(ix) ! lopl is for 16-bit offsets >= 0
667 ld l,(ix) ! fetch low order byte of offset
669 add hl,hl ! convert offset to bytes
670 ld de,zone ! to account of return address zone
672 add hl,bc ! hl now equals lb - byte offset
677 !---------------------------- LOADS -----------------------------------
681 ld d,(ix) ! loc with 16-bit offset
683 loc.s0: ld e,(ix) ! loc with 8-bit offset
685 loc.0: loc.1: loc.2: loc.3: loc.4: loc.5: loc.6: loc.7:
686 loc.8: loc.9: loc.10: loc.11: loc.12: loc.13: loc.14: loc.15:
687 loc.16: loc.17: loc.18: loc.19: loc.20: loc.21: loc.22: loc.23:
688 loc.24: loc.25: loc.26: loc.27: loc.28: loc.29: loc.30: loc.31:
697 loc.sm1:dec d ! for constants -256...-1
732 lol.0: lol.1: lol.2: lol.3: lol.4: lol.5: lol.6:
733 ld hl,-b_lolp-b_lolp+zone
739 lol.m2: lol.m4: lol.m6: lol.m8: lol.m10: lol.m12: lol.m14: lol.m16:
742 xor a ! clear carry bit
744 add hl,bc ! hl = lb - byte offset
782 lof.2: lof.4: lof.6: lof.8:
783 ld hl,-b_lof-b_lof ! assume lof 1 means stack +2, not -2
864 ld hl,-b_lil-b_lil+zone
911 .assert [ .-2b-zone] == 0
940 .assert [ zone/256] == 0
1054 ld e,(hl) ! here the 1-byte case is caught
1072 loi.1: loi.2: loi.4: loi.6: loi.8:
1075 adc hl,de ! again we use that the carry is cleared
1077 inc hl ! in case loi.0 object size is 1 byte!
1088 ! ------------------------------ STORES --------------------------------
1099 stl.m2: stl.m4: stl.m6: stl.m8: stl.m10:
1142 stf.2: stf.4: stf.6:
1191 pop de ! here the 1-byte case is caught
1207 sti.1: sti.2: sti.4: sti.6: sti.8:
1210 adc hl,de ! again we use that the carry is cleared
1212 inc hl ! in case sti.0 object size is 1 byte!
1258 !------------------------- SINGLE PRECISION ARITHMETIC ---------------
1260 ! ADI, ADP, ADS, ADU
1268 ld d,(ix) ! I guess a routine chk24.l could do this job
1278 ads.2: adi.2: adu.2:
1304 ! SBI, SBP, SBS, SBU (but what is SBP?)
1347 ! MLI, MLU Johan version
1857 ! this routine is used to call indirectly
1858 ! a routine for either 2 or 4 byte operation
1859 ! ( e.g. mli.2 or mli.4)
1860 ! de contains 2 or 4
1861 ! iy points to a descriptor containing
1862 ! the addresses of both routines
1863 pop iy ! address of descriptor
1864 ld a,d ! high byte must be 0
1871 inc iy ! points to word containing
1872 ! address of 4 byte routine
1880 !--------------------- INCREMENT, DECREMENT, ZERO ----------------------
1883 inl.m2: inl.m4: inl.m6:
2033 ! ------------------------- CONVERT GROUP ------------------------------
2039 sbc hl,de ! hl and de can only have values 2 or 4, that's
2040 ! why a single subtract can split the 3 cases
2041 jr z,loop ! equal, so do nothing
2043 3: pop hl ! smaller, so shrink size from double to single
2046 2: pop hl ! larger, so expand (for cii with sign extend)
2066 ! ------------------------------ SETS ---------------------------------
2160 ! ------------------------- LOGICAL GROUP -----------------------------
2285 ! ------------------------- COMPARE GROUP ------------------------------
2293 xor d ! check sign bit to catch overflow with subtract
2297 1: xor d ! now a equals (original) h again
2299 set 0,l ! to catch case hl=0>de bit 0 is set explicitly
2332 ld (retarea),hl ! save new sp-1
2412 ! TLT, TLE, TEQ, TNE, TGE, TGT
2484 ! ------------------------- BRANCH GROUP -------------------------------
2486 ! BLT, BLE, BEQ, BNE, BGE, BGT, BRA
2515 bgo: pop ix ! take branch
2548 pop de ! keep stack clean, so dump unused jump address
2558 pop de ! keep stack clean, so dump unused jump address
2567 xor d ! check sign bit to catch overflow with subtract
2571 1: xor d ! now a equals (original) h again
2573 pop de ! keep stack clean, so dump unused jump address
2586 ! ZLT, ZLE, ZEQ, ZNE, ZGE, ZGT
2669 ! ------------------- ARRAY REFERENCE GROUP ---------------------------
2677 aarint: pop iy ! descriptor
2678 ex (sp),hl ! save return address and hl:=index
2680 ld d,(iy+1) ! de := lwb
2690 ld d,(iy+3) ! de := upb - lwb
2706 push iy ! exchange base address and return address
2737 ! --------------------- PROCEDURE CALL/RETURN --------------------------
2741 cal.1: cal.2: cal.3: cal.4: cal.5: cal.6: cal.7: cal.8:
2742 cal.9: cal.10: cal.11: cal.12: cal.13: cal.14: cal.15: cal.16:
2743 cal.17: cal.18: cal.19: cal.20: cal.21: cal.22: cal.23: cal.24:
2744 cal.25: cal.26: cal.27: cal.28:
2754 cal: push ix ! entry point for main program of interpreter
2760 ! temporary tracing facility
2761 ! NOP it if you don't want it
2767 ! end of temporary tracing
2804 jp p,eilsize ! only result sizes <= 8 are allowed
2822 lfr.2: ld hl,(retarea)
2839 jp p,eilsize ! only result sizes <= 8 bytes are allowed
2858 push ix ! check to see if reta = boot (= 0)
2862 jr nz,loop ! not done yet
2873 ! ------------------------- MISCELLANEOUS -----------------------------
2914 ld d,(iy+1) ! file name
2916 ld h,(iy+3) ! lineno
2927 2: .ascii 'error 0xxxxxx\r\n$'
2930 ! prints lineno (hl) and filename (de)
2956 2: .ascii 'line 0xxxxxx in $'
2965 ! changed into output routine to print linenumber
2966 ! in octal (6 digits)
2981 1: .ascii 'test 0xxxxxx 0xxxxxx\r\n$'
3079 asp.2: asp.4: asp.6: asp.8: asp.10:
3097 !! temporary version while bug in cem remains
3114 ! end of temporary piece
3246 xor d ! check sign bit to catch overflow with subtract
3250 1: xor d ! now a equals (original) h again
3257 xor d ! check sign bit to catch overflow with subtract
3261 1: xor d ! now a equals (original) h again
3267 lim.z: ld hl,(ignmask)
3314 ! Floating point calling routines
3369 ld iy,fpadd ! routine to call
3379 ld iy,fpsub ! routine to call
3389 ld iy,fpmult ! routine to call
3399 ld iy,fpdiv ! routine to call
3600 ! list of not yet implemented instructions
3604 unimpld: ! used in dispatch table to
3605 ! catch unimplemented instructions
3649 ! monitor instruction
3650 ! a small collection of UNIX system calls implemented under CP/M
3724 ! Structure of filearea maintained by this implementation
3725 ! First iobuffer of 128 bytes
3726 ! Then the fcb area of 36 bytes
3727 ! The number of bytes left in the buffer, 1 byte
3728 ! The iopointer into the buffer, 2 bytes
3729 ! The openflag 0 unused, 1 reading, 2 writing, 1 byte
3730 ! The filedescriptor starting at 3, 1 byte
3731 ! The number of CTRL-Zs that have been absorbed, 1 byte
3732 ! The byte read after a sequence of CTRL-Zs, 1 byte
3735 filesize=128+36+1+2+1+1+1+1
3737 filefcb=0 ! pointers point to fcb
3746 .assert [ filefcb] == 0
3748 0: .space maxfiles*filesize
3756 argv: .space 40 ! not more than 20 args
3758 ttymode:.data1 9,9,8,21;.data2 06310+RAW*040 ! raw = 040
3826 pop de ! system call number
3829 jr nz,unimpld ! too big
3831 and 0300 ! only 64 system calls
3909 ! searches for a free filestructure
3910 ! returns pointer in iy, 0 if not found
3962 ld (iy+iopointer+1),h
3990 ld h,(iy+iopointer+1)
3995 ld (iy+iopointer+1),h
4004 ld h,(iy+iopointer+1)
4010 ld (iy+iopointer+1),h
4024 ld (iy+iopointer+1),h
4067 ! parses file name pointed to by hl and fills in fcb
4068 ! of the file pointed to by iy.
4069 ! recognizes filenames as complicated as 'b:file.zot'
4070 ! and as simple as 'x'
4077 ld b,36 ! sizeof fcb
4085 cp ':' ! drive specified ?
4092 inc a ! now 1<= a <= 16
4095 ld b,8 ! filename maximum of 8 characters
4105 and 0177 ! no parity
4108 and 0337 ! UPPER case
4124 ret ! filenames longer than 8 are truncated
4125 2: ld a,' ' ! fill with spaces
4129 3: ld b,3 ! length of extension
4277 pop hl ! file mode, not used under CP/M
4295 pop hl ! remove params
4327 ld (iy+position+1),a
4336 ld (iy+iopointer+1),h
4365 !end of no echo interface
4376 ld hl,sibuf+1 ! read from console assumed
4379 dec hl ! go read console line
4380 ld (hl),80 ! max line length
4393 ld (siptr),hl ! ready for transfer
4406 inc b ! bytes remaining
4408 pop de ! bytes wanted (probably 512)
4410 ld iy,(siptr) ! copy from
4411 xor a ! find out minimum of ramaining and wanted
4413 jr nz,3f ! more than 255 wanted (forget that)
4416 jp m,3f ! not enough remaining
4450 ! warning: this may not work if zcount overflows
4499 pop hl ! buffer address
4505 ld b,e ! count now in 'db'
4582 ld hl,12345 ! nice number
4632 dispat1: ! base for escaped opcodes
4633 .data2 aar.l, aar.z, adf.l, adf.z, adi.l, adi.z, ads.l, ads.z
4634 .data2 adu.l, adu.z, and.l, and.z, asp.l, ass.l, ass.z, bge.l
4635 .data2 bgt.l, ble.l, blm.l, bls.l, bls.z, blt.l, bne.l, cai.z
4636 .data2 cal.l, cfi.z, cfu.z, ciu.z, cmf.l, cmf.z, cmi.l, cmi.z
4637 .data2 cms.l, cms.z, cmu.l, cmu.z, com.l, com.z, csa.l, csa.z
4638 .data2 csb.l, csb.z, cuf.z, cui.z, cuu.z, dee.l, del.p, del.n
4639 .data2 dup.l, dus.l, dus.z, dvf.l, dvf.z, dvi.l, dvi.z, dvu.l
4640 .data2 dvu.z, fef.l, fef.z, fif.l, fif.z, inl.p, inl.n, inn.l
4641 .data2 inn.z, ior.l, ior.z, lar.l, lar.z, ldc.l, ldf.l, ldl.p
4642 .data2 ldl.n, lfr.l, lil.p, lil.n, lim.z, los.l, los.z, lor.s0
4643 .data2 lpi.l, lxa.l, lxl.l, mlf.l, mlf.z, mli.l, mli.z, mlu.l
4644 .data2 mlu.z, mon.z, ngf.l, ngf.z, ngi.l, ngi.z, nop.z, rck.l
4645 .data2 rck.z, ret.l, rmi.l, rmi.z, rmu.l, rmu.z, rol.l, rol.z
4646 .data2 ror.l, ror.z, rtt.z, sar.l, sar.z, sbf.l, sbf.z, sbi.l
4647 .data2 sbi.z, sbs.l, sbs.z, sbu.l, sbu.z, sde.l, sdf.l, sdl.p
4648 .data2 sdl.n, set.l, set.z, sig.z, sil.p, sil.n, sim.z, sli.l
4649 .data2 sli.z, slu.l, slu.z, sri.l, sri.z, sru.l, sru.z, sti.l
4650 .data2 sts.l, sts.z, str.s0, tge.z, tle.z, trp.z, xor.l, xor.z
4651 .data2 zer.l, zer.z, zge.l, zgt.l, zle.l, zlt.l, zne.l, zrf.l
4652 .data2 zrf.z, zrl.p, dch.z, exg.s0, exg.l, exg.z, lpb.z
4654 dispat2: ! base for 4 byte offsets
4658 ignmask: .data2 0 ! ignore mask (variable)
4659 retarea: .data2 0 ! base of buffer for result values (max 8 bytes)