; ; SWV20 Version 1.0 ; ; CP/M-80 emulator for CP/M-86, MP/M-86 and Concurrent DOS ; using the NEC V-20 and V-30 micorprocessors. ; ; Copyright 1986 Thomas M. Langley ; All rights reserved. ; Commercial and Government use prohibited ; ; ; Some codemacros for V20 instructions ; ; BRKEM - Break to 8080 mode: ; codemacro brkem int:Db db 0fh db 0ffh db int endm ; ; RETEM - Return to 8086 mode: ; codemacro retem db 0edh db 0fdh endm ; ; CALLN - Call 8086 subroutine from emulation mode ; codemacro calln int:Db db 0edh db 0edh db int endm ; ; Start of code area ; cseg org 0 mov sp,offset stack ; ; Move the BIOS and BDOS intercept code to ; the top of the emulator's address space (ES:) ; ; Entry: ES: -> Emulator address space segment ; push ds mov ax,es mov ds,ax ;DS: now equal to ES: mov si,offset tpa ;start of "8080" part of emulation code mov di,bdosv ;relocation area mov cx,copylen ;length of move in words cld ;set forward direction rep movsb ;do move pop ds ; ; Finished with emulation mode TPA so clear out prior to load ; mov di,10h mov cx,200h xor ax,ax rep stosw ;make all 0's ; ; Process command tail ; and cmdlen,00ffh call parsel cmp wrkbuf,'?' ;is it version request? mov dx,offset vermsg jne noerr ;display version jmp error ; ; Parse command name into wrkbuf ; noerr equ $ cmp cmdlen,0 ;command there? mov dx,offset ermsg1 jnz noerr1 ;no command specified jmp error ; noerr1 equ $ push di mov cx,cmdlen ;get command length mov si,offset cmdtxt mov di,offset cpmtxt mov cpmdma,cx rep movsb ;copy pop di mov ax,ftype1 mov [di],ax mov ax,ftype2 mov 2[di],ax ;add .COM mov byte ptr 4[di],0 ; ; Parse file name (of command file) into the work fcb ; call parsef ;parse to command fcb call load ;load program jz initg xor si,si ;index mov cx,12 ;move program name init1 equ $ mov al,wrkbuf[si] ;get character or al,al ;is id the end? jz init2 mov ermsg2a[si],al inc si ;next buffer location loop init1 mov di,offset ermsg2b ;now move rest of message mov cx,ermsg2l init2 equ $ mov al,[di] ;get character inc di mov ermsg2a[si],al ;and store inc si loop init2 jmp error ; ; parse first parameter into the work fcb and then move it ; to the CP/M's first default fcb ; initg equ $ mov di,1 mov cx,11 ;eleven bytes to 'blank' ; initg1 equ $ mov al,20h mov cpmfcb1[di],al mov cpmfcb2[di],al inc di ;next offset loop initg1 ; call parsel ;parse first parameter jnz done ;nothing parsed call parsef ;parse to first fcb jnz done push cx ;have to save these push si mov cx,16 ;only need 16 bytes mov si,offset wrkfcb mov di,offset cpmfcb1 rep movsb pop si pop cx ;stack still down 2 words ; ; ; Parse second parameter to temporary work area and then copy ; to second fcb at 6ch to avoid overlaying default dma ; call parsel ;parse second parameter jnz done ;no parameter call parsef ;parse to second fcb jnz done mov cx,16 mov si,offset wrkfcb mov di,offset cpmfcb2 rep movsb ;move fcb jmps done ; ; ; error equ $ mov cl,9 ;display error message in DX call bdos mov cl,0 call bdos ;exit ; ; ; Done with all initialization, so pass control to ; CP/M program. ; done equ $ pop cx ;get command tail length pop si ;and start within command buffer ; xor al,al ; ; Set up entry parameters for emulation mode ; mov iobyte,0 ;io byte mov cl,32 ;get current user mov dl,0ffh call bdos shl bl,1 ;shift to upper nibble shl bl,1 shl bl,1 shl bl,1 push bx mov cl,26 ;get current disk call bdos pop bx ;get user # back or al,bl ;combine current disk and (userno shl 4) mov curdsk,al mov dx,es ;'new' cs: mov cl,51 call bdos ;set dma seg mov dx,offset cpmdma ;and offset mov cl,26 call bdos ; stop equ $ xor ax,ax mov ds,ax mov si,128*4 mov word ptr[si],offset estart mov ax,es mov ds: 2[si],ax mov ss,ax mov ds,ax mov sp,0ffffh mov ax,0100h mov bp,ax emexit equ $ nop brkem 128 mov cl,0 int 224 ; ; load equ $ push cx push si push es or prfcbf6,80h ;indicate read only mode load1 equ $ mov dx,offset prfcb ;command fcb mov cl,15 ;open function call bdos cmp al,0ffh ;error mov ah,al mov dx,offset ermsg2 jne load2 ;exit if error cmp prfcbdr,0 ;was it default jne load4 ;exit if not mov prfcbdr,1 ;make it A: jmps load1 ;try again load2 equ $ mov dx,es mov cl,51 ;set base call bdos mov cx,63*8 ;largest amount to load mov dx,offset tpa ;start of load load3 equ $ mov di,cx mov cl,26 ;set offset call bdos mov dx,offset prfcb mov cl,20 ;read record call bdos cmp al,1 ;error? mov dx,offset ermsg4 mov ah,0ffh ja load4 mov ah,0 je load4 mov cl,52 ;get dma address call bdos add ax,128 ;next record mov dx,ax mov cx,di ;restore counter loop load3 mov dx,offset ermsg3 mov ah,0ffh load4 equ $ pop es pop si pop cx or ah,ah ret ; ; PARSEL: Parse Command Line ; ; Exit: DI -> Last character + 1 in wrkbuf (0x00) ; parsel equ $ call xblank ;skip leading blanks test cmdlen,0ffh ;see if anything in buffer mov al,0ffh ;and exit with flag set if no parameter jz parsel4 mov di,offset wrkbuf ;output area mov si,offset cmdtxt ;input buffer parsel1 equ $ mov al,[si] ;get cmdbuf or al,al ;end of string jz parsel3 cmp al,' ' ;delimiter? je parsel3 ;treat as end of string jb parsel2 ;skip if control character (< 20h) cmp al,'z' ;see if lower case ja parsel2 ;and ignore if greater mov [di],al ;store in target inc di parsel2 equ $ inc si ;next source byte dec cmdlen ;decrement command length jnz parsel1 parsel3 equ $ call shiftb xor al,al mov [di],al ;store ending zero cmp di,offset wrkbuf ;see if it didn't change jne parsel4 dec al parsel4 equ $ push ax call xblank pop ax mov ah,al or al,al ;set return code ret ; ; Skip leading blanks ; xblank equ $ test cmdlen,0ffh jz xblank2 ;exit if 0 mov si,offset cmdtxt ;always start from here xblank1 equ $ cmp byte ptr[si],' ' ;is it a blank jne xblank2 ;no more blanks inc si ;next buffer byte dec cmdlen jnz xblank1 ;get rest of buffer xblank2 equ $ call shiftb mov al,0 test cmdlen,0ffh ;see if 0 jnz xblankx dec al ;make 0ffh xblankx equ $ mov ah,al or al,al ret ; shiftb equ $ push di push si push es mov ax,ds mov es,ax mov cx,cmdlen or cx,cx ;see if 0 jz shiftb1 mov di,offset cmdtxt rep movsb ;copy buffer left shiftb1 equ $ mov cx,127 sub cx,cmdlen ;get remainder in buffer xor al,al rep stosb ;zero the rest pop es pop si pop di ret ; parsef equ $ ;parse file name push si push cx ;save these push di mov pfcbnam,offset wrkbuf mov pfcbfcb,offset wrkfcb mov dx,offset pfcb mov cl,152 call bdos or ax,ax pop di pop cx pop si ret ; bdos equ $ int 224 ret ; dseg org 80h cmdbuf rb 0 ;command line buffer cmdlen rw 1 ;command line buffer len cmdtxt rb 126 ;command line org 0100h ; vermsg db 0dh,0ah,'SW-V20 Version 1.0 ' db 'Copyright (C) 1986 Thomas M. Langley - All rights reserved.' db 0dh,0ah,'$' ; ermsg1 db 0dh,0ah,'No command name specified.',0dh,0ah,'$' ermsg2 db 0dh,0ah,'SWV20: Program File "' ermsg2a db ' ' ermsg2b db '" cannot be opened.',0dh,0ah,'$' ermsg2l equ offset $ - offset ermsg2a ermsg3 db 0dh,0ah,'Program file too large',0dh,0ah,'$' ermsg4 db 0dh,0ah,'Error loading program file',0dh,0ah,'$' ; wrkbuf db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; pfcb equ $ pfcbnam dw offset wrkbuf pfcbfcb dw offset wrkfcb ; ftype1 rw 0 db '.C' ftype2 rw 0 db 'OM' ; crlf db 0dh,0ah,'$' wrkfcb rb 0 prfcb equ $ ;default fcb 1 prfcbdr db 0 ;default drive db 0,0, 0,0,0 ;f1-f5 (file name prfcbf6 db 0 rb 36-7 ;rest of fcb db 0 wrkfcbl equ offset $ - offset wrkfcb ; sseg org 0 rw 32 stack equ $ db 0 ; jmp80 equ 0c3h ret80 equ 0c9h mvia equ 3eh mvic equ 0eh mvie equ 1eh movac equ 79h movea equ 5fh ; eseg org 0 db jmp80 dw biosv iobyte db 0 ;CP/M io byte curdsk db 0 ;currently logged disk and user number db jmp80 dw bdosj org 5ch cpmfcb1 rb 1 ;first fcb rb 15 cpmfcb2 rb 1 ;second fcb rb 15 rb 4 org 80h cpmdma rw 1 ;default dma buffer and command line cpmtxt rb 126 ; org 0100h tpa equ $ ; ; This is the emulation mode code to link to native mode opsys. ; It is relocated from here to top of ram during execution and ; this area is overlaid by the transient program. ; bas80 equ 0fe00h - offset $ ;relocation value ; bdosv equ bas80 + offset $ rb 6 bdosj equ bas80 + offset $ db jmp80 ;call dw bdose bdose equ bas80 + offset $ db 0edh,0edh,224 ;calln 224 db ret80 estart equ bas80 + offset $ db 0cdh dw 0100h db 0edh,0fdh ; biosv equ bas80 + offset $ db jmp80 dw cold db jmp80 dw warm db jmp80 dw const db jmp80 dw conin db jmp80 dw conout db jmp80 dw lstout db jmp80 dw punch db jmp80 dw reader db jmp80 dw home db jmp80 dw seldsk db jmp80 dw settrk db jmp80 dw setsec db jmp80 dw setdma db jmp80 dw read db jmp80 dw write db jmp80 dw listst db jmp80 dw sectran ; cold equ bas80 + offset $ warm equ bas80 + offset $ db mvic,0 db jmp80 dw bdosj const equ bas80 + offset $ db mvic,0bh ;cp/m get console status db jmp80 dw bdosj conin equ bas80 + offset $ db mvic,6 db mvie,0fdh ;cp/m input raw i/o db jmp80 dw bdosj conout equ bas80 + offset $ db movac db movea db mvic,6 ;cp/m output raw i/o db jmp80 dw bdosj lstout equ bas80 + offset $ db movac db movea db mvic,5 ;cp/m write to printer db jmp80 dw bdosj punch equ bas80 + offset $ db movac db movea db mvic,4 ;cp/m aux write db jmp80 dw bdosj reader equ bas80 + offset $ db mvic,3 ;cp/m aux read db jmp80 dw bdosj ; home equ bas80 + offset $ seldsk equ bas80 + offset $ settrk equ bas80 + offset $ setsec equ bas80 + offset $ setdma equ bas80 + offset $ read equ bas80 + offset $ write equ bas80 + offset $ sectran equ bas80 + offset $ db ret80 ; listst equ $ db mvia,0ffh db ret80 ; copylen equ offset $ - offset tpa ; end ;