title 'MP/M II V2.0 Basic & Extended I/O Systems for SIMH ALTAIR 8800' ; Change history ; 17-Oct-2002, P. Schorn, added support for simulated hard disk ; 04-May-2002, P. Schorn, removed di/ei pairs from disk read/write ; 28-Apr-2002, P. Schorn, automated enabling of timer interrupts ; 25-Apr-2002, P. Schorn, initial version for banked and non-banked memory .8080 jpopcod equ (jmp) ; jp op-code .Z80 cseg true equ -1 false equ not true bioserr equ 1 ; 1 indicates BIOS error ndisks equ 8 ; total number of disks tracks equ 254 ; number of tracks for regular drives track1 equ tracks+1 ; indicator for unknown track position asecsiz equ 137 ; sector size Altair csecsiz equ 0080h ; sector size CP/M sysorg equ 0000h ticks equ 50 ; 50 ticks per second deltati equ 1000/ticks ; time in milliseconds between ticks simhport equ 0feh ; SIMH port maclib MPMD.LIB ; file name must be upper case! ; defines 'banked' and nhdisks ifndef nhdisks nhdisks equ 0 endif ; SIMH commands startTimerInterrupts equ 21 setTimerDelta equ 23 setTimerInterruptAdr equ 24 if nhdisks gt 0 ; constants for hard disk port hdskReset equ 1 ; command to reset controller hdskRead equ 2 ; read command hdskWrite equ 3 ; write command hdskport equ 0fdh ; control port for simulated hard disk endif if banked hasBankedMemory equ 18 ; SIMH command to check for banked memory setBankSelect equ 12 ; SIMH command to set memory bank getCommon equ 13 ; SIMH command to get common memory base endif ; Address Mode Function ; ------- ---- -------- ; selout Out Selects and enables controller and drive ; statin In Indicates status of drive and controller ; dskcon Out Controls disk function ; secpos In Indicates current sector position of disk ; dskwrit Out Write data ; dskread In Read data selout equ 8 ; port to select and enable controller and drive (OUT) ; +---+---+---+---+---+---+---+---+ ; | C | X | X | X | Device | ; +---+---+---+---+---+---+---+---+ ; ; C = If this bit is 1, the disk controller selected by 'device' is ; cleared. If the bit is zero, 'device' is selected as the ; device being controlled by subsequent I/O operations. ; X = not used ; Device = value zero thru 15, selects drive to be controlled. statin equ 8 ; port indicating status of drive and controller (IN) ; +---+---+---+---+---+---+---+---+ ; | R | Z | I | X | X | H | M | W | ; +---+---+---+---+---+---+---+---+ ; ; W - When 0, write circuit ready to write another byte. ; M - When 0, head movement is allowed ; H - When 0, indicates head is loaded for read/write ; X - not used (will be 0) ; I - When 0, indicates interrupts enabled (not used this simulator) ; Z - When 0, indicates head is on track 0 ; R - When 0, indicates that read circuit has new byte to read dskcon equ 9 ; port to control disc function (OUT) ; +---+---+---+---+---+---+---+---+ ; | W | C | D | E | U | H | O | I | ; +---+---+---+---+---+---+---+---+ ; ; I - When 1, steps head IN one track ; O - When 1, steps head OUT one track ; H - When 1, loads head to drive surface ; U - When 1, unloads head ; E - Enables interrupts (ignored by this simulator) ; D - Disables interrupts (ignored by this simulator) ; C - When 1 lowers head current (ignored by this simulator) ; W - When 1, starts Write Enable sequence: ; W bit on device 'statin' (see above) will go 1 and data will be read from ; port 'dskread' until 137 bytes have been read by the controller from ; that port. The W bit will go off then, and the sector data will be written ; to disk. Before you do this, you must have stepped the track to the desired ; number, and waited until the right sector number is presented on ; device 'secpos', then set this bit. secpos equ 9 ; port to indicate current sector position of disk (IN) ; As the sectors pass by the read head, they are counted and the ; number of the current one is available in this register. ; ; +---+---+---+---+---+---+---+---+ ; | X | X | Sector Number | T | ; +---+---+---+---+---+---+---+---+ ; ; X = Not used ; Sector number = binary of the sector number currently under the head, 0-31. ; T = Sector True, is a 1 when the sector is positioned to read or write. dskwrit equ 10 ; port to write data (OUT) dskread equ 10 ; port to read data (IN) ; All I/O is via programmed I/O. Each device has a status port ; and a data port. A write to the status port can select ; some options for the device although the simulator only ; recognizes the reset command (0x03). ; A read of the status port gets the port status: ; ; +---+---+---+---+---+---+---+---+ ; | X | X | X | X | X | X | O | I | ; +---+---+---+---+---+---+---+---+ ; ; I - A 1 in this bit position means a character has been received ; on the data port and is ready to be read. ; O - A 1 in this bit means the port is ready to receive a character ; on the data port and transmit it out over the serial line. ; ; A read to the data port gets the buffered character, a write ; to the data port writes the character to the device. punstat equ 18 ; sio port 2 status port pundata equ 19 ; sio port 2 data port ; masks for disk controller (statin) mhm equ 02h ; head movement mask mtzero equ 40h ; head on track zero mask mall equ 0ffh ; everything ok mask ; commands for disk controller (dskcon) cstepin equ 01h ; step in command cstepot equ 02h ; step out command cload equ 04h ; load head to drive surface command cuload equ 08h ; unload head from drive surface command cwrseq equ 80h ; 'start write enable sequence' command ; masks for SIO controller (constat, punstat) m_in equ 01h ; input available mask m_out equ 02h ; output allowed mask ; commands for SIO controller (constat, punstat) creset equ 3 ; reset command dirent equ 255 ; number of directory entries restrk equ 6 ; reserved tracks dsm06 equ 1efh ; maximum data block number for disks 0 to 6 dsm07 equ 254 ; maximum data block number for disk 7 spt equ 32 ; sectors per track sptmask equ spt-1 ; mask corresponding to 'spt' cks equ (dirent+1)/4 cr equ 13 ; Carriage Return lf equ 10 ; Line Feed nmbcns equ 4 ; number of consoles sts0 equ 10h ; status port conole #0 data0 equ sts0+1 ; data port console #0 sts1 equ 14h ; status port conole #1 data1 equ sts1+1 ; data port console #1 sts2 equ 16h ; status port conole #2 data2 equ sts2+1 ; data port console #2 sts3 equ 18h ; status port conole #3 data3 equ sts3+1 ; data port console #3 poll equ 131 ; XDOS poll function flagset equ 133 ; XDOS flag set function plco0 equ 0 ; poll console out #0 plci0 equ 1 ; poll console in #0 plco1 equ 2 ; poll console out #1 plci1 equ 3 ; poll console in #1 plco2 equ 4 ; poll console out #2 plci2 equ 5 ; poll console in #2 plco3 equ 6 ; poll console out #3 plci3 equ 7 ; poll console in #3 ; bios ; jump vector for individual subroutines jp commonbase jp warmstart ; warm start jp const ; console status jp conin ; console character in jp conout ; console character out jp list ; list character out jp rtnempty ; punch not implemented jp rtnempty ; reader not implemented jp home ; move head to home jp seldsk ; select disk jp settrk ; set track number jp setsec ; set sector number jp setdma ; set dma address jp read ; read disk jp write ; write disk jp pollpt ; list status jp sectrn ; sector translate jp selmemory ; select memory jp polldevice ; poll device jp startclock ; start clock jp stopclock ; stop clock jp exitregion ; exit region jp maxconsole ; maximum console number jp systeminit ; system initialization db 0,0,0 ; force use of internal dispatch @ idle ; jmp idle ; idle procedure commonbase: jp coldstart swtuser: jp $-$ swtsys: jp $-$ pdisp: jp $-$ xdos: jp $-$ sysdat: dw $-$ coldstart: warmstart: if nhdisks gt 0 ld c,32 ; reset hard disk controller ld a,hdskReset ; by issuing the reset command 32 times rhdsk: out (hdskPort),a dec c jp nz,rhdsk ; post condition is := 0 else ld c,0 endif jp xdos ; system reset, terminate process ;I/O handlers ; MP/M II V2.0 Console Bios list: ld a,d ; get device number or a ; is it zero? ret nz ; do nothing if not zero list1: call pollp1 ; check list status jp z,list1 ; jump back if not ready for output ld a,c ; prepare character for output out (pundata),a ; do it ret ; return 0ffh if ready, 000h if not pollpt: ld a,d ; get device number or a ; is it zero? ld a,0 ; prepare for is not zero case ret nz ; result is zero in this case pollp1: in a,(punstat) ; get list status and m_out ; mask output bit ret z ; zero means that bit is not set meaning cannot send output ld a,0ffh ; otherwise indicate that we can send output ret ptsti macro ?num ; Console #&?num input status routine, retuns 0ffh if ready, 00h if not ptsti&?num equ $ in a,(sts&?num) ; get status and m_in ; check with input mask ret z ; zero means that bit is not set meaning no input available ld a,0ffh ; otherwise indicate that input is available ret endm ptsti 0 ptsti 1 ptsti 2 ptsti 3 ptin macro ?num ; Console #&?num input, returns character in ptin&?num equ $ ld c,poll ld e,plci&?num call xdos ; poll console #&?num input in a,(data&?num) ; read character and 7fh ; strip parity bit ret endm ptin 0 ptin 1 ptin 2 ptin 3 ptsto macro ?num ; Console #&?num output status routine, retuns 0ffh if ready, 00h if not ptsto&?num equ $ in a,(sts&?num) and m_out ret z ld a,0ffh ret endm ptsto 0 ptsto 1 ptsto 2 ptsto 3 ptout macro ?num ; Console #&?num Output, contains the character for output local txrdy ptout&?num equ $ in a,(sts&?num) and m_out jp nz,txrdy push bc ld c,poll ld e,plco&?num call xdos ; poll console #&?num output pop bc txrdy: ld a,c out (data&?num),a ; transmit character ret endm ptout 0 ptout 1 ptout 2 ptout 3 const: ; Console Status call ptbljmp ; compute and jump to handler dw ptsti0 ; Console #0 (Port 10h) status routine dw ptsti1 ; Console #1 (Port 14h) status routine dw ptsti2 ; Console #2 (Port 16h) status routine dw ptsti3 ; Console #3 (Port 18h) status routine conin: ; Console Input call ptbljmp ; compute and jump to handler dw ptin0 ; console #0 (Port 11h) input dw ptin1 ; console #1 (Port 15h) input dw ptin2 ; console #2 (Port 17h) input dw ptin3 ; console #3 (Port 19h) input conout: ; Console Output call ptbljmp ; compute and jump to handler dw ptout0 ; console #0 (Port 11h) output dw ptout1 ; console #1 (Port 15h) output dw ptout2 ; console #2 (Port 17h) output dw ptout3 ; console #3 (Port 19h) output ptbljmp: ; compute and jump to handler ; = console number ; do not destroy ld a,d cp nmbcns ; compare with number of consoles jp c,tbljmp ; ok pop af ; throw away table address rtnempty: xor a ret tbljmp: ; compute and jump to handler ; a = table index add a,a ; double table index for adr offst pop hl ; return adr points to jump tbl ld e,a ld d,0 add hl,de ; add table index * 2 to tbl base ld e,(hl) ; get handler address inc hl ld d,(hl) ex de,hl jp (hl) ; jump to computed cns handler ; MP/M II V2.0 Xios polldevice: ; = device number to be polled ; return 0ffh if ready, 000h if not ld a,c cp nmbdev jp c,devok ld a,nmbdev ; if dev # >= nmbdev, set to nmbdev devok: call tbljmp ; jump to dev poll code devtbl: dw ptsto0 ; poll console #0 output dw ptsti0 ; poll console #0 input dw ptsto1 ; poll console #1 output dw ptsti1 ; poll console #1 input dw ptsto2 ; poll console #2 output dw ptsti2 ; poll console #2 input dw ptsto3 ; poll console #3 output dw ptsti3 ; poll console #3 input nmbdev equ ($-devtbl)/2 ; number of devices to poll dw rtnempty ; bad device handler if banked ; checkb returns in the number of memory banks (0 for non-banked memory) checkb: ld a,hasBankedMemory out (simhport),a in a,(simhport) or a jp z,chkb1 ; no banked memory detected ld a,getCommon ; now check common base out (simhport),a ; send command in a,(simhport) ; receive lower byte cp 0ffh and expcom ; compare with expected lower byte in a,(simhport) ; receive upper byte jp nz,chkb1 ; jp if lower bytes do not agree cp 0ffh and (expcom shr 8) ; compare with upper byte ret z ; everything ok chkb1: ld de,nobankedmemory ; otherwise print message call msg halt ; halt and allow continuation if desired by user jp checkb ; and try again endif ; Select / Protect Memory selmemory: ; = adr of memory descriptor ; -> base 1 byte, ; size 1 byte, ; attributes 1 byte, ; bank 1 byte. if banked ld hl,3 ; offset of 'bank' in memory descriptor add hl,bc ; now points to desired bank call checkb ; make sure we got banked memory ld a,setbankselect ; prepare command out (simhport),a ; execute it ld a,(hl) ; get new bank out (simhport),a ; inform mmu endif ret ; Start Clock startclock: ; will cause flag #1 to be set ; at each system time unit tick ld a,0ffh ld (tickn),a ret ; Stop Clock stopclock: ; will stop flag #1 setting at system time unit tick xor a ld (tickn),a ret ; Exit Region exitregion: ; enable interrupt if not preempted or in dispatcher ld a,(preemp) or a ret nz ei ret ; Maximum Console Number maxconsole: ld a,nmbcns ret initsequence: db setTimerDelta dw deltati db setTimerInterruptAdr dw inthnd db startTimerInterrupts endinitsequence equ $ ; System Initialization. The following registers are input parameters: ; MP/M debugger restart # ; MP/M entry point address for the debugger. ; Place a jump at the proper debugger restart location to the address ; contained in . ; BIOS direct jump table address. ; Place a jump instruction at location 0000H ; in each bank's base page to the address contained in . systeminit: if banked call checkb ; make sure we got banked memory ld b,a ; top bank + 1 sys1: ld a,setbankselect ; prepare command out (simhport),a ; execute it dec b ; bank to map in ld a,b ; prepare out (simhport),a ; inform mmu ld a,jpopcod ; jp instruction ld (sysorg),a ; at 0: jp ld (sysorg+1),hl jp nz,sys1 ; bank 0 is the last bank endif ld b,endinitsequence-initsequence ld hl,initsequence sys2: ld a,(hl) out (simhport),a inc hl dec b jp nz,sys2 ld a,creset ; reset command out (sts0),a ; reset console device out (sts1),a ; reset console device out (sts2),a ; reset console device out (sts3),a ; reset console device out (punstat),a ; and list/punch device ld de,msg1 ; print welcome message call msg ei ret ; return ; Idle procedure ;idle: ; ret ; -or- ; ei ; hlt ; ret ; for full interrupt system ; MP/M II V2.0 Interrupt Handler inthnd: ld (svdhl),hl pop hl ld (svdret),hl push af ld hl,0 add hl,sp ld (svdsp),hl ; save users stk ptr ld sp,lstintstk ; lcl stk for intr hndl push de push bc ld a,0ffh ld (preemp),a ; set preempted flag ; 50 Hz clock interrupt ld a,(tickn) or a ; test tickn, indicates delayed process(es) jp z,notickn ld c,flagset ld e,1 call xdos ; set flag #1 each tick notickn: ld hl,cnt50 dec (hl) ; dec 50 tick cntr jp nz,intdone ld (hl),ticks ld c,flagset ld e,2 call xdos ; set flag #2 @ 1 sec intdone: xor a ld (preemp),a ; clear preempted flag pop bc pop de ld hl,(svdsp) ld sp,hl ; restore stk ptr pop af ld hl,(svdret) push hl ld hl,(svdhl) ; The following dispatch call will force round robin ; scheduling of processes executing at the same priority ; each 1/50th of a second. ; Note: Interrupts are not enabled until the dispatcher ; resumes the next process. This prevents interrupt ; over-run of the stacks when stuck or high frequency ; interrupts are encountered. jp pdisp ; MP/M dispatch seldsk: ld hl,0 ; select disk number ld a,c ld (diskno),a cp ndisks+nhdisks ; number of disk drives ret nc ; error - disk number too high ld l,a ; := disk number ld h,0 add hl,hl ; disk number * 2 add hl,hl ; disk number * 4 add hl,hl ; disk number * 8 add hl,hl ; disk number * 16 ld de,dpbase ; dpbase entries have size of 16 bytes add hl,de ; = 16 * disknumber + dpbase ret home: ld bc,0 ; move to track 00 ; fall into settrk settrk: ld l,c ; save track ld h,b ld (track),hl ret setsec: ld a,c ; set sector ld (sector),a ret sectrn: if nhdisks gt 0 ld l,c ; := BC, prepration for = 0 ld h,b ; load upper byte inc hl ; rebase to one ld a,e ; get lower byte of translate table address or d ; or with upper byte ret z ; if equal to zero, no translation necessary endif ex de,hl add hl,bc ld l,(hl) ld h,0 ret setdma: ld l,c ; set dma address ld h,b ld (dmaad),hl ret wldir: ld hl,(dmaad) ; source of sector is in 'dmaad' ld de,altbuf+3 ; destination inside local buffer ld bc,csecsiz ; sector size is 128 jp ldir80 ; ; altair disk read/write drivers ; read: if nhdisks gt 0 ld a,(diskno) ; get disk number cp ndisks ; compare with number of Altair disks jp c,aread ; carry means we got an Altair disk ld a,hdskRead ; otherwise perform hard disk read call set2 ; send hard disk parameters jp rldir aread equ $ endif call poshed ; select disk 'diskno' and position disk head to 'track' call secget ; position head to desired sector ld hl,altbuf ; address of sector buffer ld e,asecsiz ; number of bytes to read blrd1: in a,(statin) ; get disk status or a ; set sign of byte jp m,blrd1 ; loop until disk has new byte to read in a,(dskread) ; read byte of sector ld (hl),a ; store into buffer inc hl ; point to next position in buffer dec e ; decrement size counter jp nz,blrd1 ; if not zero, we need to continue ld a,cuload ; unload head command out (dskcon),a ; do it xor a ; := 0 means no error rldir: push af ld hl,(dmaad) ; destination address ld de,altbuf+3 ; address of sector just read ld bc,csecsiz ; sector size is 128 ex de,hl ; prepare for ldir call ldir80 pop af ret write: if nhdisks gt 0 ld a,(diskno) ; get disk number cp ndisks ; compare with number of Altair disks jp c,awrite ; carry means we got an Altair disk call wldir ld a,hdskWrite ; otherwise perform hard disk write set2: out (hdskPort),a ; send command ld a,(diskno) ; get disk number sub ndisks ; rebase out (hdskPort),a ; send rebased disk number ld a,(sector) ; get sector dec a ; rebase to 0 out (hdskPort),a ; send rebased sector number ld a,(track) ; get lower byte of track out (hdskPort),a ; send lower byte of track ld a,(track+1) ; get upper byte of track out (hdskPort),a ; send upper byte of track ld a,(dma2) ; get lower byte DMA address out (hdskPort),a ; send lower byte of DMA address ld a,(dma2+1) ; get upper byte of DMA address out (hdskPort),a ; send upper byte of DMA address in a,(hdskPort) ; perform command and get result ret dma2: dw altbuf+3 awrite equ $ endif call poshed ; select desired disk and position to desired track call secget ; position head to desired sector call wldir ld a,cwrseq ; command for 'start write enable sequence' out (dskcon),a ; do it ld hl,altbuf ; point to first byte in local buffer ld b,asecsiz+1 ; number of bytes to write (additional byte triggers 'real' write) wready: in a,(statin) ; get status rra ; get bit for ready for write jp c,wready ; loop until ready for write ld a,(hl) ; byte to write out (dskwrit),a ; write byte inc hl ; point to next byte dec b ; decrement counter of bytes jp nz,wready ; jp if not done ld a,cuload ; unload head command out (dskcon),a ; do it listst: xor a ; := 0 means no error ret ; position disk on track zero, == 0 at the end dhome: in a,(statin) ; position disk to track 0 and mtzero ; mask for 'head is on track zero' ret z ; track zero reached, done call whmove ; loop until head movement is allowed ld a,cstepot ; command for 'step head out one track' out (dskcon),a ; do it jp dhome ; try again ; Select disk 'diskno' and position disk head to 'track' poshed: call calcd ; position altair disk head ld a,d ; select disk , cur track in out (selout),a ; select disk in a,(statin) ; get status of selected drive cp mall ; ok? jp z,selerr ; no! ld a,b ; := track of selected disk cp track1 ; compare with non-existing track jp nz,alseek ; if a regular track, proceed to seek call dhome ; position disk to track 0 ld b,a ; := 0 (current track) ; Input: location 'track' contains desired track ; contains current track ; Output: desired track is reached and stored in track array alseek: ld a,(track) ; seek to 'track' (cur track in b) ld e,a ; := desired track ld a,b ; := current track sub e ; := current track - desired track ret z ; we are already at desired track ld e,a ; e is the number of "step in" or "step out" jp c,stpin ; current track < desired track ld c,cstepot ; command for step head out one track jp aseek ; perform steps stpin: ld c,cstepin ; command for step head in one track cpl ; := ~(current track - desired track) inc a ; := desired track - current track (positive) ld e,a ; is positive number of tracks to move aseek: call whmove ; loop until head movement is allowed ld a,c ; get command (step in or step out) out (dskcon),a ; perform it dec e ; next iteration jp nz,aseek ; loop if not done call calcd ; get pointer to 'track' of 'diskno' ld a,(track) ; this is the current track ld (hl),a ; update 'track' of 'diskno' ret selerr: pop hl ; discard return address ld a,bioserr ; := 1 means error ret ; loop until head movement is allowed whmove: in a,(statin) ; get status and mhm ; mask for 'head movement allowed' jp nz,whmove ; loop until movement allowed ret ; Input: - implicit input is location 'diskno' ; Output: contains the current track of 'diskno' ; , and contain 'diskno' ; points to 'track' of 'diskno' calcd: ld a,(diskno) ; get 'diskno' ld e,a ; := 'diskno' ld hl,ontrk0 ld d,0 add hl,de ; points to 'track' of 'diskno' ld b,(hl) ; := 'track' of 'diskno' ld d,e ; := 'diskno' ret ; Input: 'sector' contains desired sector number ; Output: head is positioned at desired sector secget: ld a,cload ; command to load head to drive surface out (dskcon),a ; do it ld a,(sector) ; := desired sector dec a ; adjust to range 0..(spt-1) ld b,a ; := adjusted, desired sector cp spt ; compare with sectors per track jp c,seclp2 ; desired sector is less than total sectors per track, ok ld de,secmsg ; prepare error message call msg ; print it halt ; not much we can do seclp2: in a,(secpos) ; get sector position rra ; rotate T bit into carry jp c,seclp2 ; loop until sector is positioned to read or write and sptmask ; now contains the sector under the head cp b ; compare with desired sector jp nz,seclp2 ; repeat if not equal ret ; Move bytes from start address to destination . ; This is equivalent to the Z80 instruction 'LDIR'. ; This subroutine dynamically determines the processor. ldir80: if banked push bc ; save registers , and push de push hl call swtuser ; map to bank of caller pop hl ; restore registers , and pop de pop bc endif xor a ; := 0 dec a ; := 1111'1111b jp pe,ldir1 ; on an 8080 this means parity is even ldir ; otherwise we have a Z80 if banked jp swtsys ; restore system bank, 'swtsys' returns else ret ; done endif ldir1: ld a,(hl) ; get byte from source ld (de),a ; put byte to destination inc hl ; point to next source address inc de ; point to next destination address dec bc ; decrement number of bytes to move ld a,c ; := ( or ) or b jp nz,ldir1 ; not zero, move again if banked jp swtsys ; restore system bank, return is there else ret ; done endif ; print the message pointed to by and terminated by '$' to the console ; leaves unchanged msg: ld a,(de) ; get character cp '$' ; is is the terminating character? ret z ; yes, we are done ld c,a ; 'conout' expects the character in call con0 ; disply it on console inc de ; point to next character jp msg ; and repeat con0: in a,(sts0) ; get console status and m_out ; mask output bit jp z,con0 ; jump back if not ready for output ld a,c ; prepare character for output out (data0),a ; do it ret cnt50: db ticks ; 50 tick cntr = 1 sec intstk: ; local intrpt stk dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h dw 0c7c7h,0c7c7h,0c7c7h,0c7c7h,0c7c7h lstintstk: svdhl: dw 0 ; saved Regs HL during interrupt handling svdsp: dw 0 ; saved SP during interrupt handling svdret: dw 0 ; saved return during interrupt handling tickn: db 0 ; ticking boolean, true = delayed preemp: db 0 ; preempted boolean ; diskette drives dpbase equ $ dw atrans,0,0,0,dirbf,mits2,chk00,all00 dw atrans,0,0,0,dirbf,mits2,chk01,all01 dw atrans,0,0,0,dirbf,mits2,chk02,all02 dw atrans,0,0,0,dirbf,mits2,chk03,all03 dw atrans,0,0,0,dirbf,mits2,chk04,all04 dw atrans,0,0,0,dirbf,mits2,chk05,all05 dw atrans,0,0,0,dirbf,mits2,chk06,all06 dw atrans,0,0,0,dirbf,mits, chk07,all07 if nhdisks gt 0 defdw macro ?value dw all0&?value endm defdp macro ?number local ?hdi ?hdi defl 8 rept ?number dw 0,0,0,0,dirbf,simhd,0 defdw %?hdi ?hdi defl ?hdi+1 endm endm defdp nhdisks simhd: dw spt ; SPT, sectors per track db 5 ; BSH, data allocation block shift factor, for BLS=4,096 db 31 ; BLM, data allocation block mask, for BLS=4,096 db 1 ; extent mask for BLS=4,096 and DSM>255 dw 2047-restrk ; DSM, maximum data block number dw 1023 ; DRM, number of directory entries - 1 db 0ffh,0 ; AL0, AL1, 8 blocks reserved to hold all entries ; (number of directory entries)*32 = (number of reserved blocks)*(block size BLS) ; 1024 * 32 = 8 * 4096 dw 0 ; CKS, set 0 since hard disk is fixed dw restrk ; OFF, number of tracks skipped at beginning of disk endif ; copylib (default) format mits: dw spt ; spt, sectors per track db 3 ; allocation block shift factor, bsh db 7 ; data allocation block mask, blm, allocation size (bls) = 1024 db 0 ; extent mask dw dsm07 ; dsm, maximum data block number dw dirent ; drm, number of directory entries - 1 db 0ffh,0 ; al0, al1, 8 blocks reserved to hold all entries ; 256 * 32 = 8 * 1024 ; (drm+1) * 32 = (number of bits in al0 and al1) * bls dw cks ; cks = (drm + 1)/4 dw restrk ; off, number of tracks skipped at beginning of disk mits2: dw spt ; spt, sectors per track db 4 ; allocation block shift factor, bsh db 15 ; data allocation block mask, blm, allocation size (bls) = 2048 db 0 ; extent mask dw dsm06 ; dsm, maximum data block number dw dirent ; drm, number of directory entries - 1 db 0f0h,0 ; al0, al1, 4 blocks reserved to hold all entries ; 256 * 32 = 4 * 2048 ; (drm+1) * 32 = (number of bits in al0 and al1) * bls dw cks ; cks = (drm + 1)/4 dw restrk ; off, number of tracks skipped at beginning of disk ; speedball (copylib) skewtable atrans: db 01,18,03,20,05,22,07,24 db 09,26,11,28,13,30,15,32 db 17,02,19,04,21,06,23,08 db 25,10,27,12,29,14,31,16 msg1: db cr, lf, 'MP/M XIOS (SIMH ALTAIR 8800, V1.11, ' if nhdisks gt 0 db '0' + nhdisks db ' HD, ' endif if banked db 'Banked, ' endif db '20-Oct-02)' db cr, lf, '$' secmsg: db cr, lf, 'Cannot find sector in register ', cr, lf, '$' if banked nobankedmemory: dbhex macro ?val if ?val gt 9 db 'A'+?val-10 else db '0'+?val endif endm nobankedmemory: db cr, lf, 'No banked memory detected with common base 0' dbhex %(0fh and (expcom shr 12)) dbhex %(0fh and (expcom shr 8)) dbhex %(0fh and (expcom shr 4)) dbhex %(0fh and expcom) db 'h.', cr, lf, '$' endif ; position disk drive head ontrk0: db track1 ; current track# drive 0 (logical 1) db track1 ; current track# drive 1 (logical 2) db track1 ; current track# drive 2 (logical 3) db track1 ; current track# drive 3 (logical 4) db track1 ; current track# drive 4 (logical 5) db track1 ; current track# drive 5 (logical 6) db track1 ; current track# drive 6 (logical 7) db track1 ; current track# drive 7 (logical 8) track: dw 0 sector: db 0 dmaad: dw 0 diskno: db 0 ; begin scratch area for bdos dirbf: ds 128 ; directory work space all00: ds ((dsm06+1)/8)+1 all01: ds ((dsm06+1)/8)+1 all02: ds ((dsm06+1)/8)+1 all03: ds ((dsm06+1)/8)+1 all04: ds ((dsm06+1)/8)+1 all05: ds ((dsm06+1)/8)+1 all06: ds ((dsm06+1)/8)+1 all07: ds ((dsm07+1)/8)+1 if nhdisks gt 0 deflab macro ?value all0&?value equ $ endm defall macro ?number local ?hdi ?hdi defl 8 rept ?number deflab %?hdi ds 256 ?hdi defl ?hdi+1 endm endm defall nhdisks endif chk00: ds cks chk01: ds cks chk02: ds cks chk03: ds cks chk04: ds cks chk05: ds cks chk06: ds cks chk07: ds cks altbuf: ds asecsiz+1 end