title CP/M 2 emulator for CP/M 3 - the RSX name ('CPM2RSX') ; A CP/M+ downgrade tool ; This module is the RSX for emulation BDOS ans BIOS ; functions running CP/M Plus (CP/M 3.x) ; Type 'CPM2' to install the RSX ; Type 'CPM2DEL' to install the RSX ; Program based upon an idea from the Public Domain tool ; written by Jim Lopushinsky, March 22, 1984 ; There was a little bug: The RSX did always return the ; "real" OS version. This should be changed to "22h" ; Set BOOLEAN "ORIG" to TRUE for original version ; Set it to FALSE in case of CP/M 2.x version return ; Adapted by Werner Cirsovius ; Hohe Weide 44 ; D-2000 Hamburg 20 ; Tel.: 040/4223247 ; Version 1.0, March 1987 FALSE equ 0 TRUE equ NOT FALSE ;;ORIG equ FALSE ;; !!! NOTE THIS ORIG equ TRUE OS equ 0000h .vers equ 12 .resdsk equ 13 .seldsk equ 14 .setdma equ 26 .getall equ 27 .scb equ 49 .rsx equ 60 reclng equ 128 _DPB equ 12 _DSM equ 5 _PSH equ 15 _COMmem equ 05dh null equ 00h lf equ 0ah cr equ 0dh .nop equ 000h ; #### RSX HEADER BEGINS ### ; RSXbase: ds 6 ; xx00: Serial number RSXgo: jp BegRSX ; xx06: Jump to RSX _BDOS: jp RSXgo-RSXbase ; xx09: Jump to next RSX dw RSXgo-RSXbase+1 ; xx0C: Address of previous RSX RSXperm: db 0 ; xx0E: Permanent flag db 0 ; xx0F: Bank flag db 'CPM2 ' ; xx10: Name of RSX db 0 ; xx18: Loader flag db 0,0 ; xx19: Reserved ; ; #### RSX HEADER ENDS ### ; RSXactive: db FALSE WarmReq: db FALSE ; Warm start dlag ; ; RSX entry point ; BegRSX: ld a,c ; Get function cp .rsx ; Test RSX call jr z,RSXcall ld hl,RSXactive inc (hl) ; Test active dec (hl) jr z,_BDOS ; Nope, so get next RSX inc hl inc (hl) ; Test warm start flag dec (hl) jr z,SkpWarm ; It's zero inc (hl) ld hl,R.WBOOT ld (OS+1),hl ; Set warm start entry SkpWarm: ld a,c ; Get BDOS function IF NOT ORIG cp .vers ; Test request OS version jr nz,notvers ld hl,0022h ; Fake the version ld b,h ld a,l ret notvers: ENDIF cp .resdsk ; Test reset disk system jr c,_BDOS ; Ignore 0..12 jr nz,isSelDsk ld hl,LogDsk ld (hl),-1 ; Reset disk data inc hl ld (hl),-1 ; Set no disk logged theu BDOS jr _BDOS ; Then do the real call isSelDsk: cp .seldsk ; Test select disk jr nz,isSetDMA ld a,e ; Get selected disk ld (BDOSdsk),a ; Save it jr _BDOS ; Then do the real call isSetDMA: cp .setdma ; Test disk buffer jr c,_BDOS ; Ignore 15..25 jr nz,isGetAlloc ld (I.DMA),de ; Store DMA address jr _BDOS isGetAlloc: cp .getall ; Test get allocation jr nz,_BDOS ; Nope call _BDOS ; Get vector inc hl ; Test -1 ld a,h or l dec hl ret z ; Yeap, it is not defined ld a,(COMADPTR+1) ; Get page of COMMON memory or a ; Test banked ret z ; Nope push hl ld e,1 ld a,(BDOSdsk) ; Get selected disk ld c,a call SELDSK ; Select disk pop hl ld a,(ALVflag) ; Get flag or a ret z ; Return system ALV ld hl,(ALVaddr) ; Get ALV ret ; ; RSX is called ; RSXcall: ld a,(de) ; Get parameter or a ; Test install jr nz,InstRSX ; Yeap ; ; Code 0: Remove RSX ; ld a,(RSXactive) ; Get active flag or a jr z,notactive ; It's not active ld hl,(OSwarm) ; Get address of warm start ld (OS+1),hl ; Reset it ld de,(O.WBOOT+1) inc hl ld (hl),e inc hl ld (hl),d notactive: ld hl,RSXgo+2 ld (hl),.nop ; Kill 'JP ' dec hl ld (hl),.nop dec hl ld (hl),.nop ld a,-1 ld (RSXperm),a ; Set non permanent ret ; ; Code 1: Install RSX ; InstRSX: dec a ; Test install jp nz,_BDOS ; Nope, call next ld a,(RSXactive) ; Test already active or a ret nz ; Yeap ld hl,(OS+1) ; Get address of BIOS vector ld (OSwarm),hl ; Save ld de,OSvec ld bc,OSvecln ldir ; Unpack BIOS vectors ld de,I.WBOOT ld hl,(OSwarm) ; Get warm start vector inc hl ld (hl),e ; Set intercept inc hl ld (hl),d ld hl,O.CONST ld de,R.CONST ld bc,VECln1 ldir ; Unpack character device vectors ld hl,(O.LISTST+1) ; Fetch address of list status ld (R.LISTST+1),hl ; Save ld hl,O.CONOST ld de,R.CONOST ld bc,VECln2 ldir ; Unpack next vectors ld hl,O.MOVE ld de,R.MOVE ld bc,VECln3 ldir ld hl,O.XMOVE ld de,R.XMOVE ld bc,VECln4 ldir ld a,TRUE ld (RSXactive),a ; Set active ld hl,R.WBOOT ld (OS+1),hl ; Change BIOS vector block ld c,.scb ld de,COMADR call _BDOS ; Get COMMON memory address ld (COMADPTR),hl ret ; COMADR: db _COMmem db 0 db 0,0 ; ; Original BIOS vectors ; OSvec equ $ O.WBOOT: ds 3 ; 1: Warm boot O.CONST: ds 3 ; 2: Console status ds 3 ; 3: Console input O.CONOUT: ds 3 ; 4: Console output ds 3 ; 5: Printer output ds 3 ; 6: Auxiliary output ds 3 ; 7: Auxiliary input ds 3 ; 8: Move disc head to track 0 O.SELDSK: ds 3 ; 9: Select disc drive O.SETTRK: ds 3 ; 10: Set track number O.SETSEC: ds 3 ; 11: Set sector number O.SETDMA: ds 3 ; 12: Set DMA address O.READ: ds 3 ; 13: Read a sector O.WRITE: ds 3 ; 14: Write a sector O.LISTST: ds 3 ; 15: Status of list device O.SECTRAN: ds 3 ; 16: Sector translation for skewing O.CONOST: ds 3 ; 17: Status of console output ds 3 ; 18: Status of auxiliary input ds 3 ; 19: Status of auxiliary output ds 3 ; 20: Address of devices table ds 3 ; 21: Initialise a device ds 3 ; 22: Address of discs table O.MULTIO: ds 3 ; 23: Read/write multiple sectors O.FLUSH: ds 3 ; 24: Flush host buffers O.MOVE: ds 3 ; 25: Move a block of memory ds 3 ; 26: Real time clock O.SELMEM: ds 3 ; 27: Select memory bank O.SETBNK: ds 3 ; 28: Select bank for DMA operation O.XMOVE: ds 3 ; 29: Preload banks for MOVE ds 3 ; 30: System-depedent functions ds 3 ; 31: Reserved ds 3 ; 32: Reserved OSvecln equ $-OSvec ; ; Intercepted warm start ; I.WBOOT: ld a,(RSXperm) ; Test RSX permanent or a jr nz,noWmsg ; Nope ld sp,RSXstk ld hl,$WRMSG call PrStrg ; Tell RSX active noWmsg: ld a,TRUE ld (WarmReq),a ; Indicate warm start jp O.WBOOT ; Do real warm start ; ; BIOS function 9: Select disc drive ; I.SELDSK: ld e,0 SELDSK: call BankSwitch ; Select disc drive ld a,-1 ld (LogDsk),a ; Set no selection ld (l0380),a push bc call O.SELDSK ; Select disc drive pop bc ld a,h ; Test disk defined or l ret z ; Nope ld a,c ; Get disk ld (LogDsk),a ; Save it xor a ld (ALVflag),a ; Set system ALV ld e,(hl) ; Get XLT inc hl ld d,(hl) ld (XLT),de ld de,_DPB-1 add hl,de ; Point to DPB ld de,DPB ld bc,DPlen ldir ; Copy it dec de ld a,(de) ld hl,(COMADPTR) ; Get address of COMMON memory cp h ld hl,(DPB) ; Get DPB jr nc,l02f0 push hl ld de,_DSM add hl,de ld c,(hl) ; Fetch DSM inc hl ld b,(hl) ld a,3 call shr ; DIV 8 inc bc ld (l0368),bc ld hl,(ALVaddr) ; Get ALV ld (l036a),hl ld de,l0388 ld (ALVaddr),de ; Set ALV ldir dec a ld (ALVflag),a ; Set saved ALV pop hl l02f0: ld de,_PSH add hl,de ld e,(hl) ; Get PSH inc hl ld d,(hl) ; Get PHM ld (PSH),de ld hl,XLT ret ; db 0,0,0,0 ; ; Redirected BIOS vectors ; R.WBOOT: jp I.WBOOT ; 1: Warm boot R.CONST: ds 3 ; 2: Console status ds 3 ; 3: Console input ds 3 ; 4: Console output ds 3 ; 5: Printer output ds 3 ; 6: Auxiliary output ds 3 ; 7: Auxiliary input VECln1 equ $-R.CONST jp I.HOME ; 8: Move disc head to track 0 jp I.SELDSK ; 9: Select disc drive jp I.SETTRK ; 10: Set track number jp I.SETSEC ; 11: Set sector number jp I.SETDMA ; 12: Set DMA address jp I.READ ; 13: Read a sector jp I.WRITE ; 14: Write a sector R.LISTST: jp O.LISTST ; 15: Status of list device jp I.SECTRAN ; 16: Sector translation for skewing R.CONOST: ds 3 ; 17: Status of console output ds 3 ; 18: Status of auxiliary input ds 3 ; 19: Status of auxiliary output ds 3 ; 20: Address of devices table ds 3 ; 21: Initialise a device ds 3 ; 22: Address of discs table VECln2 equ $-R.CONOST jp I.MULTIO ; 23: Read/write multiple sectors jp I.FLUSH ; 24: Flush host buffers R.MOVE: ds 3 ; 25: Move a block of memory ds 3 ; 26: Real time clock VECln3 equ $-R.MOVE jp I.SELMEM ; 27: Select memory bank jp I.SETBNK ; 28: Select bank for DMA operation R.XMOVE: ds 3 ; 29: Preload banks for MOVE ds 3 ; 30: System-depedent functions ds 3 ; 31: Reserved ds 3 ; 32: Reserved VECln4 equ $-R.XMOVE ; ALVflag: db 0 ; ALV flag for banked system OSwarm: dw 0 ; BIOS warm start vector XLT: dw 0 ; DPH: XLT l0368: dw 0 l036a: dw 0 PSH: db 0 ; PSH PHM: db 0 ; PHM ; dw l0388 ; DPB: ; \ dw 0 ; | DPB dw 0 ; | CSV ALVaddr: ; | dw 0 ; / ALV DPlen equ $-DPB ; LogDsk: ; \ db 0 ; | BDOSdsk: ; | db 0 ; / ; I.TRACK: dw 0 I.SECTOR: dw 0 I.DMA: dw 0 IOdir: db 0 l037f: db -1 ; Save of logged disk l0380: dw -1 ; Current track l0382: dw -1 ; Current sector l0384: db 0 WR.mode: db 0 COMADPTR: dw 0 l0388: ds 400 l0518: ds 1024 ; ; Give zero closed message ^HL ; PrStrg: ld a,(hl) ; Get character inc hl or a ; Test end ret z ; Yeap push hl ld c,a call O.CONOUT ; Put to console pop hl jr PrStrg ; $WRMSG: db cr,lf,'[CPM2 RSX active]',null ; ; BIOS function 8: Move disc head to track 0 ; I.HOME: ld bc,0 ; Set track 0 ; ; BIOS function 10: Set track number ; I.SETTRK: ld (I.TRACK),bc ; Store track ret ; ; BIOS function 11: Set sector number ; I.SETSEC: ld (I.SECTOR),bc ; Store sector ret ; ; BIOS function 12: Set DMA address ; I.SETDMA: ld (I.DMA),bc ; Store DMA address ret ; ; BIOS function 16: Sector translation for skewing ; I.SECTRAN: call BankSwitch ; Sector translation for skewing ld hl,(I.TRACK) ; Get track ld a,h or l jr z,l0968 ld a,(PHM) or a jp z,O.SECTRAN ; Sector translation for skewing and c ld (l0384),a ld a,(PSH) call shr ; BC SHR A jp O.SECTRAN ; Sector translation for skewing l0968: ld l,c ld h,b ret ; ; Shift BC right by Accu ; shr: srl b ; Simple one rr c dec a jr nz,shr ret ; ; Execute function thru system bank ; BankSwitch: push af ld a,(COMADPTR+1) ; Get page of common memory or a ; Test common memory jr nz,DoBankSw ; Yeap pop af ret DoBankSw: pop af ex (sp),hl ; Get caller ld (BankPC),hl ; Set for call target pop hl ld (BankSP),sp ; Save entry stack ld sp,RSXstk push af xor a call O.SELMEM ; Select system memory bank pop af BankPC equ $+1 call $-$ ; Execute user call push af ld a,1 call O.SELMEM ; Select TPA memory bank pop af ld sp,(BankSP) ; Get back user stack ret ; ds 2*25 RSXstk equ $ ; BankSP: dw 0 ; ; BIOS function 23: Read/write multiple sectors ; I.MULTIO: call BankSwitch jp O.MULTIO ; Read/write multiple sectors ; ; BIOS function 24: Flush host buffers ; I.FLUSH: call BankSwitch jp O.FLUSH ; Flush host buffers ; ; BIOS function 27: Select memory bank ; I.SELMEM: xor a ret ; ; BIOS function 28: Select bank for DMA operation ; I.SETBNK: call BankSwitch jp O.SETBNK ; Select bank for DMA operation ; ; BIOS function 13: Read a sector ; I.READ: ld a,1 ld (WR.mode),a ; Set mode jr DO.RW ; Go read ; ; BIOS function 14: Write a sector ; ; C=0 - Write can be deferred ; C=1 - Write must be immediate ; C=2 - Write can be deferred, no pre-read is necessary. ; I.WRITE: ld a,c ld (WR.mode),a ; Set mode xor a DO.RW: ld (IOdir),a ; Set I/O direction call BankSwitch ; Write a sector ld bc,(I.TRACK) ; Get track ld a,b ; Test track 0 or c jr z,RW.TR0 ; Yeap ld a,(PSH) ; Get PSH or a ; Test 128 byte sectors jr nz,l0a2e ; Nope RW.TR0: call O.SETTRK ; Set track number ld bc,(I.SECTOR) ; Get sector number call O.SETSEC ; Set sector number ld bc,(I.DMA) ; Get DMA address call O.SETDMA ; Set DMA address ld a,(COMADPTR+1) ; Get page of common memory or a ld a,1 call nz,O.SETBNK ; Select bank for DMA operation if common ld a,(WR.mode) ; Get back write mode ld c,a ; Get flag ld a,(IOdir) ; Get I/O drection or a jp z,O.WRITE ; Write a sector jp O.READ ; Read a sector l0a2e: ld hl,(l037f) ld a,(LogDsk) cp l jr nz,l0a9d ld hl,(l0380) ld bc,(I.TRACK) ; Get track sbc hl,bc jr nz,l0a9d ld bc,(I.SECTOR) ; Get sector number ld hl,(l0382) sbc hl,bc jr nz,l0a9d l0a4d: ld a,(COMADPTR+1) ; Get page of common memory if common or a ld a,1 call nz,O.SELMEM ; Select memory bank ld hl,l0518 ld a,(l0384) or a jr z,l0a66 ld b,a ld de,reclng l0a63: add hl,de ; Position in buffer djnz l0a63 l0a66: ld bc,reclng ; Set length ld de,(I.DMA) ; Get DMA address ld a,(IOdir) ; Get I/O direction or a jr nz,l0a74 ; NZ is read ex de,hl ; Swap source and destination l0a74: ldir or a jr z,l0a7b xor a ret l0a7b: ld a,(COMADPTR+1) ; Get page of common memory or a ld a,0 call nz,O.SELMEM ; Select memory bank if common ld bc,(l0380) call O.SETTRK ; Set track number ld bc,(l0382) call O.SETSEC ; Set sector number ld bc,l0518 call O.SETDMA ; Set DMA address ld c,1 ; Set immediate jp O.WRITE ; Write a sector l0a9d: ld a,(LogDsk) ld (l037f),a ld bc,(I.TRACK) ; Get track ld (l0380),bc call O.SETTRK ; Set track number ld bc,(I.SECTOR) ; Get sector number ld (l0382),bc call O.SETSEC ; Set sector number ld bc,l0518 call O.SETDMA ; Set DMA address call O.READ ; Read a sector or a ret nz jr l0a4d end