home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Elysian Archive
/
AmigaElysianArchive.iso
/
emulate
/
simcpm.lha
/
simcpm1.asm
< prev
next >
Wrap
Assembly Source File
|
1989-04-25
|
101KB
|
3,265 lines
vermaj equ $02 ;Major version number
vermin equ $30 ;Minor version number
revyear equ $89 ;Year last assembled
revmonth equ $01 ;Month last assembled
revday equ $09 ;Day last assembled
*************************************************************************
* *
* *
* Z-80 Simulator for MC68000 *
* *
* With CP/M 2.2 call support, and optional tracing *
* *
* *
* Amiga version 2.3 (December 1988, Charlie Gibbs): *
* *
* Incorporated modifications by Willi Kusche *
* (his version 2.1, June 29, 1988): *
* Register definitions split into "ecpmdefs.i" *
* Improved macro usage *
* Correction of errors in Z-80 flag setting routines *
* Incorporated modified trace dump routines *
* Allow changing of trace addresses during execution *
* Added the following BDOS calls: *
* 27 - get allocation vector *
* (using a fake allocation vector) *
* 31 - get disk parameter block *
* (using a fake disk parameter block) *
* *
* Modifications inspired by Ulf Nordquist's CP/M emulator: *
* - BDOS calls 17 and 18 (search for first file and *
* search for next file) now put file size information *
* into the FCB. *
* - BDOS call 27 (get allocation vector) now builds a fake *
* allocation vector based on actual volume information. *
* *
* The file handle table now also contains the first 12 bytes *
* of the FCB to which the handle corresponds. The FCB is *
* no longer modified with a sequence number. *
* *
* BDOS call 20 (sequential read) now returns 01h in the *
* accumulator when end of file is reached, rather than 0FFh *
* as is suggested by CP/M manuals. Some utilities (such as *
* ASM.COM) assume 0FFh indicates an I/O error. *
* *
* A new INCLUDE file, options.i, has been added. This file *
* contains EQUate statements which allow you to set options *
* that cannot be easily set at execution time. Current options *
* allow inclusion of Heath/Zenith 19 escape code translation, *
* and support of 68010 and higher processors by replacing all *
* MOVE SR instructions by MOVE CCR. *
* *
* *
* Amiga version 2.2 (June 1988, Charlie Gibbs): *
* *
* BDOS call 23 (rename) was not working properly. *
* *
* BIOS and BDOS call messages (for tracing or errors) *
* now indicate that the call number is in hexadecimal. *
* *
* The following BDOS calls have now been implemented: *
* 35 - get file end address *
* 36 - get direct address *
* *
* *
* Amiga version 2.1 (May 1988, Charlie Gibbs): *
* *
* If a reference is made to any drive other than A:, SimCPM *
* will insert the string CPMx: ahead of any file name it uses. *
* For instance, if a program tries to open MYFILE.DAT on drive *
* B:, SimCPM will try to open CPMB:MYFILE.DAT. You can ASSIGN *
* these simulated drives anywhere you want. Drive A: will *
* always go to the current directory, unless the user number *
* is not zero. If you specify any user number other than zero, *
* it will be included, e.g. if the above example were opened *
* under user 1, SimCPM will look for CPMB01:MYFILE.DAT. *
* If the file were on drive A under user 1, SimCPM will look *
* for CPMA01:MYFILE.DAT. *
* *
* If the file handle table overflowed, the file which caused *
* the overflow has already been opened. It was not being *
* closed again. This file is now closed before the emulated *
* program is aborted. *
* *
* If an emulated program failed to close all the files it opened, *
* the emulator would close these files. However, it did not *
* clear the open flag for any such files. If another program *
* was then run, when it terminated the emulator would try to *
* close these same files again. This bug has been corrected. *
* *
* Z-80 instructions not supported by version 2.0 have been *
* implemented as follows: *
* LD I,A moves to a dummy interrupt register. *
* LD A,I moves from a dummy interrupt register. *
* LD R,A is ignored. *
* LD A,R moves a random value from the clock into *
* the low-order 7 bits of the accumulator. *
* IM1 and IM2 are ignored. *
* *
* The -z command-line switch has been implemented. *
* It sets instruction tracing, just like the -t switch, *
* but causes Z-80 instruction mnemonics to be used. *
* If this switch is not set, 8080 mnemonics are used. *
* *
* BDOS call 32 (get or set user code) is now fully implemented. *
* *
* The following BDOS calls have now been implemented: *
* 13 - reset all drives *
* 24 - get active drive map *
* 28 - protect drive *
* 29 - get read-only drive map *
* *
* The USER and SAVE built-in commands have been implemented. *
* These are the only build-in commands provided, since commands *
* such as DIR or TYPE can be handled through a CLI window. *
* *
* *
* Amiga version 2.0 (January 1988, Charlie Gibbs): *
* *
* Added all Z-80 instructions except the following: *
* LD A,I *
* LD I,A *
* LD A,R *
* LD R,A *
* IM1 *
* IM2 *
* *
* Added -t flag for tracing without needing *
* a separate version to maintain speed. *
* *
* Added the following BDOS calls: *
* 17 - search for first file *
* 18 - search for next file *
* 24 - get active drive map *
* (assumes only drive A: is active) *
* 28 - protect drive (ignored) *
* 29 - get read-only map *
* (assumes no drives are protected) *
* 32 - get or set user code *
* (always gets user code zero, any attempt *
* to set to a user code other than zero *
* results in a fatal error) *
* *
* Added serial port support (simulated 8251 on ports 14 and 15) *
* *
* *
* Converted to AmigaDOS September 1987 by Charlie Gibbs *
* (after painstakingly typing it all in from Dr. Dobbs *
* Journal, January through March 1986). Improvements *
* described by Jim Cathey in his letter in the June 1986 *
* DDJ have been included. Repetitive code is generated *
* by macros whenever it would save my fingers. *
* *
* *
* Version 1.2 1/21/85 JEC *
* Fixed Extent bug in OPEN logic. *
* Sped up code, sample MAC from 2:13 to 1:40 *
* Now runs at a 1.4 MHz equivalent based on MAC sample. *
* *
* Version 1.1 8/29/84 JEC *
* Fixed BIOS call #6 bug. *
* *
* Version 1.0 05/25/84 by Jim Cathey *
* *
* This program has been written for speed whenever possible, *
* as such tends to be large because of the separate subroutine *
* for each and every opcode of the target processor. *
* *
* On an 8MHz 68000 (Compupro) system the simulation speed is *
* a little better than a 1MHz Z-80 when running MAC. The time *
* for a sample assembly was 2:13 for the simulation vs. 0:35 *
* on a 4MHz Z-80, both systems used identical hard disk systems. *
* *
* It is not a complete simulation, as some flag handling *
* isn't quite right, but it is enough to run the program *
* I wrote for it (DDT, LU, MAC, and Morrow's FORMATW). *
* *
*************************************************************************
code
page
*************************************************************************
* *
* This file contains the startup routines, the simulator core, *
* tracing code, and the CP/M 2.2 simulation. *
* *
*************************************************************************
xref optabl,flags,mloop,mloopt,tracesad,traceead,traceflg,start2
xdef illegl,service,dump,inp,outp,movear
xref _AbsExecBase
xref _CreatePort
xref _DeletePort
xdef _SysBase
xdef _DOSBase
include "options.i"
include "ecpmdefs.i"
*
* ASCII character values
*
bel equ $07 ;Bell (or beep or whatever)
bs equ $08 ;Backspace
ht equ $09 ;Horizontal tab
lf equ $0A ;Line feed
ff equ $0C ;Form feed
cr equ $0D ;Carriage return
so equ $0E ;Shift out
si equ $0F ;Shift in
esc equ $1B ;Escape
page
*--------------------------------
*
* Some commonly used macros
*
*--------------------------------
sys macro ;Call a system routine.
ifnd _LVO\1
xref _LVO\1
endc
jsr _LVO\1(a6)
endm
*----------------------------
* Target system Mnemonics
*----------------------------
tHLT EQU $76
tJMP EQU $C3
tRET EQU $C9
*----------------------
* Equates
*----------------------
MODE_OLDFILE equ 1005
MODE_NEWFILE equ 1006
ACCESS_READ equ -2
ACCESS_WRITE equ -1
STARTBAUD equ 1200 ;Starting baud rate
NUMBITS equ 8 ;Number of data bits
CTLCHAR equ $11130501 ;XON, XOFF, ENQ, ACK
SERFLAGS equ $A0 ;No XON/XOFF, shared
PARITYON equ 1 ;Parity enabled
PARITYODD equ 2 ;Odd parity
* I/O command codes
CMD_INVALID equ 0
CMD_RESET equ 1
CMD_READ equ 2
CMD_WRITE equ 3
CMD_UPDATE equ 4
CMD_CLEAR equ 5
CMD_STOP equ 6
CMD_START equ 7
CMD_FLUSH equ 8
CMD_NONSTD equ 9
SDCMD_QUERY equ CMD_NONSTD
SDCMD_BREAK equ CMD_NONSTD+1
SDCMD_SETPARAMS equ CMD_NONSTD+2 ;Set serial port parameters
page
*************************************************************************
* *
* Initialization *
* *
*************************************************************************
start: move.l sp,savesp ;Save the stack pointer.
move.l _AbsExecBase,a6
move.l a6,_SysBase ;Working copy of _AbsExecBase
move.b #1,testdol ;"pstring" should test for leading $.
clr.w esclen ;No partial escape sequence is saved.
clr.b insflag ;We're not in insert mode.
clr.b traceit ;Clear trace request flag.
clr.b z80flag ;Clear Z-80 flag.
clr.b btrcflg ;Turn off BIOS/BDOS call tracing.
clr.b quitflg ;Clear the quit flag.
clr.b bufflag ;Disable output buffering.
clr.b listopn ;The list device is closed.
clr.b frstset ;First call to "setbaud"
clr.b target+4 ;Set default drive and user to A0:.
move.w #1,acmap ;Set active drive map to A: only.
clr.w romap ;No drives are set read-only.
move.l #STARTBAUD,baud ;Initial serial port parameters
move.b #NUMBITS,bits
move.l #strbuf,strptr ;Initialize output buffer pointer.
lea handles,a1
move.w #(handlen-handles)/4-1,d1
1$ clr.l (a1)+ ;Clear file handles.
dbra d1,1$
clr.l rawhand ;Clear RAW: handle too.
*
* Copy the command line to "cmdline", stripping out leading switches if any.
*
lea cmdline,a1
subq #1,d0
* Skip over leading blanks, if any.
leadblk cmpi.b #' ',(a0)+ ;Leading blank?
bne.s setbuf ;No.
dbra d0,leadblk ;Skip over leading blank.
* If the -b switch is given, activate output buffering.
setbuf subq.l #1,a0 ;Back onto the first non-blank.
cmpi.b #'-',(a0) ;Possible buffer switch?
bne.s savecmd ;No - start saving the command line.
cmpi.b #'B',1(a0) ;Activate output buffering?
beq.s 1$ ;Yes.
cmpi.b #'b',1(a0)
bne.s settrc ;No.
1$ move.b #1,bufflag ;Set buffered-output flag.
bra.s skipsw
* If the -t switch is given, set "traceit".
settrc cmpi.b #'T',1(a0)
beq.s 1$
cmpi.b #'t',1(a0)
bne.s setz80
1$ move.b #1,traceit ;Set trace flag.
* If the -z switch is given, set both "z80flag" and "traceit".
setz80 cmpi.b #'Z',1(a0)
beq.s 1$
cmpi.b #'z',1(a0)
bne.s skipsw
1$ move.b #1,z80flag ;Set Z-80 flag.
move.b #1,traceit ;Set trace flag also.
* Skip over the switch and check for more.
skipsw cmpi.b #' ',(a0)+ ;End of switch?
beq.s 1$ ;Yes.
dbra d0,skipsw
addq.l #1,a1 ;Adjust A1.
bra.s gotcmd ;There is no command line left.
1$ subq.l #1,a0 ;Back onto the first blank.
bra.s leadblk ;Look for the next non-blank.
* Save the command line (except for leading switches).
savecmd move.b (a0)+,(a1)+ ;Save the command line, if any.
dbra d0,savecmd
gotcmd move.b #0,-1(a1) ;Replace the newline with a null.
move.b cmdline,cmdflag ;Save command-line flag.
*
* Open libraries and set up a RAW: window.
*
move.b #1,quitflg ;Quit immediately if failure below.
move.l _SysBase,a6 ;Find library
lea dosname,a1 ;'dos.library'
moveq #0,d0 ;Any version
sys OpenLibrary ;Open dos.library.
move.l d0,a6 ;Point to doslib for next operation.
move.l d0,_DOSBase ;Save it for future reference.
sys Input ;Get file handle for keyboard.
move.l d0,stdin ;Save it here.
beq quitprg ;Couldn't get keyboard handle.
sys Output ;Get file handle for screen.
move.l d0,stdout ;Save it here.
beq quitprg ;Couldn't get screen handle.
move.l #loadmsg,d1
bsr pstring ;Display where we loaded.
move.l #start,d1
bsr plong
bsr pspace
move.b #'/',d1
bsr pchar
bsr pspace
move.l #start2,d1
bsr plong
bsr pcrlf
move.l #rawspec,d1
move.l #MODE_NEWFILE,d2
sys Open ;Open a RAW: window.
move.l d0,rawhand ;Save the file handle here.
bne opened ;We succeeded.
move.l #rawerr,d1
bsr pstring ;Display error message...
sys IoErr
move.l d0,d1
bsr plong ; and error code.
bsr pcrlf
bra quitprg
dosname dc.b 'dos.library',0
loadmsg dc.b 'SimCPM version '
dc.b vermaj&15+'0','.',vermin/16+'0',vermin&15+'0'
dc.b ' has loaded at $'
rawspec dc.b 'RAW:0/0/640/200/CP/M Emulator'
dc.b ' by Jim Cathey and Charlie Gibbs - version '
dc.b vermaj&15+'0','.'
dc.b vermin/16+'0',vermin&15+'0',' ('
dc.b revyear/16+'0',revyear&15+'0','/'
dc.b revmonth/16+'0',revmonth&15+'0','/'
dc.b revday/16+'0',revday&15+'0',')',0
rawerr dc.b 'Unable to open RAW: - code $'
setwin dc.b $9B,'0x',$9B,'8y',$9B,'24t',$9B,'80u',$9B,'H',$9B,'J$'
opened move.b cmdflag,quitflg ;If we have a command, execute it and quit.
move.l #setwin,d1
bsr pstring ;Set the window to 24 by 80.
*
* Come back here to load another program.
*
nextprg lea target,targbase ;Start of target memory
clr.b insflag ;Reset insert mode.
move.l #simsg,d1
bsr pstring ;In case last program sent SHIFT OUT
clr.b dumpcnt ;Reset dump pause counter.
clr.l rpport ;serial.device message port pointers
clr.l wpport
bsr entrads ;Enter trace delimiting addresses.
bsr lodfdos ;Load up the fake FDOS in target memory.
bsr lodregs ;Load the remaining simulation registers.
bsr loadcom ;Load the .COM program.
jmp (return) ;Execute the simulation.
simsg dc.b si,'$' ;Resets MSB of each output character
page
*************************************************************************
* *
* Illegal instructions and dumping *
* *
*************************************************************************
illegl move.l #illgmsg,d1 ;Illegal opcode, say what & where,
bsr pstring
lea -1(pseudopc),a0
move.b (a0),d1
bsr pbyte
cmpi.b #$CB,(a0) ;Display sub-op-code for prefixes CB...
beq.s 1$
cmpi.b #$DD,(a0) ;DD...
beq.s 1$
cmpi.b #$ED,(a0) ;ED...
beq.s 1$
cmpi.b #$FD,(a0) ;and FD.
bne.s 2$
1$ move.b 1(a0),d1
bsr pbyte
2$ move.l #ilgmsg2,d1
bsr pstring
suba.l targbase,a0
move.l a0,d1
bsr pword
move.l #ilgmsg3,d1
bsr pstring
move.l #dumpmsg,d1
bsr pstring
clr.b dumpcnt
bsr dump ; and spill guts.
bra quitprg ;Quit simulation.
illgmsg dc.b cr,lf,'Illegal instruction $'
ilgmsg2 dc.b ' at $'
ilgmsg3 dc.b '.$'
dumpmsg dc.b cr,lf,'Register contents:$'
*
* Dump all registers and decode the current instruction.
*
dump movem.l d0-d3/a1,-(sp)
bsr pcrlf
move.b regf,d0
bsr dspflag
move.b #'A',(a0)+
move.b #'=',(a0)+
move.b rega,d1 ;Accumulator
bsr ubyte
move.b #' ',(a0)+
move.b #'B',(a0)+
move.b #'=',(a0)+
move.w regb(regs),d1 ;BC
bsr uword
move.b #' ',(a0)+
move.b #'D',(a0)+
move.b #'=',(a0)+
move.w regd(regs),d1 ;DE
bsr uword
move.b #' ',(a0)+
move.b #'H',(a0)+
move.b #'=',(a0)+
move.w regh(regs),d1 ;HL
bsr uword
move.b #' ',(a0)+
move.b #'S',(a0)+
move.b #'=',(a0)+
move.l pseudosp,d1 ;SP
sub.l targbase,d1
bsr uword
move.b #' ',(a0)+
move.b #'P',(a0)+
move.b #'=',(a0)+
move.l pseudopc,d1 ;PC
sub.l targbase,d1
bsr uword
move.b #' ',(a0)+
move.l a1,-(sp)
move.l pseudosp,a1
moveq #3,d2
moveq #'0',d3
tosloop move.b #'S',(a0)+ ;Display the top 4 stack entries.
move.b d3,(a0)+
addq.b #1,d3
move.b #'=',(a0)+
move.b 1(a1),d1
ror.w #8,d1
move.b 0(a1),d1
bsr uword
move.b #' ',(a0)+
addq.l #2,a1
dbra d2,tosloop
move.b #'$',(a0)+
move.l (sp)+,a1
move.l #workbuf,d1
bsr pstring ;Displaying as a single string is much faster.
bsr pcrlf
move.b regf2(regs),d0 ;Alternate flags
bsr dspflag
move.b #'A',(a0)+
move.b #'''',(a0)+
move.b rega2(regs),d1 ;Alternate accumulator
bsr ubyte
move.b #' ',(a0)+
move.b #'B',(a0)+
move.b #'''',(a0)+
move.w regb2(regs),d1 ;Alternate BC
bsr uword
move.b #' ',(a0)+
move.b #'D',(a0)+
move.b #'''',(a0)+
move.w regd2(regs),d1 ;Alternate DE
bsr uword
move.b #' ',(a0)+
move.b #'H',(a0)+
move.b #'''',(a0)+
move.w regh2(regs),d1 ;Alternate HL
bsr uword
move.b #' ',(a0)+
move.b #'X',(a0)+
move.b #'=',(a0)+
move.w regix(regs),d1 ;IX
bsr uword
move.b #' ',(a0)+
move.b #'Y',(a0)+
move.b #'=',(a0)+
move.w regiy(regs),d1 ;IY
bsr uword
move.b #' ',(a0)+
move.b #' ',(a0)+
move.b (pseudopc),d1 ;Current opcode byte
cmpi.b #$CB,d1 ;Prefix?
beq.s oppfx ;Yes.
cmpi.b #$DD,d1
beq.s oppfx
cmpi.b #$ED,d1
beq.s oppfx
cmpi.b #$FD,d1
beq.s oppfx
move.b #' ',(a0)+
bsr ubyte ;Unprefixed opcode
move.b #' ',(a0)+
bra.s opx
oppfx bsr ubyte ;Prefix
move.b 1(pseudopc),d1
bsr ubyte ;Prefixed opcode
opx move.b #' ',(a0)+
*
* Decode the instruction.
*
moveq #0,d0
move.b (pseudopc),d0 ;Opcode
clr.b prefix ;Assume it's not a prefix.
cmpi.b #$DD,d0 ;Is it?
beq.s dopfx ;Yes.
cmpi.b #$FD,d0
bne.s saveop ;No.
dopfx move.b d0,prefix ;It's a prefix.
move.b 1(pseudopc),d0 ;The opcode is really here.
saveop move.b d0,opcode ;Save the opcode.
* Look up the opcode.
cmpi.b #$40,d0 ;Is opcode in range 00-3F?
bcc.s lookupC ;No.
* The opcode is in the range 00-3F.
lea mnop008,a1 ;Assume we're using 8080 mnemonics.
tst.b z80flag ;Should we use Z-80 mnemonics?
beq.s lokup0c ;No
lea mnop00z,a1
lokup0c mulu #12,d0 ;Offset into opcode table
add.l d0,a1 ;A1 points to decoded instruction
bra decode ;Decode remainder of instruction.
* The opcode is in the range C0-FF.
lookupC cmpi.b #$C0,d0 ;Is opcode in range C0-FF?
bcs.s lookup8 ;No.
cmpi.b #$ED,d0 ;ED-prefix instructions?
beq lookupE ;Yes.
cmpi.b #$CB,d0 ;CB-prefix instructions?
beq lookupB ;Yes.
lea mnopC08,a1 ;Assume we're using 8080 mnemonics.
tst.b z80flag ;Should we use Z-80 mnemonics?
beq.s lokupCc ;No.
lea mnopC0z,a1
lokupCc subi.w #$C0,d0
mulu #12,d0 ;Offset into opcode table
add.l d0,a1 ;A1 points to decoded instruction
bra decode ;Decode remainder of instruction.
* The opcode is in the range 80-BF.
lookup8 cmpi.b #$80,d0 ;Is opcode in range 80-BF?
bcs.s lookup4 ;No - it's in range 40-7F.
lea mnop808,a1 ;Assume we're using 8080 mnemonics.
tst.b z80flag ;Are we?
beq.s lokup8c ;Yes.
lea mnop80z,a1 ;Use the Z-80 mnemonic table.
lokup8c lsr.b #3,d0 ;Divide opcode by 8.
andi.w #7,d0 ;Instruction type
mulu #9,d0 ;Offset into opcode table
add.l d0,a1 ;A1 points to decoded instruction
bra decode ;Decode remainder of instruction.
* The opcode is in the range 40-7F.
lookup4 cmpi.b #$76,d0 ;Is this a HLT (Z-80 HALT) instruction?
bne.s lokup4m ;No.
lea mnop768,a1
tst.b z80flag ;Do the Z-80 mnemonic?
beq decode ;No.
lea mnop76z,a1
bra decode
lokup4m lea mnop408,a1 ;Assume 8080 MOV instruction.
tst.b z80flag ;Are we doing Z-80 mnemonics?
beq decode ;No.
lea mnop40z,a1 ;Make it a Z-80 LD instruction.
bra decode
* CB-prefix instruction decoding
lookupB move.b 1(pseudopc),d0 ;Sub-opcode
tst.b prefix ;IX or IY with displacement?
beq.s lokupB0 ;No.
move.b 2(pseudopc),d0 ;Skip over the displacement.
lokupB0 move.b d0,opcode
cmpi.b #$40,d0 ;Is opcode in range CB00-CB3F?
bcc.s lokupB4 ;No.
lea mnopCB08,a1
lsr.b #3,d0
mulu #8,d0
add.l d0,a1
bra decode
lokupB4 move.b d0,d1 ;Save the sub-opcode.
andi.w #$C0,d0 ;Opcode is in range CB40-CBFF.
lsr.b #3,d0 ;Displacement into 8-byte entries
lea mnopCB48,a1
add.l d0,a1
lokupBm move.b (a1)+,(a0)+ ;Move mnemonic to decoded area.
cmpi.b #'$',(a1)
bne.s lokupBm
move.b d1,d0 ;Get the sub-opcode again.
andi.w #$38,d0 ;Isolate the bit number.
lsr.b #3,d0
addi.b #'0',d0 ;Convert the bit number to ASCII.
move.b d0,(a0)+ ;Move bit number to decoded instruction.
move.b #',',(a0)+
move.b d1,d0
andi.b #7,d0 ;Isolate the register specification.
bsr dspreg ;Set up the register number.
bra dispop
* ED-prefix instruction decoding
lookupE move.b 1(pseudopc),d0 ;Sub-opcode
move.b d0,opcode
cmpi.b #$40,d0 ;Is it below ED40?
bcs lookupI ;Yes - it's illegal.
cmpi.b #$80,d0 ;Is it in range ED40-ED7F?
bcc lokupE8 ;No.
lea mnopE48,a1 ;Assume we're using 8080 mnemonics.
tst.b z80flag ;Should we use Z-80 mnemonics?
beq.s lokupEc ;No
lea mnopE4z,a1
lokupEc andi.w #$3F,d0
mulu #12,d0 ;Offset into opcode table
add.l d0,a1 ;A1 points to decoded instruction
bra decode ;Decode remainder of instruction.
lokupE8 cmpi.b #$A0,d0 ;Is opcode in range ED80-ED9F?
bcs lookupI ;Yes - it's illegal.
cmpi.b #$BC,d0 ;It must be in range EDA0-EDBB.
bcc lookupI
lea mnopEA8,a1
andi.w #$1F,d0
mulu #5,d0
add.l d0,a1
cmpi.b #' ',(a1) ;Is opcode blank?
bne.s decode ;No - decode the instruction.
* The instruction is illegal.
lookupI lea mnopilg,a1 ;Point to "ILLEGAL" message.
*
* Decode the instruction according to the string pointed to by A1.
* The decoded instruction is built where A0 points.
* See the mnemonic tables for an explanation of operand codes.
*
decode:
* Code "r" - register in bits 0-2 of opcode
cmpi.b #'r',(a1)
bne.s decodeq
move.b opcode,d0
bsr dspreg
bra decodex
* Code "q" - register in bits 3-5 of opcode
decodeq cmpi.b #'q',(a1)
bne.s decodep
move.b opcode,d0
lsr.b #3,d0
bsr dspreg
bra decodex
* Code "p" - register pair in bits 4-5 of opcode
decodep cmpi.b #'p',(a1)
bne decodeh
move.b opcode,d0
andi.b #$30,d0 ;Isolate the register bits.
bne.s decodpd ;They aren't 00.
move.b #'B',(a0)+ ;00 - register B
tst.b z80flag ;Are we using Z-80 mnemonics?
beq decodex ;No.
move.b #'C',(a0)+ ;Call it BC for Z-80.
bra decodex
decodpd cmpi.b #$20,d0
beq.s decodph
bhi.s decodps
move.b #'D',(a0)+ ;01 - register D (DE for Z-80)
tst.b z80flag
beq decodex
move.b #'E',(a0)+
bra decodex
decodph cmpi.b #$DD,prefix ;10 - check for index prefix.
beq.s decodpx ;IX
bhi.s decodpy ;IY
move.b #'H',(a0)+ ;Register H (HL for Z-80)
tst.b z80flag
beq decodex
move.b #'L',(a0)+
bra decodex
decodpx move.b #'I',(a0)+ ;IX
move.b #'X',(a0)+
bra decodex
decodpy move.b #'I',(a0)+ ;IY
move.b #'Y',(a0)+
bra decodex
decodps cmpi.b #$F0,opcode ;11 - depends on opcode
bcc.s decodpp
move.b #'S',(a0)+ ;11 is SP if opcode is less than F0.
move.b #'P',(a0)+
bra decodex
decodpp tst.b z80flag ;Otherwise, it's PSW (AF for Z-80).
bne.s decodpz
move.b #'P',(a0)+
move.b #'S',(a0)+
move.b #'W',(a0)+
bra.s decodex
decodpz move.b #'A',(a0)+
move.b #'F',(a0)+
bra.s decodex
* Code "h" - HL, IX, or IY depending on prefix
decodeh cmpi.b #'h',(a1)
bne.s decoden
cmpi.b #$DD,prefix
beq.s decodpx ;IX
bhi.s decodpy ;IY
move.b #'H',(a0)+ ;HL
move.b #'L',(a0)+
bra decodex
* Code "n" - 8-bit value following opcode
decoden cmpi.b #'n',(a1)
bne.s decodea
move.b 1(pseudopc),d1
tst.b prefix ;DD or FD prefix?
beq.s 1$ ;No - we have the value.
cmpi.b #$36,opcode ;Is opcode below 36?
bcs.s 1$ ;Yes - it's a move to index register half
move.b 3(pseudopc),d1 ;The value is actually here.
1$ bsr ubyte ;Convert the value to a hex string.
bra.s decodex
* Code "a" - 16-bit value following opcode
decodea cmpi.b #'a',(a1)
bne.s decodem
move.b 2(pseudopc),d1
lsl.w #8,d1
move.b 1(pseudopc),d1
cmpi.b #$ED,(pseudopc) ;Is this an ED-prefix instruction?
beq.s 1$ ;Yes - the value is shifted one byte.
tst.b prefix ;DD or FD prefix?
beq.s 2$ ;No - we have the value.
1$ move.b 3(pseudopc),d1 ;The value is actually here.
lsl.w #8,d1
move.b 2(pseudopc),d1
2$ bsr uword ;Convert the value to a hex string.
bra.s decodex
* Not a special code - just move the character as is.
decodem move.b (a1),(a0)+
* Try for another character to move (and possibly decode).
decodex addq.l #1,a1
cmpi.b #'$',(a1)
bne decode
*
* Display the decoded instruction.
*
dispop move.b #cr,(a0)+
move.b #lf,(a0)+
move.b #'$',(a0)+
move.l #workbuf,d1
bsr pstring ;Displaying as a single string is much faster.
* Pause after every screenful to give the operator time to read it.
addq.b #1,dumpcnt ;Count the number of times dumped.
cmpi.b #8,dumpcnt ;Is the screen full of dumps?
bcs dumpx ;No - exit.
move.l #dmpmsg3,d1
bsr pstring ;Ask for operator action.
bsr dmpstr ;Make sure the prompt gets out!
movem.l a0-a1/a6,-(sp)
move.l rawhand,d1 ;Console input
move.l #dumpcnt,d2
move.l _DOSBase,a6
moveq #1,d3
sys Read ;Get the operator's reply.
movem.l (sp)+,a0-a1/a6
bsr pcrlf
cmpi.b #'Q',dumpcnt ;Does he want to quit?
beq quitprg ;Yes.
cmpi.b #'q',dumpcnt
beq quitprg
cmpi.b #'S',dumpcnt ;Stop tracing?
beq.s 2$ ;Yes.
cmpi.b #'s',dumpcnt
beq.s 2$
cmpi.b #'C',dumpcnt ;Change trace address?
beq.s 1$ ;Yes.
cmpi.b #'c',dumpcnt
bne.s dmpcont
1$ bsr gtrange ;Get new trace range.
2$ clr.b traceflg ;Disable tracing and continue.
dmpcont clr.b dumpcnt ;Reset the dump counter.
dumpx movem.l (sp)+,d0-d3/a1
rts
dmpmsg3 dc.b 'Q to quit, S to stop tracing, C to change trace,'
dc.b ' any other key to continue: $'
*
* Display the register number indicated by the low-order 3 bits of D0.
* H and L will be displayed as XH and XL if necessary.
* M ((HL) for Z-80) will be converted to (IX+d) or (IY+d) if necessary.
*
dspreg andi.b #7,d0 ;Isolate register number.
cmpi.b #6,d0 ;Which register is it?
beq.s dspregm ;M - might need a special routine.
bhi.s dsprega ;A - this one is easy.
addi.b #'B',d0 ;Registers B, C, D, E, H, and L
cmpi.b #'F',d0 ;Is it H or L?
bcs.s dspregb ;No - we're ready.
bne.s dspregl ;It's L.
moveq #'H',d0 ;It's H.
bra.s dspregp
dspregl moveq #'L',d0 ;It's L.
dspregp cmpi.b #$DD,prefix ;Are we manipulating an index register?
bcs.s dspregb ;No.
bne.s dspregy ;Register YH or YL
move.b #'X',(a0)+ ;Register XH or XL
bra.s dspregb
dspregy move.b #'Y',(a0)+
dspregb move.b d0,(a0)+
rts
dsprega move.b #'A',(a0)+ ;It's the accumulator.
rts
dspregm cmpi.b #$DD,prefix ;Index register?
bcc.s dspregi ;Yes.
tst.b z80flag ;Are we using 8080 mnemonics?
bne.s dspregz ;No.
move.b #'M',(a0)+ ;It's M for 8080.
rts
dspregz move.b #'(',(a0)+ ;It's (HL) for Z-80.
move.b #'H',(a0)+
move.b #'L',(a0)+
move.b #')',(a0)+
rts
dspregi move.b #'(',(a0)+ ;It's an indexed operand.
move.b #'I',(a0)+
move.b #'X',(a0)+ ;Assume it's IX.
cmpi.b #$DD,prefix ;Is it?
beq.s dspregd ;Yes.
move.b #'Y',-1(a0) ;It's IY.
dspregd move.b #'+',(a0)+
move.b 2(pseudopc),d1 ;Displacement
bsr ubyte
move.b #')',(a0)+
rts
*
* Display the contents of the flag register in D0.
*
dspflag lea workbuf,a0
move.b #'-',d1 ;Carry (assume not set)
btst #0,d0
beq 1$
move.b #'C',d1 ;Carry flag is set.
1$ move.b d1,(a0)+
move.b #'-',d1 ;Zero
btst #6,d0
beq 2$
move.b #'Z',d1
2$ move.b d1,(a0)+
move.b #'-',d1 ;Minus
btst #7,d0
beq 3$
move.b #'M',d1
3$ move.b d1,(a0)+
move.b #'-',d1 ;Even parity
btst #2,d0
beq 4$
move.b #'E',d1
4$ move.b d1,(a0)+
move.b #'-',d1 ;Intermediate carry
btst #4,d0
beq 5$
move.b #'I',d1
5$ move.b d1,(a0)+
move.b #' ',(a0)+
rts
page
*************************************************************************
* *
* Initialization subroutines *
* *
*************************************************************************
*
* Load up the fake FDOS.
*
lodfdos move.l a6,-(sp)
lea fdos,a6
move.l targbase,pseudosp
adda.l #$10000,pseudosp
lea -256(pseudosp),a0
move.w #fdoslen,d0
1$ move.b (a6)+,(a0)+
dbra d0,1$
lea -256(pseudosp),a0
move.l a0,d0
sub.l targbase,d0
move.b #tJMP,0(targbase) ;Build BIOS and BDOS jumps.
move.b #tJMP,5(targbase)
move.b d0,6(targbase)
rol.w #8,d0
move.b d0,7(targbase)
rol.w #8,d0
addq.w #3,d0
move.b d0,1(targbase)
rol.w #8,d0
move.b d0,2(targbase)
clr.w -(pseudosp) ;Set up a return stack to exit simulation.
move.l (sp)+,a6
rts
*
* Set up working registers.
*
lodregs lea optabl,opptr ;Point base reg. to opcode dispatch table.
lea mloop,return
tst.b traceit ;Is tracing required?
beq.s 1$ ;No.
lea mloopt,return ;Point return to trace test.
1$ lea flags,flagptr
lea $100(targbase),pseudopc ;Start execution at 0100H.
moveq #$E,regcon0e ;Set up quick constants.
moveq #$1,regcon01
moveq #$F,regcon0f
move.l #$FF,regconff
moveq #0,rega
moveq #0,regf
clr.b regi(regs)
rts
page
*
* Get start and end addresses for tracing.
*
entrads tst.b traceit ;Is tracing required?
beq entradx ;No.
bsr gtrange ;Get trace range.
* Find out whether BIOS/BDOS calls are to be traced.
move.l #btrcmsg,d1
bsr pstring
lea workbuf,a0
move.b #10,(a0)
bsr getline
move.b #1,btrcflg
cmpi.b #'Y',workbuf+2
beq.s entradx
cmpi.b #'y',workbuf+2
beq.s entradx
clr.b btrcflg
entradx clr.b traceflg ;Start with tracing turned off.
rts
gtrange move.l #tracemsg,d1 ;Enter trace address if necessary.
bsr pstring
lea workbuf,a0
move.b #workbufn-workbuf-2,(a0)
bsr getline ;Get the string.
moveq #0,d0
move.b 1(a0),d0 ;Number of bytes read
addq.l #2,a0 ;Skip over length information.
clr.b 0(a0,d0) ;Insert string terminator.
bsr atol ;Get trace start address.
andi.l #$FFFF,d1
add.l #target,d1
move.l d1,tracesad
* Now get the ending address.
move.l #tracemg2,d1
bsr pstring
lea workbuf,a0
move.b #workbufn-workbuf-2,(a0)
bsr getline
moveq #0,d0
move.b 1(a0),d0
addq.l #2,a0
clr.b 0(a0,d0)
bsr atol
andi.l #$FFFF,d1
add.l #target,d1
move.l d1,traceead
bsr pcrlf
rts
tracemsg dc.b cr,lf,'Start trace at >$'
tracemg2 dc.b ' End trace at >$'
btrcmsg dc.b 'Trace BIOS/BDOS calls? >$'
*
* Open the file to be loaded, and load it into target space if successful.
*
loadcom movem.l d1-d3/a1-a2/a6,-(sp) ;Save registers.
lea cmdline,a0
tst.b cmdflag ;Do we have a command already?
bne.s scancmd ;Yes - process it.
* Display the command prompt.
prompt lea target,targbase ;Just in case "targbase" gets clobbered
move.b 4(targbase),d1 ;Get the default drive code.
andi.b #$0F,d1
addi.b #'A',d1 ;Convert drive code to ASCII letter.
bsr pchar ;Display the current drive.
move.b 4(targbase),d1 ;Get user number.
lsr.b #4,d1 ;Move it to low-order 4 bits.
beq.s promptp ;Don't insert user number if it's zero.
cmpi.b #10,d1 ;Is user number 10 or greater?
bcs.s promptu ;No.
move.b d1,-(sp)
move.b #'1',d1
bsr pchar ;Display tens digit of user number.
move.b (sp)+,d1
subi.b #10,d1
promptu addi.b #'0',d1 ;Convert user number to ASCII.
bsr pchar ;Display user number.
promptp move.b #'>',d1
bsr pchar
lea cmdline,a0
move.b #cmdlinen-cmdline-2,(a0)
bsr getline ;Get a command line.
moveq #0,d0
move.b 1(a0),d0 ;Length of command line
beq.s prompt ;Zero - try again.
addq.l #2,a0 ;Skip over length information.
clr.b 0(a0,d0) ;Insert command line terminator.
cmpi.b #3,(a0) ;Control-C?
bne.s scancmd ;No.
move.b #1,quitflg ;Set quit flag.
bra quitprg ;Exit the simulator.
scancmd clr.b cmdflag ;Ask for a new command next time.
clr.b builtin ;Clear "built-in command" flag.
* Check for a change of drive number.
tst.b 2(a0) ;Is the command two characters long?
bne.s convcmd ;No - treat as a normal command.
cmpi.b #':',1(a0) ;Drive specifier?
bne.s convcmd ;No.
bsr ucase ;Get the drive code.
cmpi.b #'A',d0 ;Is it valid?
bcs.s opensel ;No.
cmpi.b #'P',d0
bhi.s opensel
subq.b #1,d0
andi.b #$0F,d0 ;New drive code
andi.b #$F0,4(targbase)
or.b d0,4(targbase) ;Insert into current drive slot.
bra prompt
opensel move.l #1$,d1
bsr pstring ;'BDOS Error on '
bsr ucase
move.b d0,d1
bsr pchar ;Drive letter
move.l #2$,d1
bsr pstring ;': Select'
bra prompt ;Try again.
1$ dc.b 'BDOS Error on $'
2$ dc.b ': Select',cr,lf,'$'
* Convert the program file name to an AmigaDOS file name in "comname".
convcmd lea comname,a2
cmpi.b #':',1(a0) ;Is there a drive specifier?
bne.s 1$ ;No.
bsr ucase ;Get the drive letter.
cmpi.b #'A',d0 ;Is it valid?
bcs.s opensel ;No.
cmpi.b #'P',d0
bhi.s opensel
addq.l #1,a0 ;Skip over the colon.
bra.s 2$
1$ move.b 4(targbase),d0 ;Current drive and user number
beq.s loadnam ;Drive A:, user 0
andi.b #$0F,d0 ;Isolate current drive bits
addi.b #'A',d0 ;Convert to drive code.
2$ move.b 4(targbase),d1 ;Current drive and user number
lsr.b #4,d1 ;Isolate the user number.
bne.s 3$ ;Not zero - make a directory name.
cmpi.b #'A',d0 ;Are we loading from drive A:?
beq.s loadnam ;Yes - use the current directory.
3$ move.b #'C',(a2)+ ;Convert to AmigaDOS device.
move.b #'P',(a2)+
move.b #'M',(a2)+
move.b d0,(a2)+ ;Insert the drive letter.
tst.b d1 ;User zero?
beq.s 5$ ;Yes - don't bother inserting it.
move.b #'0',(a2)+ ;Assume user number is less than 10.
cmpi.b #10,d1 ;Is user number 10 or greater?
bcs.s 4$ ;No.
move.b #'1',-1(a2) ;Change the first digit to 1.
subi.b #10,d1
4$ addi.b #'0',d1 ;Convert user number to ASCII.
move.b d1,(a2)+ ;Insert user number into file spec.
5$ move.b #':',(a2)+ ;Insert a colon.
loadnam move.l #comnamen,d1 ;End of name, allowing for .COM suffix
subq.l #6,d1 ;Allow for .COM suffix.
sub.l a2,d1 ;Adjust for directory prefix, if any.
1$ bsr ucase ;Convert file name to upper case.
move.b d0,(a2)+
cmpi.b #' ',(a0) ;End of name?
beq.s gotname ;Yes.
tst.b (a0) ;End of command string?
dbeq d1,1$ ;No - keep on going.
gotname move.l a0,comend ;Save position in command line.
move.b #'.',(a2)+ ;Mash file name to .COM.
move.b #'C',(a2)+
move.b #'O',(a2)+
move.b #'M',(a2)+
clr.b (a2)
clr.b cmdline ;Ask for a new command next time.
* If this is a USER or SAVE command, don't look for a .COM file -
* we'll process these commands ourselves.
lea comname,a0 ;Scan for possible device name.
move.l a0,a1 ;Use A1 as a scan pointer - preserve A0.
1$ tst.b (a1) ;End of command name?
beq.s 2$ ;Yes - there is no device name.
cmpi.b #':',(a1)+ ;End of device name?
bne.s 1$ ;No - continue scanning.
move.l a1,a0 ;Point A0 past the device name.
2$ cmpi.b #'.',4(a0) ;Is the command name four characters long?
bne opencom ;No.
cmpi.b #'U',(a0) ;Check for a USER command.
bne.s tstsave
cmpi.b #'S',1(a0)
bne.s tstsave
cmpi.b #'E',2(a0)
bne.s tstsave
cmpi.b #'R',3(a0)
bne.s tstsave
move.b #1,builtin ;Indicate we have a USER command.
bra loaded
tstsave cmpi.b #'S',(a0) ;Check for a SAVE command.
bne.s opencom
cmpi.b #'A',1(a0)
bne.s opencom
cmpi.b #'V',2(a0)
bne.s opencom
cmpi.b #'E',3(a0)
bne.s opencom
move.b #2,builtin ;Indicate we have a SAVE command.
bra loaded
* Open the command file and load it if it exists.
opencom move.l #comname,d1
move.l #MODE_OLDFILE,d2
move.l _DOSBase,a6
sys Open ;Open the file.
tst.l d0 ;Did the open fail?
bne.s comopen ;No.
lea comname,a0
openerr cmpi.b #'.',(a0)+ ;Find end of file name.
bne.s openerr
move.b #'?',-1(a0)
move.b #cr,(a0)+
move.b #lf,(a0)+
move.b #'$',(a0)
move.l #comname,d1
bsr pstring ;Echo "name?"
bra prompt ;Try again.
comopen move.l d0,-(sp) ;Save the file handle.
move.l d0,d1
move.l pseudopc,d2 ;Start loading at $0100 in target.
move.l #65536-512,d3 ;Maximum number of bytes to load
move.l _DOSBase,a6
sys Read ;Load the .COM file.
move.l (sp)+,d1
move.l _DOSBase,a6
sys Close ;Close the .COM file.
* The program has now been loaded (unless it's USER or SAVE).
loaded movem.l (sp)+,d1-d3/a1-a2/a6 ;Refresh registers.
movem.l d1-d3/a1-a2/a6,-(sp)
lea $80(targbase),a0 ;Set up target's base page.
move.l a0,dmaaddr
* Set up FCBs and command line tail.
lea $5C(targbase),a0
lea $6C(targbase),a2
clr.b (a0)+
clr.b (a2)+
moveq #10,d0
clrfcb move.b #' ',(a0)+ ;Clear FCBs.
move.b #' ',(a2)+
dbra d0,clrfcb
clr.b $80(targbase) ;Clear the command line tail.
move.l comend,a0 ;Restore position in command line.
fcb1 tst.b (a0) ;End of command line?
beq loadusr ;Yes.
cmpi.b #' ',(a0)+ ;Skip over to first file name
beq.s fcb1
subq.l #1,a0 ;Back onto start of file name.
move.l a0,-(sp) ;Save position on command line.
lea $81(targbase),a2;A2 loads the command line tail.
gettail move.b (a0)+,(a2)+ ;Copy the command tail.
bne.s gettail
move.l a2,d0
lea $82(targbase),a0;Don't count null terminator!
sub.l a0,d0
move.b d0,$80(targbase);Length of command line tail
move.l (sp)+,a0 ;Go back to the first file name.
lea $5C(targbase),a2;Address of current FCB
getfcb move.l a2,fcbptr ;Save pointer to current FCB.
cmpi.b #':',1(a0) ;Is a drive specified?
bne.s 1$ ;No.
move.b (a0),(a2) ;Get drive letter.
subi.b #'A'-1,(a2) ;Convert to drive code.
addq.l #2,a0 ;Skip over drive designator.
tst.b (a0) ;End of command line?
beq.s loadusr ;Yes - we're done.
cmpi.b #' ',(a0) ;End of file name?
beq.s getfcbx ;Yes.
1$ addq.l #1,a2 ;Start of file name in FCB
2$ move.b (a0)+,(a2)+ ;Copy file name to FCB.
3$ tst.b (a0) ;End of command?
beq.s loadusr ;Yes.
cmpi.b #' ',(a0) ;End of file name?
beq.s getfcbx ;Yes.
cmpi.b #'.',(a0) ;Start of file name extension?
bne.s 2$ ;No - continue loading file name.
move.l fcbptr,a2 ;Copy original pointer
lea 9(a2),a2 ;Skip over to extension field.
addq.l #1,a0 ;Skip over the period.
bra.s 3$
getfcbx tst.b (a0) ;End of command line?
beq.s loadusr ;Yes.
cmpi.b #' ',(a0)+ ;Look for another file name.
beq.s getfcbx
subq.l #1,a0 ;Back onto start of file name.
move.l fcbptr,d0
lea $5C(targbase),a2
cmp.l d0,a2 ;Was this the first FCB?
bne.s loadusr ;No - stop after two FCBs.
lea 16(a2),a2 ;Skip over to the next FCB.
bra.s getfcb ;Load the next FCB.
* If this is a USER or SAVE command, process it here.
* These are the only built-in commands that SimCPM supports,
* since the rest are just as easily done through the CLI.
* The first operand is a USER number or the number of pages to SAVE.
loadusr tst.b builtin ;Is this a built-in command?
beq loadcmx ;No - we're done.
lea $5C(targbase),a0 ;Scan the first FCB.
moveq #0,d0 ;Build the number here.
tst.b (a0)+ ;Is drive code omitted?
bne.s badunum ;No - we don't have a valid number.
cmpi.b #' ',(a0) ;Is the number missing?
beq.s badunum ;Yes - error.
cmpi.b #' ',3(a0) ;Is the number too long?
bne.s badunum ;Yes - it's likely too big.
getuser cmpi.b #'0',(a0) ;Is the current digit valid?
bcs.s badunum ;No.
cmpi.b #'9',(a0)
bhi.s badunum
mulu #10,d0 ;Shift previous digits, if any.
move.b (a0)+,d1 ;Get the current digit.
andi.w #$0F,d1 ;Convert current digit to binary.
add.w d1,d0 ;Accumulate total.
cmpi.b #' ',(a0) ;End of number?
bne.s getuser ;No - try for another digit.
bra.s chkuser
badunum moveq #-1,d0 ;Invalid number
chkuser cmpi.b #1,builtin ;Is this a USER command?
bne.s chksave ;No.
cmpi.w #15,d0 ;Is user number over 15?
bls.s setuser ;No - it's valid.
move.l #badumsg,d1
bsr pstring ;Display an error message and try again.
bra prompt
setuser andi.b #$0F,4(targbase);Clear original user number.
lsl.b #4,d0 ;Move new user bits into position.
or.b d0,4(targbase) ;Insert new user number.
bra prompt
badumsg dc.b 'Invalid user number',cr,lf,'$'
chksave tst.w d0 ;Attempt to save zero blocks?
beq.s badblks ;Yes - error.
cmpi.w #255,d0 ;Is number of blocks to SAVE valid?
bls.s savefn ;Yes.
badblks move.l #1$,d1
bsr pstring
bra prompt
1$ dc.b 'Number of pages must be from 1 to 255.',cr,lf,'$'
savefn lea $6C(targbase),a0;FCB for SAVE file name
cmpi.b #' ',1(a0) ;Is file name missing?
bne.s opensav ;No.
move.l #1$,d1
bsr pstring
bra prompt
1$ dc.b 'File name is missing.',cr,lf,'$'
opensav lea opnname,a1 ;Build AmigaDOS file name here.
move.l a1,d1 ;We'll need it here.
move.l d0,-(sp) ;Save number of blocks to save.
bsr convfn ;Make a file name.
move.l #MODE_NEWFILE,d2
move.l _DOSBase,a6
sys Open ;Open the file.
tst.l d0 ;Did the open fail?
bne.s saveit ;No.
move.l (sp)+,d0 ;Clean up the stack.
move.l #1$,d1
bsr pstring
bra prompt
1$ dc.b 'Unable to open file for SAVE.',cr,lf,'$'
saveit move.l d0,d1 ;Handle for SAVE file
lea target+$100,a0 ;"targbase" isn't intact right now.
move.l a0,d2 ;Start of data to save
move.l (sp)+,d3 ;Number of 256-byte pages to save
asl.l #8,d3 ;Convert to number of bytes.
move.l d1,-(sp) ;Save the file handle.
sys Write ;Write the file.
move.l (sp)+,d1
sys Close ;Close the file.
bra prompt ;Successful completion
* We have successfully loaded a .COM file.
loadcmx movem.l (sp)+,d1-d3/a1-a2/a6 ;Restore registers.
rts ;Exit.
*
* Subroutine to get a character and convert it to upper case
*
ucase move.b (a0)+,d0
cmpi.b #'a',d0
bcs.s ucasex
cmpi.b #'z',d0
bhi.s ucasex
subi.b #'a'-'A',d0
ucasex rts
page
*************************************************************************
* *
* BDOS / BIOS service routines *
* *
*************************************************************************
service movem.l a1/a6,-(sp)
move.b rega,newrega ;Save Z-80 accumulator (D2)
move.l _DOSBase,a6 ;Get dos.library pointer
* Decode the byte following the HLT instruction (BIOS call type).
moveq #0,d0 ;Handle BIOS/BDOS service request
move.b (pseudopc)+,d0 ; of form HLT DB opcode.
cmp #(biostabn-biostab)/4,d0
blt.s dobios ;Function number is within range.
badbios move.b d0,-(sp) ;Flag illegal BIOS call
move.l #ilgbios,d1 ; and spill guts.
bsr pstring
move.b (sp)+,d1
bsr pbyte
move.l #atmsg,d1
bsr pstring
move.b 1(pseudosp),d1 ;Address where called (top stack entry)
ror.w #8,d1
move.b 0(pseudosp),d1
bsr pword
bsr pcrlf
bsr dump
bra quitprg
ilgbios dc.b cr,lf,'Illegal BIOS call $'
biosmsg dc.b 'BIOS call $'
atmsg dc.b ' (hex) at $'
dobios move.l d0,-(sp) ;Save BIOS function number.
beq.s biostrx ;Zero - it's a BDOS call.
tst.b btrcflg ;Trace BIOS calls?
beq.s biostrx ;No.
move.l #biosmsg,d1
bsr pstring
move.l (sp),d1
bsr pbyte
move.l #atmsg,d1
bsr pstring
move.b 1(pseudosp),d1 ;Address where called (top stack entry)
ror.w #8,d1
move.b 0(pseudosp),d1
bsr pword
bsr pcrlf
move.l (sp),d0
biostrx asl #2,d0 ;Multiply function number by 4.
addi.l #biostab,d0 ;Point at address table entry.
movea.l d0,a0
movea.l (a0),a0 ;Point to appropriate service routine.
move.l (sp)+,d0 ;Restore BIOS function number.
jmp (a0) ;Jump to the routine.
* If the BIOS code is zero, it's a BDOS call.
* Decode register C using a similar routine to the BIOS decoding above.
bdosfn moveq #0,d0
move.b regc(regs),d0 ;Get BDOS function number.
cmp #(bdostabn-bdostab)/4,d0
blt.s dobdos ;Function number is within range.
badbdos move.b d0,-(sp)
move.l #ilgbdos,d1 ;Illegal or unsupported BDOS call
bsr pstring
move.b (sp)+,d1
bsr pbyte
move.l #atmsg,d1
bsr pstring
move.b 1(pseudosp),d1 ;Address where called (top stack entry)
ror.w #8,d1
move.b 0(pseudosp),d1
bsr pword
bsr pcrlf
bsr dump
bra quitprg
ilgbdos dc.b cr,lf,'Illegal BDOS call $'
bdosmsg dc.b 'BDOS call $'
dobdos move.l d0,-(sp) ;Save BDOS function number.
tst.b btrcflg ;Trace BDOS calls?
beq.s bdostrx ;No.
move.l #bdosmsg,d1
bsr pstring
move.l (sp),d1
bsr pbyte
move.l #atmsg,d1
bsr pstring
move.b 1(pseudosp),d1
ror.w #8,d1
move.b 0(pseudosp),d1
bsr pword
bsr pcrlf
move.l (sp),d0
bdostrx cmpi.b #10,d0 ;BDOS function 10 or higher?
bcs.s bdosjmp ;No.
bsr dmpstr ;Dump any outstanding console output.
move.l (sp),d0 ;Restore BDOS function number.
bdosjmp asl #2,d0 ;Multiply function number by 4.
addi.l #bdostab,d0 ;Point at address table entry.
movea.l d0,a0
movea.l (a0),a0 ;Point to appropriate service routine.
move.l (sp)+,d0 ;Restore BDOS function number.
moveq #0,d1
move.w regd(regs),d1 ;Get argument.
jmp (a0) ;Jump to the routine.
* Return here after performing the BDOS function.
results movem.l (sp)+,a1/a6
moveq #0,rega
move.b newrega,rega ;Get new accumulator value.
* We have finished processing the BDOS function.
move.b rega,d0 ;Set flags.
and.w regconff,d0
move.b 0(flagptr,d0.w),regf
rts
*
* Individual BDOS service routines
*
bdos00 bra quitprg ;Exit program.
bdos01 bsr dmpstr ;Console input
move.l rawhand,d1
move.l #newrega,d2
moveq #1,d3
sys Read
bra results
bdos02 move.b rege(regs),d1 ;Console output
clr.b testdol ;Allow dollar signs
bsr pchar
bra results
bdos03 equ badbdos ;Reader input
bdos04 equ badbdos ;Punch output
bdos05 pea rege(regs) ;List output byte
bdos05t tst.b listopn ;Is the printer already open?
bne.s bdos05w ;Yes.
move.l #prtname,d1
move.l #MODE_NEWFILE,d2
sys Open ;Open the printer.
move.l d0,prthand ;Save the file handle.
bne.s 1$ ;The open was successful.
move.l #badprt,d1
bsr pstring ;Indicate an unsuccessful open.
bsr dump ;Spill guts...
bra quitprg ; and exit.
1$ move.b #1,listopn ;Indicate that the list device is open.
bdos05w move.l prthand,d1
move.l (sp)+,d2 ;Character to send to the list device
moveq #1,d3 ;Just send one byte.
sys Write ;Send the byte to the list device.
bra results
prtname dc.b 'PRT:RAW',0
badprt dc.b 'Unable to open the list device!$'
bdos06 cmpi.b #$FF,rege(regs) ;Direct console I/O
bne bdos02 ;Send the byte.
bsr dmpstr ;Dump any outstanding output.
move.l rawhand,d1
moveq #1,d2 ;Wait for one microsecond.
sys WaitForChar ;Check whether a character is ready.
tst.l d0 ;Is a character ready?
bne bdos01 ;Yes - get it.
clr.b newrega ;Indicate that nothing is ready.
bra results
bdos07 move.b 3(targbase),newrega ;Get IOBYTE
bra results
bdos08 move.b rege(regs),3(targbase) ;Set IOBYTE
bra results
bdos09 add.l targbase,d1 ;Console output string
bsr pstring
bra results
bdos10 add.l targbase,d1 ;Console input line
movea.l d1,a0 ;The buffer is here.
bsr getline ;Get a line.
cmpi.b #3,2(a0) ;Was it a control-C?
bne results ;No - continue processing.
bra quitprg ;Terminate the program.
bdos11 move.l rawhand,d1 ;Console status check
moveq #1,d2 ;Wait for one microsecond.
sys WaitForChar ;Check whether a character is ready.
move.b d0,newrega ;Result is compatible with CP/M.
bra results
bdos12 clr.b regh(regs) ;Get system identification
move.b #$22,regl(regs) ;Pretend we're CP/M 2.2.
move.b #$22,newrega ;Some programs use undocumented return reg.
clr.b regb(regs)
bra results
bdos13 move.b 4(targbase),d0 ;Reset all drives
andi.b #$0F,d0 ;Current drive
moveq #1,d1
lsl.w d0,d1 ;Set up drive bit.
move.w d1,acmap ;Set active drive map to current drive only.
clr.w romap ;Reset read-only map.
bra results
bdos14 move.b rege(regs),d0 ;Select drive
andi.b #$0F,d0 ;Isolate drive bits.
andi.b #$F0,4(targbase)
or.b d0,4(targbase) ;Insert new bits.
moveq #1,d1
lsl.w d0,d1 ;Set up drive bit.
or.w d0,acmap ;Add new drive to active drive map.
bra results
bdos15 move.l d1,-(sp) ;Open existing file
bsr gethand
tst.l d1 ;Is the file already open?
beq.s bdos15g ;No - go ahead.
clr.l (a1) ;Clear file handle table entry.
sys Close ;Close the file.
bdos15g move.l (sp)+,d1
add.l #target,d1
move.l #MODE_OLDFILE,d2
movea.l d1,a0 ;The FCB is here.
bsr mapdrv ;Get drive map bit.
bdos15o move.l a0,-(sp)
lea opnname,a1 ;Build AmigaDOS file name here.
move.l a1,d1 ;We'll need it here.
bsr convfn ;Make a file name.
sys Open ;Open the file.
move.l (sp)+,a1 ;The FCB is here.
lea handles,a0
moveq #(handlen-handles)/16-1,d1
move.b #$FF,newrega ;Assume the open failed.
tst.l d0 ;Did it fail?
beq results ;Yes.
clr.b newrega ;Set flag to indicate success.
1$ tst.l (a0) ;Available handle entry?
beq.s 2$ ;Yes.
lea 16(a0),a0 ;Check the next entry.
dbra d1,1$
move.l d0,d1 ;File handle table overflow!
sys Close ;Close the file.
move.l #fullmsg,d1
bsr pstring ;Display an error message
bra quitprg ; and forget the whole thing.
2$ move.l d0,(a0)+ ;Save the file handle.
moveq #11,d0
3$ move.b (a1)+,(a0)+ ;Move first 12 bytes of FCB to table.
dbra d0,3$
move.w newdmap,d0
or.w d0,acmap ;Add drive to active drive map.
bra results
fullmsg dc.b 'Too many files are open!',cr,lf,'$'
bdos16 move.b #$FF,newrega ;Close file
bsr gethand ;Get the file handle.
tst.l d1 ;Did we find it?
beq results ;No - return failure code.
clr.l (a1) ;Clear the handle table entry.
sys Close ;Close the file.
clr.b newrega ;Indicate success.
bra results
bdos18 lea renname,a0 ;Search for next file
bra.s bdos17i
bdos17 lea srchnam,a0 ;Search for first file
bdos17i move.b #$FF,newrega ;Assume we'll fail.
add.l targbase,d1
movea.l d1,a1 ;The FCB is here.
addq.l #1,a1 ;The file name is here.
moveq #10,d0
1$ cmp.b #'*',(a1) ;Is this an asterisk?
beq.s 2$ ;Yes - replace with question marks.
move.b (a1)+,(a0)+ ;Move the current character.
dbra d0,1$
bra.s bdos17c
2$ move.b #'?',(a0)+ ;Convert to question marks.
addq.l #1,a1
subq #1,d0
bmi.s bdos17c ;The asterisk was in the extension.
cmpi.w #2,d0 ;End of file name?
bne.s 2$ ;No - insert another question mark.
bra.s 1$ ;Start the extension field.
bdos17c lea target,targbase
cmpi.b #18,regc(regs) ;Search for next file?
bne.s bdos17k ;No.
lea srchnam,a0
lea renname,a1
moveq #10,d0
1$ cmp.b (a0)+,(a1)+ ;Search for the same name as last time?
bne results ;No - exit with failed status.
dbra d0,1$
tst.l fibsize ;Is the last file completely done?
beq.s bdos17k ;Yes - try for another one.
pea 0 ;Housekeeping for the stack
bra.s bdos17b ;Create an entry for the next extent.
bdos17k move.l #null,d1
move.l #ACCESS_READ,d2
sys Lock ;Get a file lock.
tst.l d0 ;Did we fail?
beq results ;Yes - exit.
move.l d0,-(sp) ;Save the lock.
move.l d0,d1 ;Put it here for Examine.
move.l #fib,d2
lea target,targbase
move.w newdmap,d0
or.w d0,acmap ;Add drive to active drive map.
cmpi.b #18,regc(regs) ;Search for next file?
beq.s bdos17n ;Yes.
sys Examine ;Set up to find the first file.
tst.l d0 ;Did we succeed?
beq bdos17x ;No.
bdos17n move.l (sp),d1
move.l #fib,d2
sys ExNext ;Look for the next file.
tst.l d0 ;Did we succeed?
beq bdos17x ;No.
tst.l fibtype ;Is this a file entry?
bpl bdos17n ;No - ignore subdirectory entries.
clr.w ext17 ;Clear extent number.
bdos17b move.l dmaaddr,a1 ;Build a fake FCB here.
clr.b (a1)+ ;Clear drive code.
move.l a1,-(sp) ;The file name starts here.
moveq #10,d0
1$ move.b #' ',(a1)+ ;Clear file name and extension.
dbra d0,1$
moveq #19,d0
2$ clr.b (a1)+ ;Clear remainder of FCB to zeros.
dbra d0,2$
lea fibname,a0 ;A0 scans AmigaDOS file name.
move.l (sp)+,a1 ;A1 builds CP/M file name.
bdos17f tst.b (a0) ;End of file name?
beq.s 2$ ;Yes.
move.b (a0)+,d0 ;Get the current character.
cmpi.b #'a',d0 ;Is it lower case?
bcs.s 1$ ;No - leave it alone.
cmpi.b #'z'+1,d0
bcc.s 1$
subi.b #'a'-'A',d0 ;Convert to upper case.
1$ move.b d0,(a1)+ ;Move the (possibly-converted) character.
cmp.b #'.',(a0) ;Start of file name extension?
bne.s bdos17f ;No.
move.l dmaaddr,d0
add.w #9,d0 ;Point to start of extension field.
cmpa.l d0,a1 ;Is file name too long?
bhi bdos17n ;Yes - not a valid CP/M file name - ignore it.
move.l d0,a1
addq.l #1,a0 ;Skip over period.
bra.s bdos17f ;Get the file name extension.
2$ move.l dmaaddr,d0
add.w #13,d0
cmpa.l d0,a1 ;Is the file name too long?
bhi bdos17n ;Yes - ignore it.
move.l dmaaddr,a0
addq.l #1,a0
lea srchnam,a1
moveq #10,d0
3$ cmpi.b #'?',(a1) ;Wild card character?
bne.s 4$ ;No - compare it.
addq.l #1,a0 ;Skip over wild card character.
addq.l #1,a1
dbra d0,3$
bra.s bdos17w ;We found a wild card match.
4$ cmp.b (a0)+,(a1)+ ;Does the file name match the search name?
bne bdos17n ;No - try for another one.
dbra d0,3$ ;Check the next character.
bdos17w lea target,targbase
moveq #0,d1
move.w regd(regs),d1
moveq #0,d0
move.b 12(targbase,d1.l),d0 ;Extent flag
beq.s bdos17h ;Extent zero - take it.
cmpi.b #'?',d0 ;Search for all extents?
beq.s bdos17h ;Yes - take this one.
move.w d0,ext17 ;Save extent number.
moveq #14,d1
lsl.l d1,d0 ;Multiply by 16384 to get byte displacement.
sub.l d0,fibsize ;Beyond end of file? (Adjust byte count!)
bcs bdos17n ;Yes - forget this entry.
bdos17h clr.b newrega ;Set success flag.
move.l dmaaddr,a0 ;The FCB is here.
move.w ext17,d0 ;Current extent number (counts up from zero)
move.b d0,12(a0) ;Insert it into the FCB.
andi.b #$1F,12(a0) ;Only the low-order 5 bits go here.
lsr.l #5,d0
move.b d0,14(a0) ;High-order portion of extent number
move.l fibsize,d0 ;Number of bytes remaining
move.b #$80,15(a0) ;Assume the current extent is full.
cmpi.l #16384,d0 ;Is it?
bcc.s 1$ ;Yes.
and.l #$3FFF,d0 ;Number of bytes in the last extent
add.w #$7F,d0 ;(for rounding)
lsr.l #7,d0 ;Number of 128-byte records in extent
move.b d0,15(a0) ;Store record count in FCB.
1$ lea target,targbase
moveq #0,d1
move.w regd(regs),d1
cmpi.b #'?',12(targbase,d1.l) ;Search for all extents?
bne.s 2$ ;No - we're done with this file.
addq.w #1,ext17 ;Bump extent counter.
move.l #16384,d0
sub.l d0,fibsize ;Decrement remaining byte count
bhi.s bdos17x ;There's more to do.
2$ clr.l fibsize ;Set remaining count to zero.
bdos17x move.l (sp)+,d1 ;Get the lock.
beq results ;Ignore dummy entry for multi-extent file.
sys UnLock ;Release the lock.
bra results
bdos19 add.l targbase,d1 ;Delete file
movea.l d1,a0 ;The FCB is here.
bsr mapdrv ;Get drive map bit.
and.w romap,d1 ;Is this drive read-only?
bne roerr ;Yes - abort the deletion.
lea opnname,a1 ;Build AmigaDOS file name here.
move.l a1,d1 ;We'll need it here.
bsr convfn ;Make a file name.
sys DeleteFile ;Delete the file.
move.w newdmap,d0
or.w d0,acmap ;Add drive to active drive map.
bra results
bdos20 clr.b newrega ;Sequential read
bsr gethand
tst.l d1
beq.s 1$
move.l dmaaddr,d2
move.l #128,d3
sys Read
tst.l d0 ;Were we successful?
bgt results ;Yes.
1$ move.b #$1,newrega ;Set end-of-file flag.
bra results
bdos21 clr.b newrega ;Sequential write
bsr gethand
tst.l d1
beq.s 1$
move.l d1,-(sp)
bsr mapdrv ;Get drive map bit.
move.w d1,d0
move.l (sp)+,d1
and.w romap,d0 ;Is this drive read-only?
bne.s roerr ;Yes - error
move.l dmaaddr,d2
move.l #128,d3
sys Write
tst.l d0 ;Were we successful?
bgt results ;Yes.
1$ move.b #$FF,newrega ;Set failure flag.
bra results
bdos22 move.l d1,-(sp) ;Make new file
bsr gethand
tst.l d1 ;Is the file already open?
beq.s bdos22g ;No - go ahead.
clr.l (a1) ;Clear file handle table entry.
sys Close ;Close the file.
bdos22g move.l (sp)+,d1
add.l #target,d1
move.l #MODE_NEWFILE,d2
movea.l d1,a0 ;The FCB is here.
bsr mapdrv ;Get the drive map bit.
and.w romap,d1 ;Is the drive read-only?
beq bdos15o ;No - continue with BDOS 15 open routine
roerr move.l #1$,d1
bsr pstring ;'BDOS Error on '
bsr ucase
move.b d0,d1
add.b #'A',d1
bsr pchar ;Drive letter
move.l #2$,d1
bsr pstring ;': R/O'
bra quitprg ;Abort the program.
1$ dc.b 'BDOS Error on $'
2$ dc.b ': R/O',cr,lf,'$'
bdos23 add.l targbase,d1 ;Rename file
movea.l d1,a0
move.l a0,-(sp)
bsr mapdrv ;Get drive map bit.
and.w romap,d1 ;Is this drive read-only?
bne roerr ;Yes - error
lea opnname,a1
bsr convfn ;Convert old file name.
move.l (sp)+,a0
lea 16(a0),a0
lea renname,a1
bsr convfn ;Convert new file name.
move.l #opnname,d1
move.l #renname,d2
sys Rename ;Rename the file.
move.w newdmap,d1
or.w d1,acmap ;Add drive to active drive map.
clr.b newrega ;Assume we succeeded.
tst.l d0 ;Did we fail?
bne results ;No.
move.b #$FF,newrega
bra results
bdos24 move.w acmap,regh(regs);Get active drive map
bra results
bdos25 move.b 4(targbase),newrega ;Get default drive number
andi.b #$0F,newrega ;Isolate drive bits.
bra results
bdos26 add.l targbase,d1 ;Set file buffer address
move.l d1,dmaaddr
bra results
bdos27 move.w #fakealv-fdos+$FF00,regh(regs) ;Get allocation vector
move.b regl(regs),newrega ;Make undocumented copy.
move.b regh(regs),regb(regs)
move.l #null,d1
move.l #ACCESS_READ,d2
sys Lock ;Get a file lock.
move.l d0,d1 ;Did we fail?
beq results ;Yes - exit.
move.l d0,-(sp) ;Save the lock.
move.l #InfoData,d2
sys Info ;Get disk information.
tst.l d0 ;Did we fail?
beq bdos27x ;Yes - exit.
lea fakealv-fdos+$FF00,a0
adda.l targbase,a0 ;A0 loads allocation vector.
move.b #$F0,(a0)+ ;Initial allocation for directory
clr.b (a0)+
move.l a0,-(sp)
move.l id_NumBlocks,d1 ;Number of 512-byte blocks on disk
addq.l #3,d1
lsr.l #2,d1 ;Convert to 2048-byte blocks for CP/M.
move.l d1,d0
subq.l #1,d0
move.b d0,fakedpb+5 ;Insert high block number in DPB.
lsr.l #8,d0
move.b d0,fakedpb+6
1$ clr.b (a0)+ ;Clear remainder of allocation vector.
dbra d1,1$
move.l (sp)+,a0
move.l id_NumBlocksUsed,d0 ;Number of 512-byte blocks used
moveq #11,d1
lsr.w #2,d0 ;CP/M (2048-byte) blocks used
beq.s bdos27x ;Nothing is allocated.
move.w d0,d1
and.w #7,d1 ;Number of bits to set in partial ALV byte
beq.s 2$ ;No partial byte
subq.w #1,d1 ;Number of bits less one
move.b #$80,d2 ;Here's one bit.
asr.b d1,d2 ;Make some more.
move.b d2,(a0)+ ;Store partial allocation vector byte.
2$ lsr.w #3,d0 ;Number of ALV bytes to set all bits in
beq bdos27x ;There are none - we're done.
subq.w #1,d0
3$ move.b #$FF,(a0)+ ;Set all bits in these ALV bytes.
dbra d0,3$
bdos27x move.l (sp)+,d1
sys UnLock ;Release the file lock.
bra results
bdos28 move.b 4(targbase),d0 ;Protect drive
andi.b #$0F,d0 ;Current drive
moveq #1,d1
lsl.w d0,d1 ;Set up drive bit.
or.w d1,romap ;Add it to read-only map.
bra results
bdos29 move.w romap,regh(regs);Get read-only map
bra results
bdos30 equ badbdos ;Set file attributes
bdos31 move.w #fakedpb-fdos+$FF00,regh(regs) ;Get disk parameter block
move.b regl(regs),newrega ;Make undocumented copy.
move.b regh(regs),regb(regs)
bra results
bdos32 cmp.b #$FF,rege(regs) ;Get or set user code
bne.s 1$ ;Set it.
move.b 4(targbase),d0 ;Current drive and user code
lsr.b #4,d0 ;Set up user code.
move.b d0,newrega
move.b newrega,regl(regs)
move.b regh(regs),regb(regs)
bra results
1$ andi.b #$0F,4(targbase);Clear old user code.
move.b rege(regs),d0 ;Get new user code.
lsl.b #4,d0 ;Shift bits into position.
or.b d0,4(targbase) ;Insert new user code.
bra results
bdos33 pea _LVORead(a6) ;Direct access read
clr.b newrega
bsr gethand
bra.s bdos34c ;Use common read/write routine.
bdos34 pea _LVOWrite(a6) ;Direct access write
clr.b newrega
bsr gethand
move.l d1,-(sp)
bsr mapdrv ;Get drive map bit.
move.w d1,d0
move.l (sp)+,d1
and.w romap,d0 ;Is this drive read-only?
bne roerr ;Yes - error
bdos34c move.l d1,-(sp) ;Save file handle (common read/write routine)
moveq #0,d2
move.b 35(a0),d2 ;Get seek address.
rol.l #8,d2
move.b 34(a0),d2
rol.l #8,d2
move.b 33(a0),d2
rol.l #7,d2 ;Convert record number to byte displacement.
move.b 33(a0),32(a0) ;Set up current record number in extent.
andi.b #$7F,32(a0)
moveq #14,d0
ror.l d0,d2
move.b d2,12(a0) ;Current extent number
rol.l d0,d2
moveq #-1,d3
sys Seek ;Seek to desired position.
move.l (sp)+,d1 ;Get the file handle again.
move.l (sp)+,a0 ;Address of read or write routine
tst.l d0 ;Were we successful?
bmi 1$ ;No.
move.l dmaaddr,d2
move.l #128,d3
jsr (a0) ;Read or write the desired record.
tst.l d0 ;Were we successful?
bgt results ;Yes.
1$ move.b #6,newrega ;Set failure (invalid address) flag.
bra results
bdos35 bsr gethand ;Get file end address
move.l a0,-(sp) ;Pointer to FCB
move.l d1,-(sp)
moveq #0,d2
moveq #1,d3
sys Seek ;Jump to end of file.
move.l (sp)+,d1
move.l d0,d2 ;Old position
moveq #-1,d3
sys Seek ;Go back to the old position.
move.l (sp)+,a0 ;Restore FCB pointer.
add.l #$7F,d0 ;Adjust for partial final block, if any.
lsr.l #7,d0 ;File size in 128-byte records
move.b d0,33(a0) ;Insert file size into FCB.
lsr.l #8,d0
move.b d0,34(a0)
lsr.l #8,d0
move.b d0,35(a0)
bra results
bdos36 bsr gethand ;Get direct address
move.l a0,-(sp) ;Save pointer to FCB.
moveq #0,d2
moveq #0,d3
sys Seek ;Seek to current position.
move.l (sp)+,a0 ;Restore FCB pointer.
lsr.l #7,d0 ;Convert to 128-byte record number.
beq.s bdos36m ;Beginning of file
subq.l #1,d0 ;Back up to record just read.
bdos36m move.b d0,33(a0) ;Insert position into FCB.
lsr.l #8,d0
move.b d0,34(a0)
lsr.l #8,d0
move.b d0,35(a0)
bra results
*
* Individual BIOS service routines
*
bios01 bra quitprg ;Warm boot
bios02 equ bdos11 ;Console status check
bios03 equ bdos01 ;Console input byte
bios04 move.b regc(regs),d1 ;Console output byte
clr.b testdol ;Allow dollar signs
bsr pchar
bra results
bios05 pea regc(regs) ;List output byte
bra bdos05t
bios06 equ badbios ;Punch output byte
bios07 equ badbios ;Reader input byte
bios08 equ badbios ;Home disk
bios09 equ badbios ;Select disk
bios10 equ badbios ;Set track
bios11 equ badbios ;Set sector
bios12 equ badbios ;Set DMA address
bios13 equ badbios ;Read disk
bios14 equ badbios ;Write disk
bios15 move.b #$FF,newrega ;List status
bra results
*
* Set "newdmap" with a bit corresponding to the drive code in the
* FCB pointed to by A0 (or the default drive if necessary).
* The contents of "newdmap" will also be in the low-order word of D1.
* Register D0 will be set to 0 for drive A:, 1 for drive B:, 2 for C:, etc.
* The contents of register A0 will be preserved.
*
mapdrv move.b (a0),d0 ;Drive code
bne.s 1$ ;The drive is specified.
move.b 4(targbase),d0 ;Get the default drive.
andi.b #$0F,d0
addq #1,d0
1$ subq.b #1,d0 ;Adjust drive code so that 0 is A:, etc.
moveq #1,d1
lsl.w d0,d1 ;Set up drive map bit.
move.w d1,newdmap ;Save it.
rts
*
* Simulation of the Z-80 LD A,R instruction -
* load a random 7-bit value into the accumulator.
*
movear move.l #dtstamp,d1
movem.l a1/a6,-(sp)
move.l _DOSBase,a6
sys DateStamp
movem.l (sp)+,a1/a6
move.b dtstamp+11,rega ;Low-order 7 bits of timer ticks
andi.b #$7F,rega
jmp (return)
*
* End of program, one way or another
*
quitprg move.l savesp,sp ;Restore stack pointer.
bsr dmpstr ;Dump any outstanding console output.
clr.w romap ;Clear the read-only map.
* Close the serial.device if it was used.
tst.l rpport
beq.s closlis ;Nothing was opened.
tst.l wpport
beq.s q3 ;The output port wasn't opened.
lea writreq,a1
move.l _SysBase,a6
sys CloseDevice ;Close the serial output device.
move.l wpport,-(sp)
jsr _DeletePort ;Delete the serial output port.
addq.l #4,sp
clr.l wpport
q3 lea readreq,a1
move.l _SysBase,a6
sys CloseDevice ;Close the serial input device.
move.l rpport,-(sp)
jsr _DeletePort ;Delete the serial input port.
addq.l #4,sp
clr.l rpport
* If the list device was used, close it.
closlis tst.b listopn ;Is the printer open?
beq.s closprt ;No.
move.l prthand,d1
sys Close ;Close the printer.
closprt clr.b listopn ;Reset the "printer-open" flag.
* If any files were left open by the last program, close them.
lea handles,a0
moveq #(handlen-handles)/16-1,d0
closall move.l (a0),d1
beq.s closnxt ;This file isn't open.
movem.l a0/d0,-(sp)
move.l _DOSBase,a6
sys Close ;Close this file.
movem.l (sp)+,a0/d0
clr.l (a0) ;Clear the file handle.
closnxt lea 16(a0),a0
dbra d0,closall
* Check whether we should quit the simulation.
tst.b quitflg ;Exit the simulator?
bne.s exitsim ;Yes.
tst.b cmdflag ;Was .COM file loaded from command line?
beq nextprg ;No - re-display the command prompt.
* Terminate execution of the simulator.
exitsim move.l rawhand,d1 ;Is RAW: open?
beq.s closlib ;No.
move.l _DOSBase,a6
sys Close ;Close RAW:
closlib move.l _SysBase,a6
move.l _DOSBase,a1
sys CloseLibrary ;Close dos.library.
moveq #0,d0 ;Return with no error.
rts ;All done
page
*************************************************************************
* *
* AmigaDOS interface routines *
* *
*************************************************************************
*
* Get a line from the console. CP/M BDOS 10 conventions are used.
* A0 is assumed to point to the start of the buffer.
* If the first character encountered is a control-C, this routine
* exits, leaving just the control-C in the buffer.
*
getline movem.l d2-d3/a0-a1/a6,-(sp)
bsr dmpstr ;Flush the screen buffer first.
move.l _DOSBase,a6
lea 2(a0),a1 ;The current character loads here.
clr.b 1(a0) ;Clear character count.
getlinl move.l rawhand,d1 ;Read from RAW:
move.l a1,d2 ; into current position
moveq #1,d3 ; for a length of one byte.
movem.l d1-d3/a0-a1,-(sp)
sys Read ;Get a character.
movem.l (sp)+,d1-d3/a0-a1
cmpi.b #cr,(a1) ;Did we get a carriage return?
beq.s getlinc ;Yes - stop here.
cmpi.b #lf,(a1) ;Stop on a line feed too.
beq.s getlinc
cmpi.b #bs,(a1) ;Backspace?
bne.s getlinp ;No.
tst.b 1(a0) ;Do we have anything yet?
beq.s getlinl ;No - ignore the backspace.
subq.l #1,a1 ;Back over the previous character.
subq.b #1,1(a0) ;Decrement character count.
movem.l a0-a1,-(sp)
move.l #bsmsg,d1
bsr pstring ;Erase the previous character on the screen.
movem.l (sp)+,a0-a1
bra.s getlinl
bsmsg dc.b bs,' ',bs,'$' ;Erases the previous character
getlinp movem.l a0-a1,-(sp)
sys Write ;Echo the current character.
movem.l (sp)+,a0-a1
addq.b #1,1(a0) ;Bump character count.
move.b 1(a0),d0 ;Number of bytes read so far.
cmpi.b #3,(a1)+ ;Did we get a control-C?
bne.s 1$ ;No.
cmpi.b #1,d0 ;Is is the first character?
beq.s getlinx ;Yes - exit now.
1$ cmp.b (a0),d0 ;Is the buffer full?
bne.s getlinl ;No - try for another character.
bra.s getlinx
getlinc bsr pcrlf ;Carriage return or line feed
getlinx movem.l (sp)+,d2-d3/a0-a1/a6
rts ;Exit.
*
* Display the message pointed to by D1.
* The message must be terminated by a dollar sign.
*
pstring movem.l d2-d3/a1-a2,-(sp) ;Save work registers.
move.l d1,a0 ;A0 scans the message.
bset #0,testdol ;Suppress $ test?
beq.s pstrs ;Yes (used by BDOS/BIOS character output)
cmpi.b #'$',(a0) ;Null string?
beq pstrx ;Yes - do nothing.
pstrs move.l strptr,a1 ;A1 loads the output buffer.
move.l #strbufn,d3
sub.l a1,d3 ;Number of bytes left in buffer
ifne h19
moveq #0,d0
move.w esclen,d0 ;Is a partial escape sequence saved?
beq.s pstrl ;No.
lea escbuf,a2
adda.l d0,a2 ;Continue loading it here.
clr.w esclen ;Reset "saved length" counter.
cmpi.w #2,d0 ;Did we just save one byte?
bcs.s pstresc ;Yes - get the remainder.
bhi pstreY2 ;Get the last cursor positioning byte.
subq.l #1,a2 ;Back over dummy byte.
bra pstreY1 ;Get both cursor positioning bytes.
endc
pstrl cmpi.b #lf,(a0) ;Line feed?
bne.s notlf ;No.
lea escbuf,a2 ;Translate it to a cursor-down sequence.
move.b #$9B,(a2)+
move.b #'B',(a2)+
addq.l #1,a0
bra pstrsub
notlf:
ifne h19
* Optional H19 escape sequence translation
cmpi.b #esc,(a0) ;Escape character?
bne pstrm ;No - treat it normally.
lea escbuf,a2 ;Build translated escape sequence here.
move.b #$9B,(a2)+ ;Start with an AmigaDOS escape character.
addq.l #1,a0 ;Check the next character.
cmpi.b #'$',(a0) ;End of string?
bne.s pstresc ;No - analyze the sequence.
move.w #1,esclen ;We've saved one byte for next time.
bra pstrw ;Write everything up to the ESC character.
pstresc move.b (a0)+,d0
cmpi.b #'[',d0 ;ANSI escape sequence?
beq pstrsub ;Yes - pass the sequence with $9B header.
cmpi.b #'Y',d0
beq.s pstreY ;Set cursor position.
cmpi.b #'@',d0
beq.s pstrein ;Set insert mode.
cmpi.b #'A',d0
bcs pstreun ;Unknown code - copy it as is.
cmpi.b #'O',d0
beq.s pstreO ;Reset insert mode.
bhi pstreun ;Unknown code
move.l a0,-(sp)
lea esctran(pc),a0 ;Translation table with offset
move.b -'A'(a0,d0.w),d2;Get the translated code.
move.l (sp)+,a0
btst #6,d2 ;Does the translated code stand alone?
bne.s 1$ ;No.
subq.l #1,a2 ;Back over stored CSI character.
1$ move.b d2,(a2)+ ;Get the translated code.
bra.s pstrsub
esctran dc.b 'ABCD',ff,so,si,'H',$8D,'JKLMP' ;Escape sequence translation
pstrein move.b #1,insflag ;Set insert mode.
bra.s pstrsbx
pstreO clr.b insflag ;Reset insert mode.
bra.s pstrsbx
pstreY cmpi.b #'Y',d0 ;Set cursor position
bne.s pstreun
cmpi.b #'$',(a0) ;End of string?
bne.s pstreY1 ;No.
move.w #2,esclen ;Indicate we need both position bytes.
bra pstrw ;Finish the sequence next time.
pstreY1 moveq #0,d0
move.b (a0)+,d0 ;Get the first position byte.
bsr pstrcvd ;Convert to decimal in save area.
move.b #';',(a2)+ ;Add the separator character.
cmpi.b #'$',(a0) ;End of string?
bne.s pstreY2 ;No.
sub.l #escbuf,a2 ;Number of bytes saved
move.w a2,esclen
bra pstrw ;Get the last byte next time.
pstreY2 moveq #0,d0
move.b (a0)+,d0 ;Get the last position byte.
bsr pstrcvd ;Convert to decimal in save area.
move.b #'H',(a2)+ ;Terminate the sequence.
bra.s pstrsub
pstreun move.b #esc,escbuf ;Unidentified escape sequence -
move.b d0,(a2)+ ; pass it through as is.
endc
* The translated escape sequence is now in "escbuf" -
* copy it to the output buffer.
pstrsub move.l a2,d0
lea escbuf,a2 ;A2 scans translated escape sequence.
sub.l a2,d0 ;Length of translated escape sequence
subq.l #1,d0
1$ move.b (a2)+,(a1)+ ;Copy substitution to output string.
subq.w #1,d3 ;Count down remaining length.
dbra d0,1$
pstrsbx cmpi.b #'$',(a0) ;End of string?
beq pstrw ;Yes - write it out.
tst.w d3 ;Is the buffer full?
bmi pstrw ;Yes - write out what we have.
cmpi.b #lf,-1(a0) ;Line feed?
bne pstrl ;No.
tst.b bufflag ;Is console buffering in effect?
beq pstrl ;No.
move.l a1,strptr
bsr dmpstr ;Dump the buffer.
move.l strptr,a1
bra pstrl ;Check for another escape sequence.
* Subroutine to convert the byte in D0 to a character string at (A2)+
pstrcvd subi.b #' '-1,d0 ;Convert to binary row or column number.
divu #10,d0 ;Convert to tens and units.
tst.w d0 ;Is the number 10 or greater?
beq.s 1$ ;No - just create a one-digit number.
addi.b #'0',d0 ;Convert the tens digit to ASCII.
move.b d0,(a2)+ ;Move it to the result field.
1$ swap d0 ;Get the units digit.
addi.b #'0',d0 ;Convert it to ASCII.
move.b d0,(a2)+
rts
* Normal character processing
pstrm tst.b insflag ;Are we in insert mode?
beq.s pstrmv ;No.
lea escbuf,a2
move.b #$9B,(a2)+ ;Build an insert-character sequence.
move.b #'@',(a2)+
move.b (a0)+,(a2)+ ;Here's the character to insert.
bra.s pstrsub ;Use the substitution routine.
pstrmv move.b (a0)+,(a1)+ ;Move one character.
tst.b bufflag ;Is console buffering in effect?
beq.s 2$ ;No.
cmpi.b #cr,-1(a0) ;Carriage return?
beq.s 1$ ;Yes - dump the current segment.
cmpi.b #bel,-1(a0) ;Bell?
bne.s 2$ ;No - continue buffering.
1$ move.l a1,strptr
bsr dmpstr ;Dump the buffer.
move.l strptr,a1
2$ cmpi.b #'$',(a0) ;Test for end of string.
dbeq d3,pstrl ;Loop until we get there or buffer is full.
pstrw move.l a1,strptr
tst d3 ;Is the buffer full?
bmi.s 1$ ;Yes - dump it regardless.
tst.b bufflag ;Is console buffering in effect?
bne.s 2$ ;Yes - don't write anything yet.
1$ bsr dmpstr ;Dump the buffer.
move.l strptr,a1
2$ tst d3 ;Did the output buffer overflow?
bmi pstrs ;Yes - get another section of the message.
pstrx movem.l (sp)+,d2-d3/a1-a2 ;Restore registers
rts
*
* Write the contents of "strbuf" to RAW: if possible, or stdout if not.
* The number of bytes to be written is calculated from "strptr".
*
dmpstr movem.l d2-d3/a0-a1/a6,-(sp)
move.l strptr,d3
move.l #strbuf,d2 ;Address of buffer
move.l d2,strptr ;Reset the buffer pointer.
sub.l d2,d3 ;Length of output string
beq.s 2$ ;Zero - don't write anything.
move.l rawhand,d1 ;Assume we're writing to RAW:
bne.s 1$
move.l stdout,d1 ;We don't have RAW: - use stdout.
1$ move.l _DOSBase,a6
sys Write ;Display the line.
2$ movem.l (sp)+,d2-d3/a0-a1/a6
rts
*
* Convert the file name in the FCB pointed to by A0
* to an AmigaDOS-format file name in the field pointed to by A1.
* D0 is the only other register used by this routine.
*
convfn move.l a1,-(sp)
move.l a0,-(sp) ;Save start address of FCB.
move.b (a0)+,d0 ;Get the drive code.
bne.s 1$ ;We have a drive code.
move.b target+4,d0 ;Use the default drive code.
andi.b #$0F,d0
addq.b #1,d0 ;Start at 1 for drive A:.
1$ cmpi.b #1,d0 ;Is it drive A:?
beq.s 4$ ;Yes - don't add anything special.
move.b #'C',(a1)+
move.b #'P',(a1)+ ;Set up the prefix CPMx:
move.b #'M',(a1)+ ; where x is the drive letter.
add.b #'A'-1,d0
move.b d0,(a1)+
move.b target+4,d0 ;Get user number.
lsr.b #4,d0 ;Move it to low-order 4 bits.
beq.s 3$ ;Don't insert user number if it's zero.
move.b #'0',(a1)+ ;Assume user number is less than 10.
cmpi.b #10,d0 ;Is user number 10 or greater?
bcs.s 2$ ;No.
move.b #'1',-1(a1) ;Change the first digit to 1.
subi.b #10,d0
2$ addi.b #'0',d0 ;Convert user number to ASCII.
move.b d0,(a1)+ ;Insert user number into file spec.
3$ move.b #':',(a1)+
4$ moveq #7,d0 ;Maximum of 8 characters for file name
convfn1 cmpi.b #' ',(a0) ;End of file name?
beq.s 3$ ;Yes
cmpi.b #'*',(a0) ;Wild card?
bne.s 1$ ;No.
move.b #'#',(a1)+ ;Convert to AmigaDOS format.
move.b #'?',(a1)+
addq.l #1,a0 ;Skip over the asterisk.
bra.s 2$
1$ move.b (a0)+,(a1)+ ;Move one character of file name.
2$ dbra d0,convfn1 ;Try for more.
3$ movea.l (sp)+,a0 ;Back to start of FCB.
lea 9(a0),a0 ;Go to start of file name extension.
cmpi.b #' ',(a0) ;Do we have an extension?
beq.s convfnx ;No.
move.b #'.',(a1)+ ;Insert extension separator.
moveq #2,d0 ;Maximum of 3 characters for extension.
convfn2 cmpi.b #' ',(a0) ;End of extension?
beq.s convfnx ;Yes.
cmpi.b #'*',(a0) ;Wild card?
bne.s 1$ ;No.
move.b #'#',(a1)+ ;Convert to AmigaDOS format.
move.b #'?',(a1)+
addq.l #1,a0 ;Skip over the asterisk.
bra.s 2$
1$ move.b (a0)+,(a1)+ ;Move one character of extension.
2$ dbra d0,convfn2 ;Try for more.
convfnx clr.b (a1) ;Terminate file name string.
move.l (sp)+,a1
rts
*
* Get the file handle indicated by the first 12 bytes of the CP/M FCB
* whose CP/M address is in D1. The file handle (if found) is copied
* to D1 from the file handle table entry (which is pointed to by A1).
* If the file handle cannot be found, D1 will be set to zero.
* In any event, A0 will be set to point to the FCB.
*
gethand lea 0(targbase,d1.l),a0 ;The FCB is here.
lea handles,a1 ;A1 scans the file handle table.
moveq #(handlen-handles)/16-1,d0
1$ movem.l d0/a0-a1,-(sp)
tst.l (a1) ;Is this entry empty?
beq.s 3$ ;Yes - ignore it.
addq.l #4,a1 ;Skip over to file name in table.
moveq #11,d1
2$ cmp.b (a0)+,(a1)+ ;Compare first 12 bytes of FCB with table.
bne.s 3$ ;No match.
dbra d1,2$
movem.l (sp)+,d0/a0-a1
move.l (a1),d1 ;Here's the file handle.
rts
3$ movem.l (sp)+,d0/a0-a1
lea 16(a1),a1 ;Try the next table entry.
dbra d0,1$
moveq #0,d1 ;Couldn't find the handle!
rts
page
*************************************************************************
* *
* Serial port routines *
* *
*************************************************************************
*
* Read a byte from the port whose number is in D0.
* A0 points to where to put it.
*
inp movem.l a1/a6/d2-d3,-(sp)
cmp.b #$14,d0 ;Port 14?
bne.s inp15 ;No.
move.l a0,-(sp)
tst.l rpport ;Is serial.device open?
bne.s 1$ ;Yes.
bsr initser ;Set up serial.device.
1$ bsr checkio
tst.l d0 ;Is a character ready?
beq.s 2$ ;No - give the previous one again.
bsr serread ;Read the new character.
2$ move.l (sp)+,a0
move.b charin,(a0) ;Move it to user's area.
bra.s inpx
inp15 cmp.b #$15,d0 ;Port 15?
bne.s inpx ;No - ignore it.
move.b #7,(a0) ;Assume a character is ready.
move.l a0,-(sp)
bsr checkio ;Check whether a character is ready.
move.l (sp)+,a0
tst.l d0 ;Is a character ready?
bne.s inpx ;Yes.
move.b #5,(a0) ;Don't set "receiver ready" bit.
inpx movem.l (sp)+,a1/a6/d2-d3
rts
*
* Write the byte pointed to by A0 to the port whose number is in D0.
*
outp movem.l a1/a6/d2-d3,-(sp)
cmp.l #$14,d0 ;Port 14?
bne.s outpx ;No - ignore it.
move.b (a0),charout ;Character to write
tst.l rpport ;Is serial.device open?
bne.s 1$ ;Yes.
bsr initser ;Set up serial.device.
1$ bsr serwrit ;Write the character.
outpx movem.l (sp)+,a1/a6/d2-d3
rts
*
* Initialize the serial port.
*
initser:
* Open a reply port for the serial input device.
lea readreq,a0
move.w #rsize-1,d0
1$ clr.b (a0)+ ;Clear the I/O request block.
dbra d0,1$
clr.l -(sp)
move.l #2$,-(sp)
jsr _CreatePort ;rpport = CreatePort ("Read_RS", NULL);
addq.l #8,sp
move.l d0,rpport
move.l d0,r_ReplyPort
bne.s openin
move.l #3$,d1
bsr pstring
bra quitprg
2$ dc.b 'Read_RS',0
3$ dc.b 'Can''t create input port for serial.device!',cr,lf,'$'
* Open the serial input device.
openin move.w #rsize,r_MLength
move.b #SERFLAGS,r_SerFlags
move.l #CTLCHAR,r_CtlChar
lea 1$,a0
moveq #0,d0
lea readreq,a1
moveq #0,d1
move.l _SysBase,a6
sys OpenDevice
tst.l d0 ;Were we successful?
beq.s setin ;Yes.
move.l #2$,d1
bsr pstring
bra quitprg
1$ dc.b 'serial.device',0
2$ dc.b 'Can''t open serial.device!',cr,lf,'$'
setin move.l baud,r_Baud
move.b bits,r_ReadLen ;Number of bits per character
move.b bits,r_WriteLen
move.w #SDCMD_SETPARAMS,r_Command
lea readreq,a1
move.l _SysBase,a6
sys DoIO ;Set new input port parameters.
* Set up the serial output port.
lea readreq,a0
lea writreq,a1
move.w #wsize-1,d0
copw move.b (a0)+,(a1)+ ;Clone the read request block.
dbra d0,copw
clr.l -(sp)
move.l #1$,-(sp)
jsr _CreatePort ;wpport = CreatePort ("Write_RS", NULL);
addq.l #8,sp
move.l d0,wpport
move.l d0,w_ReplyPort
bne.s startup
move.l #2$,d1
bsr pstring
bra quitprg
1$ dc.b 'Write_RS',0
2$ dc.b 'Can''t create output port for serial.device!',cr,lf,'$'
startup bsr setbaud ;Set parameters and start reading.
rts
*
* Return TRUE (D0 <> 0) if the serial port has a character.
*
checkio lea readreq,a1
move.l _SysBase,a6
sys CheckIO
rts
*
* Set the serial port's baud rate, number of data bits, etc.
*
setbaud tas frstset ;Is this the first call?
beq.s 1$ ;Yes - input port is set up.
lea readreq,a1
move.l _SysBase,a6
sys AbortIO ;Abort the outstanding read.
move.l baud,r_Baud ;Baud rate
move.b bits,r_ReadLen ;Number of bits per character
move.b bits,r_WriteLen
move.w #SDCMD_SETPARAMS,r_Command
lea readreq,a1
move.l _SysBase,a6
sys DoIO ;Set new input port parameters.
1$ move.w #CMD_READ,r_Command
move.l #1,r_Length
move.l #charinb,r_Data
lea readreq,a1
move.l _SysBase,a6
sys SendIO ;Start reading again.
* Now set up the output port - this one is more straightforward.
move.l baud,w_Baud
move.b bits,w_ReadLen
move.b bits,w_WriteLen
move.w #SDCMD_SETPARAMS,w_Command
lea writreq,a1
move.l _SysBase,a6
sys DoIO
rts
*
* Write the byte in "charout" to the serial port.
*
serwrit move.w #CMD_WRITE,w_Command
move.l #1,w_Length
move.l #charout,w_Data
lea writreq,a1
move.l _SysBase,a6
sys DoIO
rts
*
* Read a byte from the serial port into "charin".
* If a byte isn't ready, this routine will wait until one is.
*
serread lea readreq,a1
move.l _SysBase,a6
sys WaitIO ;Wait until a character is ready.
move.b charinb,charin ;Get the character from the buffer.
move.w #CMD_READ,r_Command
move.l #1,r_Length
move.l #charinb,r_Data
lea readreq,a1
move.l _SysBase,a6
sys SendIO ;Get ready for the next character.
rts
page
*************************************************************************
* *
* Miscellaneous service routines *
* (Inelegant, but rarely used so they stand as is.) *
* *
*************************************************************************
*
* Display the contents of D1 in hex.
*
pbyte move.l #$20018,d0 ;2 nybbles, 24-bit shift first
bra.s phex
pword move.l #$40010,d0 ;4 nybbles, 16-bit shift first
bra.s phex
paddr move.l #$60008,d0 ;6 nybbles, 8-bit shift first
bra.s phex
plong move.l #$80000,d0 ;8 nybbles, no shift first
phex lea workbuf,a0
move.l a0,-(sp)
bsr pdigits
move.b #'$',(a0)+
move.l (sp)+,d1
bsr pstring
rts
*
* Convert the contents of D1 to hex at (A0).
* On exit, A0 points to the next available byte.
*
ubyte move.l #$20018,d0 ;2 nybbles, 24-bit shift first
bra.s pdigits
uword move.l #$40010,d0 ;4 nybbles, 16-bit shift first
bra.s pdigits
uaddr move.l #$60008,d0 ;6 nybbles, 8-bit shift first
bra.s pdigits
ulong move.l #$80000,d0 ;8 nybbles, no shift first
pdigits rol.l d0,d1 ;Do shift.
bra.s 3$
1$ swap d0 ;Save nybble count.
rol.l #4,d1 ;Print variable in d1.
move.l d1,-(sp)
and #$F,d1 ;Isolate the current nybble.
cmp #$A,d1
bcs.s 2$
add.b #'A'-'9'-1,d1 ;Adjust for digits A through F.
2$ add.b #'0',d1 ;Convert to ASCII.
move.b d1,(a0)+ ;Add to the result string.
move.l (sp)+,d1
3$ swap d0 ;Get nybble count.
dbra d0,1$
rts
pchar move.b d1,workbuf ;Print the character in D1.
move.b #'$',workbuf+1
move.l #workbuf,d1
bsr pstring
rts
pspace move.l #1$,d1 ;Print a space.
bsr pstring
rts
1$ dc.b ' $'
pcrlf move.l #1$,d1 ;Print a carriage return and line feed.
bsr pstring
rts
1$ dc.b cr,lf,'$'
*
* Convert the hex string pointed to by A0 to long in d1.
* Stops on the first invalid hex digit, which is returned in d0.
* A0 is left pointing to this first invalid digit.
* d2 = 1 if any valid digits were found, 0 otherwise.
*
atol moveq #0,d1
moveq #0,d2
1$ move.b (a0)+,d0 ;Get the current byte.
cmpi.b #$60,d0
bcs.s 2$
andi.b #$5F,d0 ;Mask to upper case.
2$ cmpi.b #'0',d0 ;Check range (0..9,A..F).
bcs.s atolend
cmpi.b #'F',d0
bhi.s atolend
cmpi.b #'9',d0
bls.s 3$
cmpi.b #'A',d0
bcs.s atolend
3$ moveq #1,d2 ;Valid characters entered, set flag.
sub.b #'0',d0 ;Convert to binary
cmpi.b #$9,d0 ;Digit in range 0..9?
bls.s 4$ ;Yes - conversion is complete
sub.b #'A'-'9'-1,d0 ;Adjust digits A..F.
4$ ext d0 ;Convert to long.
ext.l d0
asl.l #4,d1 ;Tack it onto d1.
add.l d0,d1
bra.s 1$ ;Try for another digit.
atolend subq.l #1,a0 ;Back onto the first invalid digit.
rts
page
*************************************************************************
* *
* Instruction mnemonic table (used for tracing) *
* *
*************************************************************************
data data
* This table contains the mnemonic strings for the 8080/Z-80 opcodes.
*
* "q" denotes a register number in bits 3 through 5 of the opcode.
* Values are interpreted as follows:
* Normal 8080 Normal Z-80 DD prefix FD prefix
* 000 B B B B
* 001 C C C C
* 010 D D D D
* 011 E E E E
* 100 H H XH YH
* 101 L L XL YL
* 110 M (HL) (IX+n) (IY+n)
* 111 A A A A
*
* "r" denotes a register number in bits 0 through 2 of the opcode.
* Values are interpreted the same as for "q" above.
*
* "p" denotes a 2-bit register pair number in bits 4 and 5 of the opcode.
* Values are interpreted as follows:
* 8080 Z-80
* 00 B BC
* 01 D DE
* 10 H HL (no DD or FD prefix)
* 10 IX IX (with DD prefix)
* 10 IX IX (with FD prefix)
* 11 SP SP (if opcode is below F0)
* 11 PSW AF (if opcode is F0 or greater)
*
* "h" is replaced by IX or IY if the opcode prefix is DD or FD respectively.
* If the instruction is not prefixed, "h" is replaced by HL.
*
* "n" denotes an 8-bit number following the opcode.
*
* "a" denotes a 16-bit address following the opcode.
* Mnemonics for 8080 opcodes 00 through 3F
mnop008:
dc.b 'NOP$ LXI p,a$ STAX p$ INX p$ ' ;00-03
dc.b 'INR q$ DCR q$ MVI q,n$ RLC$ ' ;04-07
dc.b 'EXAF$ DAD p$ LDAX p$ DCX p$ ' ;08-0B
dc.b 'INR q$ DCR q$ MVI q,n$ RRC$ ' ;0C-0F
dc.b 'DJNZ n$ LXI p,a$ STAX p$ INX p$ ' ;10-13
dc.b 'INR q$ DCR q$ MVI q,n$ RAL$ ' ;14-17
dc.b 'JR n$ DAD p$ LDAX p$ DCX p$ ' ;18-1B
dc.b 'INR q$ DCR q$ MVI q,n$ RAR$ ' ;1C-1F
dc.b 'JRNZ n$ LXI p,a$ ShD a$ INX p$ ' ;20-23
dc.b 'INR q$ DCR q$ MVI q,n$ DAA$ ' ;24-27
dc.b 'JRZ n$ DAD p$ LhD a$ DCX p$ ' ;28-2B
dc.b 'INR q$ DCR q$ MVI q,n$ CMA$ ' ;2C-2F
dc.b 'JRNC n$ LXI p,a$ STA a$ INX p$ ' ;30-33
dc.b 'INR q$ DCR q$ MVI q,n$ STC$ ' ;34-37
dc.b 'JRC n$ DAD p$ LDA a$ DCX p$ ' ;38-3B
dc.b 'INR q$ DCR q$ MVI q,n$ CMC$ ' ;3C-3F
* Mnemonics for Z-80 opcodes 00 through 3F
mnop00z:
dc.b 'NOP$ LD p,a$ LD (p),A$ INC p$ ' ;00-03
dc.b 'INC q$ DEC q$ LD q,n$ RLCA$ ' ;04-07
dc.b 'EX AF,AF$ ADD HL,p$ LD A,(p)$ DEC p$ ' ;08-0B
dc.b 'INC q$ DEC q$ LD q,n$ RRCA$ ' ;0C-0F
dc.b 'DJNZ n$ LD p,a$ LD (p),A$ INC p$ ' ;10-13
dc.b 'INC q$ DEC q$ LD q,n$ RLA$ ' ;14-17
dc.b 'JR n$ ADD HL,p$ LD A,(p)$ DEC p$ ' ;18-1B
dc.b 'INC q$ DEC q$ LD q,n$ RRA$ ' ;1C-1F
dc.b 'JR NZ,n$ LD p,a$ LD (a),HL$INC p$ ' ;20-23
dc.b 'INC q$ DEC q$ LD q,n$ DAA$ ' ;24-27
dc.b 'JR Z,n$ ADD HL,p$ LD HL,(a)$DEC p$ ' ;28-2B
dc.b 'INC q$ DEC q$ LD q,n$ CPL$ ' ;2C-2F
dc.b 'JR NC,n$ LD p,a$ LD (a),A$ INC p$ ' ;30-33
dc.b 'INC q$ DEC q$ LD q,n$ SCF$ ' ;34-37
dc.b 'JR C,n$ ADD HL,p$ LD A,(a)$ DEC p$ ' ;38-3B
dc.b 'INC q$ DEC q$ LD q,n$ CCF$ ' ;3C-3F
* Mnemonics for opcodes 40 through 7f are easy - 76 is HLT
* (HALT for Z-80), and all others are MOV (LD for Z-80).
mnop408 dc.b 'MOV q,r$'
mnop40z dc.b 'LD q,r$'
mnop768 dc.b 'HLT$'
mnop76z dc.b 'HALT$'
* Mnemonics for 8080 opcodes 80 through BF
mnop808:
dc.b 'ADD r$ ADC r$ SUB r$ SBB r$ ' ;80-9F
dc.b 'ANA r$ XRA r$ ORA r$ CMP r$ ' ;A0-BF
* Mnemonics for Z-80 opcodes 80 through BF
mnop80z:
dc.b 'ADD A,r$ADC A,r$SUB r$ SBC A,r$' ;80-9F
dc.b 'AND r$ XOR r$ OR r$ CP r$ ' ;A0-BF
* Mnemonics for 8080 opcodes C0 through FF
* These are interpreted by the same routine as for opcodes 00 through 3F.
mnopC08:
dc.b 'RNZ$ POP p$ JNZ a$ JMP a$ ' ;C0-C3
dc.b 'CNZ a$ PUSH p$ ADI n$ RST 0$ ' ;C4-C7
dc.b 'RZ$ RET$ JZ a$ ILLEGAL$ ' ;C8-CB
dc.b 'CZ a$ CALL a$ ACI n$ RST 1$ ' ;CC-CF
dc.b 'RNC$ POP p$ JNC a$ OUT n$ ' ;D0-D3
dc.b 'CNC a$ PUSH p$ SUI n$ RST 2$ ' ;D4-D7
dc.b 'RC$ EXX$ JC a$ IN n$ ' ;D8-DB
dc.b 'CC a$ ILLEGAL$ SBI n$ RST 3$ ' ;DC-DF
dc.b 'RPO$ POP p$ JPO a$ XTh$ ' ;E0-E3
dc.b 'CPO a$ PUSH p$ ANI n$ RST 4$ ' ;E4-E7
dc.b 'RPE$ PCh$ JPE a$ XCHG$ ' ;E8-EB
dc.b 'CPE a$ ILLEGAL$ XRI n$ RST 5$ ' ;EC-FF
dc.b 'RP$ POP p$ JP a$ DI$ ' ;F0-F3
dc.b 'CP a$ PUSH p$ ORI n$ RST 6$ ' ;F4-F7
dc.b 'RM$ SPh$ JM a$ EI$ ' ;F8-FB
dc.b 'CM a$ ILLEGAL$ CPI n$ RST 7$ ' ;FC-FF
* Mnemonics for Z-80 opcodes C0 through FF
* These are interpreted by the same routine as for opcodes 00 through 3F.
mnopC0z:
dc.b 'RET NZ$ LD p,(SP)$JP NZ,a$ JP a$ ' ;C0-C3
dc.b 'CALL NZ,a$ LD (SP),p$ADD A,n$ RST 0$ ' ;C4-C7
dc.b 'RET Z$ RET$ JP Z,a$ ILLEGAL$ ' ;C8-CB
dc.b 'CALL Z,a$ CALL a$ ADC A,n$ RST 8$ ' ;CC-CF
dc.b 'RET NC$ LD p,(SP)$JP NC,a$ OUT n,A$ ' ;D0-D3
dc.b 'CALL NC,a$ LD (SP),p$SUB A,n$ RST 10$ ' ;D4-D7
dc.b 'RET C$ EXX$ JP C,a$ IN A,n$ ' ;D8-DB
dc.b 'CALL C,a$ ILLEGAL$ SBC A,n$ RST 18$ ' ;DC-DF
dc.b 'RET PO$ LD p,(SP)$JP PO,a$ EX (SP),p$' ;E0-E3
dc.b 'CALL PO,a$ LD (SP),p$AND A,n$ RST 20$ ' ;E4-E7
dc.b 'RET PE$ JP (p)$ JP PE,a$ EX DE,p$ ' ;E8-EB
dc.b 'CALL PE,a$ ILLEGAL$ XOR A,n$ RST 28$ ' ;EC-FF
dc.b 'RET P$ LD p,(SP)$JP P,a$ DI$ ' ;F0-F3
dc.b 'CALL P,a$ LD (SP),p$OR A,n$ RST 30$ ' ;F4-F7
dc.b 'RET M$ LD SP,h$ JP M,a$ EI$ ' ;F8-FB
dc.b 'CALL M,a$ ILLEGAL$ CP A,n$ RST 38$ ' ;FC-FF
* Mnemonics for opcodes CB00 through CB3F -
* these are the same for both the 8080 and the Z-80.
mnopCB08:
mnopCB0z:
dc.b 'RLC r$ RRC r$ RL r$ RR r$ ' ;CB00-CB1F
dc.b 'SLA r$ SRA r$ ILLEGAL$SRL r$ ' ;CB20-CB3F
* Mnemonics for opcodes CB40 through CBFF -
* these are the same for both the 8080 and the Z-80.
mnopCB48:
mnopCB4z:
dc.b 'BIT $ RES $ SET $ '
* Mnemonics for 8080 opcodes ED40 through ED7F
* These are interpreted by the same routine as for opcodes 00 through 3F.
mnopE48:
dc.b 'IN q,(C)$ OUT (C),q$ DSBB p$ SBCD a$ ' ;ED40-ED43
dc.b 'NEG$ RETN$ IM0$ MOV I,A$ ' ;ED44-ED47
dc.b 'IN q,(C)$ OUT (C),q$ DADC p$ LBCD a$ ' ;ED48-ED4B
dc.b 'ILLEGAL$ RETI$ ILLEGAL$ MOV R,A$ ' ;ED4C-ED4F
dc.b 'IN q,(C)$ OUT (C),q$ DSBB p$ SDED a$ ' ;ED50-ED53
dc.b 'ILLEGAL$ ILLEGAL$ IM1$ MOV A,I$ ' ;ED54-ED57
dc.b 'IN q,(C)$ OUT (C),q$ DADC p$ LDED a$ ' ;ED58-ED5B
dc.b 'ILLEGAL$ ILLEGAL$ IM2$ MOV A,R$ ' ;ED5C-ED5F
dc.b 'IN q,(C)$ OUT (C),q$ DSBB p$ SHLD a$ ' ;ED60-ED63
dc.b 'ILLEGAL$ ILLEGAL$ ILLEGAL$ RRD$ ' ;ED64-ED67
dc.b 'IN q,(C)$ OUT (C),q$ DADC p$ LHLD a$ ' ;ED68-ED6B
dc.b 'ILLEGAL$ ILLEGAL$ ILLEGAL$ RLD$ ' ;ED6C-ED6F
dc.b 'IN q,(C)$ OUT (C),q$ DSBB p$ SSPD a$ ' ;ED70-ED73
dc.b 'ILLEGAL$ ILLEGAL$ ILLEGAL$ ILLEGAL$ ' ;ED74-ED77
dc.b 'IN q,(C)$ OUT (C),q$ DADC p$ LSPD a$ ' ;ED78-ED7B
dc.b 'ILLEGAL$ ILLEGAL$ ILLEGAL$ ILLEGAL$ ' ;ED7C-ED7F
* Mnemonics for Z-80 opcodes ED40 through ED7F
* These are interpreted by the same routine as for opcodes 00 through 3F.
mnopE4z:
dc.b 'IN q,(C)$ OUT (C),q$ SBC HL,p$ LD (a),p$ ' ;ED40-ED43
dc.b 'NEG$ RETN$ IM 0$ LD I,A$ ' ;ED44-ED47
dc.b 'IN q,(C)$ OUT (C),q$ ADC HL,p$ LD p,(a)$ ' ;ED48-ED4B
dc.b 'ILLEGAL$ RETI$ ILLEGAL$ LD R,A$ ' ;ED4C-ED4F
dc.b 'IN q,(C)$ OUT (C),q$ SBC HL,p$ LD (a),p$ ' ;ED50-ED53
dc.b 'ILLEGAL$ ILLEGAL$ IM 1$ LD A,I$ ' ;ED54-ED57
dc.b 'IN q,(C)$ OUT (C),q$ ADC HL,p$ LD p,(a)$ ' ;ED58-ED5B
dc.b 'ILLEGAL$ ILLEGAL$ IM 2$ LD A,R$ ' ;ED5C-ED5F
dc.b 'IN q,(C)$ OUT (C),q$ SBC HL,p$ LD (a),p$ ' ;ED60-ED63
dc.b 'ILLEGAL$ ILLEGAL$ ILLEGAL$ RRD$ ' ;ED64-ED67
dc.b 'IN q,(C)$ OUT (C),q$ ADC HL,p$ LD p,(a)$ ' ;ED68-ED6B
dc.b 'ILLEGAL$ ILLEGAL$ ILLEGAL$ RLD$ ' ;ED6C-ED6F
dc.b 'IN q,(C)$ OUT (C),q$ SBC HL,p$ LD (a),p$ ' ;ED70-ED73
dc.b 'ILLEGAL$ ILLEGAL$ ILLEGAL$ ILLEGAL$ ' ;ED74-ED77
dc.b 'IN q,(C)$ OUT (C),q$ ADC HL,p$ LD p,(a)$ ' ;ED78-ED7B
dc.b 'ILLEGAL$ ILLEGAL$ ILLEGAL$ ILLEGAL$ ' ;ED7C-ED7F
* Mnemonics for miscellaneous ED-prefix instructions -
* these are the same for both the 8080 and the Z-80.
mnopEA8:
mnopEAz:
dc.b 'LDI$ CPI$ INI$ OUTI$' ;EDA0-EDA3
dc.b ' ' ;EDA4-EDA7 (illegal)
dc.b 'LDD$ CPD$ IND$ OUTD$' ;EDA8-EDAB
dc.b ' ' ;EDAC-EDAF (illegal)
dc.b 'LDIR$CPIR$INIR$OTIR$' ;EDB0-EDB3
dc.b ' ' ;EDB4-EDB7 (illegal)
dc.b 'LDDR$CPDR$INDR$OTDR$' ;EDB8-EDBB
* Mnemonic for illegal instructions
mnopilg dc.b 'ILLEGAL$'
page
*************************************************************************
* *
* Fake FDOS *
* *
*************************************************************************
*
* Fake BDOS for target system
*
fdos dc.b tHLT,0,tRET ;BIOS jump table
dc.b tJMP,$33,$FF ;Warm boot
dc.b tJMP,$36,$FF ;Console status
dc.b tJMP,$39,$FF ;Console input
dc.b tJMP,$3C,$FF ;Console output
dc.b tJMP,$3F,$FF ;List output
dc.b tJMP,$42,$FF ;Punch output
dc.b tJMP,$45,$FF ;Reader input
dc.b tJMP,$48,$FF ;Home disk
dc.b tJMP,$4B,$FF ;Select disk
dc.b tJMP,$4E,$FF ;Set track
dc.b tJMP,$51,$FF ;Set sector
dc.b tJMP,$54,$FF ;Set DMA address
dc.b tJMP,$57,$FF ;Read
dc.b tJMP,$5A,$FF ;Write
dc.b tJMP,$5D,$FF ;Get list device status
dc.b tJMP,$60,$FF ;Sector translation
*
* Fake BIOS for target system
*
dc.b tHLT,1,tRET ;Warm boot
dc.b tHLT,2,tRET ;Console status
dc.b tHLT,3,tRET ;Console input
dc.b tHLT,4,tRET ;Console output
dc.b tHLT,5,tRET ;List output
dc.b tHLT,6,tRET ;Punch output
dc.b tHLT,7,tRET ;Reader input
dc.b tHLT,8,tRET ;Home disk *
dc.b tHLT,9,tRET ;Select disk *
dc.b tHLT,10,tRET ;Set track *
dc.b tHLT,11,tRET ;Set sector *
dc.b tHLT,12,tRET ;Set DMA address *
dc.b tHLT,13,tRET ;Read *
dc.b tHLT,14,tRET ;Write *
dc.b tHLT,15,tRET ;Get list device status *
dc.b tHLT,16,tRET ;Sector translation *
*
* Fake Disk Parameter Block
*
fakedpb dc.b 11,0 ;SPT (sectors per track)
dc.b 4 ;BSH (block shift to record number)
dc.b 15 ;BLM (block number mask to record no.)
dc.b 0 ;EXM (logical->physical extent shift)
dc.b 439&255,439/256 ;DSM (highest allocation block number)
dc.b 255,0 ;DRM (highest directory entry number)
dc.b $F0,0 ;AL0, AL1 (initial allocation vector)
dc.b 64,0 ;CKS (size of directory check area)
dc.b 0,0 ;OFF (offset, number of reserved tracks)
*
* Fake Disk Block Allocation Table
*
fakealv dcb.b 21,$FF
dc.b %11111100
dcb.b 10,0
fdoslen equ *-fdos
*
* BDOS function vector table
*
cnop 0,4
bdostab dc.l bdos00,bdos01,bdos02,bdos03,bdos04,bdos05,bdos06,bdos07
dc.l bdos08,bdos09,bdos10,bdos11,bdos12,bdos13,bdos14,bdos15
dc.l bdos16,bdos17,bdos18,bdos19,bdos20,bdos21,bdos22,bdos23
dc.l bdos24,bdos25,bdos26,bdos27,bdos28,bdos29,bdos30,bdos31
dc.l bdos32,bdos33,bdos34,bdos35,bdos36
bdostabn:
*
* BIOS function vector table
*
cnop 0,4
biostab dc.l bdosfn,bios01,bios02,bios03,bios04,bios05,bios06,bios07
dc.l bios08,bios09,bios10,bios11,bios12,bios13,bios14,bios15
biostabn:
null dc.b 0 ;Null string
page
*************************************************************************
* *
* Variable storage *
* *
*************************************************************************
bss bss
* File information block - must be on a 4-byte boundary!
fib:
fibkey ds.l 1
fibtype ds.l 1 ;Type (file if negative, directory if positive)
fibname ds.b 108 ;File name (null-terminated)
fibprot ds.l 1 ;Protection mask
fibent ds.l 1
fibsize ds.l 1 ;Number of bytes in file
fibblks ds.l 1 ;Number of blocks in file
fibdays ds.l 1 ;Date stamp - number of days since Jan. 1, 1978
fibmins ds.l 1 ;Date stamp - number of minutes past midnight
fibtick ds.l 1 ;Date stamp - number of ticks past minute
fibcmt ds.b 116 ;Comments (null-terminated)
* InfoData structure
InfoData:
id_NumSoftErrors ds.l 1 ;number of soft errors on disk
id_UnitNumber ds.l 1 ;Which unit disk is (was) mounted on
id_DiskState ds.l 1 ;See defines below
id_NumBlocks ds.l 1 ;Number of blocks on disk
id_NumBlocksUsed ds.l 1 ;Number of block in use
id_BytesPerBlock ds.l 1
id_DiskType ds.l 1 ;Disk Type code
id_VolumeNode ds.l 1 ;BCPL pointer to volume node
id_InUse ds.l 1 ;Flag, zero if not in use
id_SIZEOF equ 36
* Miscellaneous storage areas
savesp ds.l 1 ;Stack pointer save area
_SysBase ds.l 1 ;Copy of _AbsExecBase
_DOSBase ds.l 1 ;Pointer to dos.library
stdin ds.l 1 ;Keyboard handle (stdin)
stdout ds.l 1 ;Screen handle (stdout)
rawhand ds.l 1 ;RAW: file handle
prthand ds.l 1 ;PRT:RAW file handle
handles ds.l 8*4 ;File handle (or zero) plus 12 bytes of FCB
handlen: ;End of file handle table
dmaaddr ds.l 1 ;Current DMA address
comend ds.l 1 ;End of .COM file name on command line
dtstamp ds.l 3 ;Date and time stamp
rpport ds.l 1 ;Serial input message port
wpport ds.l 1 ;Serial output message port
baud ds.l 1 ;New baud rate for "setbaud"
bits ds.b 1 ;Number of data bits
charin ds.b 1 ;Current input character
charinb ds.b 1 ;Serial input buffer
charout ds.b 1 ;Current output character
frstset ds.b 1 ;$80 after first call to "setbaud"
cmdline ds.b 128 ;Command line
cmdlinen: ;End of command line
comname ds.b 20 ;Name of file to load
comnamen: ;End of file name
opnname ds.b 24 ;File name for OPEN or RENAME
renname ds.b 24 ;New file name for RENAME
srchnam ds.b 11 ;CP/M file name for search first/next
ext17 ds.w 1 ;Extent counter for search first/next
newrega ds.b 1 ;BIOS/BDOS accumulator work area
workbuf ds.b 80 ;Work buffer for "pstring" (including $)
workbufn: ;End of work buffer
strbuf ds.b 2048 ;String output buffer
strbufn ds.b 8 ;"strbuf" overflow area - must follow "strbuf"!
strptr ds.l 1 ;Current position in "strbuf"
escbuf ds.b 8 ;Translated escape sequence
esclen ds.w 1 ;Number of bytes saved in "escbuf"
cmdflag ds.b 1 ;Take program name from command line.
quitflg ds.b 1 ;"quitprg" exit flag
testdol ds.b 1 ;"pstring" should test for leading $
insflag ds.b 1 ;We're in insert mode.
dumpcnt ds.b 1 ;"dump" counter for pausing
traceit ds.b 1 ;-t (trace) flag was set on command line
btrcflg ds.b 1 ;Trace BIOS/BDOS calls.
bufflag ds.b 1 ;Console output is buffered.
z80flag ds.b 1 ;Display Z-80 mnemonics in instruction trace.
opcode ds.b 1 ;Current opcode (used for tracing)
prefix ds.b 1 ;Instruction prefix (DD or FD)
fcbptr ds.l 1 ;Pointer to current FCB
listopn ds.b 1 ;The list device is open.
builtin ds.b 1 ;1 = USER command, 2 = SAVE command
acmap ds.w 1 ;Active drive map
romap ds.w 1 ;Read-only map
newdmap ds.w 1 ;Map bit from "mapdrv"
*
* Serial port read request
*
ds.l 0
readreq ;struct IOExtSer
;struct IOStdReq
;struct Message
ds.b 14 ;struct Node
r_ReplyPort ds.l 1 ;Pointer to MsgPort (message reply port)
r_MLength ds.w 1 ;Message length in bytes
; End of struct Message
ds.l 1 ;Pointer to device node
ds.l 1 ;Pointer to Unit (driver private)
r_Command ds.w 1 ;Device command
ds.b 1 ;io_Flags
ds.b 1 ;Error or warning number
; End of struct IOReq - IOStdReq continues...
r_Actual ds.l 1 ;Actual number of bytes transferred
r_Length ds.l 1 ;Requested number of bytes transferred
r_Data ds.l 1 ;Points to data area.
r_Offset ds.l 1 ;Offset for block-structured devices
; End of struct IOStdReq
r_CtlChar ds.l 1 ;control char's (order = xON,xOFF,INQ,ACK)
r_RBufLen ds.l 1 ;length in bytes of serial port's read buffer
r_ExtFlags ds.l 1 ;additional serial flags (see bitdefs below)
r_Baud ds.l 1 ;baud rate requested (true baud)
ds.l 1 ;duration of break signal in MICROseconds
;struct IOTArray termination character array
r_ReadLen ds.b 1 ;bits per read character (# of bits)
r_WriteLen ds.b 1 ;bits per write character (# of bits)
r_StopBits ds.b 1 ;stopbits for read (# of bits)
r_SerFlags ds.b 1 ;see SerFlags bit definitions below
r_Status ds.w 1 ;status of serial port
rsize equ *-readreq
*
* Serial port write request
*
ds.l 0
writreq ;struct IOExtSer
;struct IOStdReq
;struct Message
ds.b 14 ;struct Node
w_ReplyPort ds.l 1 ;Pointer to MsgPort (message reply port)
w_MLength ds.w 1 ;Message length in bytes
; End of struct Message
ds.l 1 ;Pointer to device node
ds.l 1 ;Pointer to Unit (driver private)
w_Command ds.w 1 ;Device command
ds.b 1 ;io_Flags
ds.b 1 ;Error or warning number
; End of struct IOReq - IOStdReq continues...
w_Actual ds.l 1 ;Actual number of bytes transferred
w_Length ds.l 1 ;Requested number of bytes transferred
w_Data ds.l 1 ;Points to data area.
w_Offset ds.l 1 ;Offset for block-structured devices
; End of struct IOStdReq
w_CtlChar ds.l 1 ;control char's (order = xON,xOFF,INQ,ACK)
w_RBufLen ds.l 1 ;length in bytes of serial port's read buffer
w_ExtFlags ds.l 1 ;additional serial flags (see bitdefs below)
w_Baud ds.l 1 ;baud rate requested (true baud)
ds.l 1 ;duration of break signal in MICROseconds
;struct IOTArray termination character array
w_ReadLen ds.b 1 ;bits per read character (# of bits)
w_WriteLen ds.b 1 ;bits per write character (# of bits)
w_StopBits ds.b 1 ;stopbits for read (# of bits)
w_SerFlags ds.b 1 ;see SerFlags bit definitions below
w_Status ds.w 1 ;status of serial port, as follows:
wsize equ *-writreq
* BIT ACTIVE FUNCTION
* 0 low busy
* 1 low paper out
* 2 low select
* 3 low Data Set Ready
* 4 low Clear To Send
* 5 low Carrier Detect
* 6 low Ready To Send
* 7 low Data Terminal Ready
* 8 high read overrun
* 9 high break sent
* 10 high break received
* 11 high transmit x-OFFed
* 12 high receive x-OFFed
* 13-15 reserved
*************************************************************************
* *
* Target processor's address space *
* *
*************************************************************************
even
registers ds.b 22 ;Actual storage for Z-80's other registers
target ds.b $10000 ;Z-80's universe
end