
;
; BIOS FUER DEN CP/M EMULATOR
;
;
; (C) 1990,1991,1992 by Jrgen Weber
;
; Version: 1.0
;          1.1     Disparameter laden/speichern
;          1.11    bios read zeigt Dos File Ende an
;          1.2     conin bersetzt CsrTasten nach WS
;                  erg„nzte time fkt und Blink Attribut

INCLUDE Z80EMU.INC

EXTRN conout:near,conin:near,reset_conout:near,sel_scr_page:near
EXTRN cursor_off:near,cursor_on:near

PUBLIC prg_exit,bios88
PUBLIC breakflag


%nosyms        ; keine symbols im listing
warn           ; alle warnings an

DOSSEG   ; UNBEDINGT notwendig, da fuer Speicherverwaltung des Z80 Segments
         ; und der Ramdisk Stacksegment als letztes kommen muss
         ; auch mussen Stack- und Codesegment Klassen 'STACK' bzw.
         ; 'CODE'bekommen

LOCALS   ; fuer locale Labels in Prozeduren

JUMPS    ; automatische Sprunglaengenanpassung

CALLZ macro label ; Call if zero
local exit
      jnz short exit
      call label
exit:
endm

CALLNZ macro label ; Call if not zero
local exit
      jz   short exit
      call label
exit:
endm

CALLC macro label ; Call if carry
local exit
      jnc  short exit
      call label
exit:
endm


;
; segmente des emulators:
;

; emulator_seg   : emulator und bios code
; emudata_seg    : emulator data und z80 bios code
; stack_seg      : emulator und bios stack
; z80cpu_seg     : Segment des emulierten z80, 64k, dummy at 0 (in z80seg_adr)
; ramdisc_seg    : ramdisc fuer cp/m
;


include dos.inc         ; dos functionen

;
; ******* KONSTANTEN ********
;

DEFAULT_DMA        EQU 80H
RECORD_LEN         EQU 80H



ZBIOSLEN           equ (zbiosend-zbiosbeg)
ZBIOSDISPL         equ (0ffffh-ZBIOSLEN) and 0ff00h
; dies bewirkt Start auf neuer Seite, damit Bios auf XX00 anfaengt
ZBIOS              equ offset zbiosbeg+ZBIOSDISPL

CTRL_BREAK_INT EQU 1BH

ESC_KEY            EQU 27

EMU_SCR_PAGE EQU 0
DOS_SCR_PAGE EQU 1

; folgende Konstanten werden als Flags zur Addressberechnung
; im Sektorbuffer benoetigt

READ_OP            EQU 0
WRITE_OP           EQU 0FFH

PHYSDSK            EQU 0


BDOSLEN            equ 1600h     ; Laenge ccp+bdos cp/m 2.2
BDOS_CHK_SUM       equ 2d88h     ; Checksumme ueber Bdos Copyright Meldung

SYS_FRST_SEC       EQU 28
         ; erster absoluter Sektor, den Bdos auf SysSpuren belegt

SYS_SECS           EQU BDOSLEN/RECORD_LEN ; Anzahl Sektoren, die System belegt

TRACK_BUF_LEN      EQU 10*512

CPM_EOF            EQU 26

PHYS_DRV           EQU 0   ; # diskettenlaufwerk
RMD                equ 1   ; # ramdisk


PHYS_SEKLEN        equ 512
NSECTS             equ    2
NDISKS             equ    2  ; last disk # +1
BPS                equ    2

; -------------------------------------
;
; emulator data segment
;
; --------------------------------------


emudata_seg segment para public 'DATA'

include zbios.inc

dpb_cpc     dpb <SPTC,BSHC,BLMC,EXMC,DSMC,DRMC,AL0C,AL1C,CKSC,OFFC>
dpb_dta     dpb <SPTD,BSHD,BLMD,EXMD,DSMD,DRMD,AL0D,AL1D,CKSD,OFFD>
dpb_eig     dpb <SPTD,BSHD,BLMD,EXMD,DSMD,DRMD,AL0D,AL1D,CKSD,OFFD>
dpb_cpm86SS dpb <SPT0,BSH0,BLM0,EXM0,DSM0,DRM0,AL00,AL10,CKS0,OFF0>
dpb_cpm86DS dpb <SPT1,BSH1,BLM1,EXM1,DSM1,DRM1,AL01,AL11,CKS1,OFF1>

ramdisklen dw (?)
lasttrack_rd db (?) ; 0..127  (512K)

PUNCH_BUF_SIZE  equ 512
READER_BUF_SIZE equ 256

start_string db 27,'E',27,'Y',32+8,32+24,27,'p'
             db 'ZSIM',27,'q'
             db 27,'Y',32+10,32+18
             db 'Free for personal use'
             db 27,'Y',32+12,32+18
             db 27,'p','USE AT YOUR OWN RISC !',27,'q'
             db 27,'Y',32+15,32+18
             db '(C) 1990,1992 by '
             db 27,'Y',32+17,32+18
             db 'Jrgen G. Weber'
             db 27,'Y',32+18,32+18
             db 'Wiesentalstr. 1'
             db 27,'Y',32+19,32+18
             db 'W-7170 Schw„bisch Hall'
             db 27,'Y',32+20,32+18
             db 'Federal Republic of Germany'
             db 27,'Y',32+24,32+17,27,'p'
             db 'Insert a CP/M disk then press any key'
             db 27,'q'
             db 0


db 'ZSIM looks for the fellowing string: '

bdos_str db '        COPYRIGHT (C) 1979, DIGITAL RESEARCH  '
bdos_str_end equ this byte

cpmsys_fileName  db 'CPMSYS.CPM',0
ramdisc_fname    db 'RAMDISC.CPM',0
punch_fileName   db 'PUNCH.CPM',0
reader_fileName  db 'READER.CPM',0
temp_fname       db '\EMUCPM.OVL',0

fmt_file_flg db FALSE
fmt_file_name    db 80 dup (?)
file_promt_str db 'Enter Disk Parameter filename: ',0
file_promt_str_end equ $


menutab label word

     dw m_esc

     dw m_continue
     dw m_save_rd_quit
     dw m_quit
     dw m_save_rd
     dw m_del_pf
     dw m_init_rdr
     dw m_shell
     dw m_edit_disk_parm
     dw m_save_d_parm
     dw m_load_d_parm
     dw m_w_boot
     dw m_info

menu_strs    db '\Continue','|'
             db 'Save \Ramdisc/Quit','|'
             db '\Quit','|'
             db '\Save Ramdisc','|'
             db '\Delete Punch File','|'
             db '\Init Reader','|'
             db '\OS Shell','|'
             db 'Disk \Parameters','|'
             db 'Sa\ve Parameters','|'
             db '\Load Parameters','|'
             db '\Warm Boot','|'
             db '\About','|'
             db 0
menu_strs_end equ this byte

MESG_ESC equ 0

LONGEST_MESSAGE=20
MESSAGE_COUNT=12

prg_ext_box_res  dw 0ffffh

ins_cpm_disk_txt db 'Insert CP/M disk and press ENTER|',0
ins_cpm_disk_txt_end equ this byte

ins_dos_disk_txt db 'Insert MS-DOS disk and press ENTER|',0
ins_dos_disk_txt_end equ this byte

disk_err_txt db 'Disk Error. Press ESC|',0
disk_err_txt_end equ this byte

disk_ful_txt db 'Disk full. Press ESC|',0
disk_ful_txt_end equ this byte

f_not_found_txt db 'File not found. Press ESC|',0
f_not_found_txt_end equ this byte

on_sign_string equ this byte
                   db 27,'E',27,'H'
                   db 'jgw 64K cp/m 80 bios ver 1.2 --  '
                   db ??date
                   db '  (C) 1990,1992 by Jrgen G. Weber'
                   db 13,10,10
                   db 'BDOS: ',27,'j',0
bad_format         db 13,10
                   db 'Unknown disc format. Insert new disc and press any key'
                   db 13,10,0
no_sys             db 13,10
                   db 'Could not read System sectors. Insert'
                   db ' new disc and press any key'
cr_lf_txt          db 13,10,0
control_c_txt      db '^C...',0

mfulstr db 13,10,'Not enough memory',13,10
mfsend equ this byte

illparm_str db 13,10,'Illegal Command Line Parameter',13,10
illparm_strend equ this byte

dma_txt db 13,10
        db 'Fatal: DMA Boundary Crossing'
        db 13,10
dma_txt_end equ this byte

exit_str db 'Type EXIT to return to CP/M Emulator ...',13,10
exit_str_end equ this byte


exec_par_block equ this word
       dw 0
       dw offset exec_cmd_line
       dw seg    exec_cmd_line
       dd 0
       dd 0
exec_cmd_line equ this word
       db ec_str_end-ec_str
ec_str db ''
ec_str_end equ this byte
       db 13

no_cc_str  db 13,10,'COMMAND.COM not found. Press ESC.',13,10
no_cc_str_end equ this byte

exec_fname db '\COMMAND.COM',0


good_dpb_edit db (?)
db 2 dup (?)

breakflag  db FALSE

old_int1b  dd (?)
stackpoi   dw (?)

sp_save    dw (?)
ss_save    dw (?)

z80seg_adr dw (?)
rdseg_adr  dw (?)
rdlen      dw (?)
exec_mem_start dw (?)

psp_adr    dw (?)
prog_len   dw (?)
mem_end    dw (?)
ovl_base   dw (?)
hd_drive_flag db (?)
ms_current_drive db (?)

tmp_word        dw (?)

z80_pc             dw ?
ccp_adr            dw ?
bdos_adr           dw ?
bios_adr           dw ?

month_tab db 31,28,31,30,31,30,31,31,30,31,30,31

; flag, dass im Speicher noch ein nicht auf disk geschriebener
; nicht-dir Track ist
write_flag         db FALSE

in_boot_flag       db FALSE

home_flag          db FALSE
last_track_written db 0,0
dirtrack           db 0,0
r_track            db 0,0
w_track            db 0,0

track              db 0,0
sector             db 0,0
last_track_read    db 0ffh  ; default unmoeglich, noch nicht gelesen

ms_phys_sec_len    db (?)
pspt_last          db 9
phys_tracks_last   db 40
frstps_last        db 041h
dirtr_last         db 2
dpb_last           dw offset dpb_cpc

cpm86dd_flag       db FALSE

; ACHTUNG: die Reihenfolge der folgenden Vars nicht
;          ver„ndern, da sie so ans Modula UP bergeben werden

cpm_drive          db PHYS_DRV
first_phys_sec     db 41h
phys_tracks        db (?)
phys_sec_pt        db (?)
cpm_phys_sec_len   db 2
autologin_flag     db TRUE

; end Reihenfolge wichtig


retry_count        db 5
side               db 0

dmaad              dw ?          ;direct memory address
diskno             db ?          ;disk number 0-15


punch_buf_entries  dw (?)
punch_buf_ptr      dw (?)

reader_empty_flag  db TRUE
reader_buf_entries dw (?)
reader_buf_ptr     dw (?)
reader_file_pointer dw 0,0


; zeigt auf Bufferadresse des Tracks, der geschrieben werden soll
outbuf_ptr         dw (?)

cpm_bdos_buf db BDOSLEN dup (?)

dirtrbuf db TRACK_BUF_LEN dup (?)
secbuf   db TRACK_BUF_LEN dup (?)
wrtbuf   db TRACK_BUF_LEN dup (?)

punch_buf  db PUNCH_BUF_SIZE dup (?)
reader_buf db READER_BUF_SIZE dup (?)

; dass die Buffer in der EXE-Datei erscheinen, laesst sich
; durch die Verwendung einer Gruppe mit den Buffern als
; Extra Segment umgehen
; jedoch muss dann bei jedem offset der Gruppenname angegeben werden

emudata_seg ends

; ------------------------

DATA segment para public 'DATA'
     ; Dummysegment zur Kombinierung mit Modula 2
DATA ends

STACK_SIZE EQU 1000H

stack_seg  segment para stack 'STACK'
      dw STACK_SIZE dup (?)            ; Stack ist recht gross
                                  ; wegen Modula 2 UPs
stack_seg  ends

z80cpu_seg segment at 0 ; dummy
z80cpu_seg ends

; -------------------------------------
; emulator und bios segment start
; -------------------------------------

emulator_seg segment para public 'CODE'

assume  cs:emulator_seg,ds:emudata_seg,es:z80cpu_seg,ss:stack_seg

m2_ds  dw (?)                          ; fmodula routinen erwarten
                                       ; bei cs:0 ihr DS

init proc                              ; emulator start

       mov  ax,emudata_seg

       mov  ds,ax            ; damit Vars angesprochen werden koennen

       mov  stackpoi,sp

       mov bx,ss             ; finde Programmende
       mov ax,sp
       mov cl,4
       shr ax,cl             ; ax:=4
       add bx,ax             ; bx=>programm ende
       inc bx                ; vorsichtshalber

       push bx
       mov  ax,es            ; es:0 => PSP
       mov  psp_adr,ax
       mov  bx,ax

       mov si,0
       mov ax,[es:si+2]      ; ax=> mem end
       mov  mem_end,ax
       push ax
       sub  ax,1000h
       mov  ovl_base,ax
       pop  ax
       push ax
       sub  ax,bx            ; mem end - prg start
       mov  prog_len,ax
       pop  ax               ; mem end
       pop  bx               ; prg end


       sub ax,bx             ; ax:=free mem
       mov  z80seg_adr,bx

       mov  cx,10000h / 16
       add  bx,cx

       mov  rdseg_adr,bx

       sub  ax,cx
       jc   short @@memful

       mov  ramdisklen,ax

       call parse_cmd_line
       mov dx,offset illparm_str
       mov cx,illparm_strend-illparm_str
       jc short abort              ; Fehler: falscher Parameter

       mov  bx,offset start_string
       call puts
       call conin            ; wait for keypressed

       call patch_int

       DOS  GET_DISK_DRIVE
       mov  ms_current_drive,al

       mov  ax,z80seg_adr
       call clearz80         ; loesche z80 mem

       call init_ramdisc

       call patch_box

; physikalische Sektorlaenge merken

      push es
      mov  ax,0
      mov  es,ax
      les  di,[es:78h]
      mov  al,es:[di+3]
      pop  es
      mov  ms_phys_sec_len,al


;
; offset z80 bios start merken
;
       mov bios_adr,ZBIOSDISPL          ; bios Sprungleiste einrichten

       call z80ini                      ; reset z80 cpu

       mov ax,z80seg_adr
       mov es,ax
       mov ds,ax
;
; weiter mit cp/m cold boot
;
       mov al,0
       jmp bios88

@@memful:
       mov cx,mfsend-mfulstr
       mov dx,offset mfulstr
abort:
       mov bx,STDERR
       DOS WRITE_TO_HANDLE
       mov al,1
       DOS TERMINATE_EXE
init   endp

parse_cmd_line proc
       PUSHR <ax,bx,cx,di,si,ds,es>
        mov ax,ds
        mov es,ax             ; es:=prog vars
        mov ax,psp_adr
        mov ds,ax
        mov di,offset fmt_file_name
        mov si,80h            ; si => Commandozeilenparam
        lodsb                 ; count
        mov ch,0
        mov cl,al
        or  al,al
        mov al,FALSE
        jz  short @@no_parm
@@findslash:
        lodsb
        cmp al," "
        loopz @@findslash     ; +DEC CX
        cmp al,"/"
        jnz short @@parm_err
        lodsb
        dec cx
        and al,not ('a'-'A')  ; toupper
        cmp al,'F'
        jnz short @@parm_err
        mov di,offset fmt_file_name
@@SKIP_BLN:
        lodsb
        cmp al," "
        loopz @@SKIP_BLN; +DEC CX
        stosb         ; 1. non-blank

        rep movsb
        mov al,0
        stosb         ; Filename Endzeichen
        mov al,TRUE
@@no_parm:
       POPR <es,ds,si,di,cx,bx>
       mov fmt_file_flg,al
       pop ax
       clc
       ret
@@parm_err:
       POPR <es,ds,si,di,cx,bx,ax>
       stc
       ret
parse_cmd_line endp

tst_hd_drive proc
       PUSHR <ax,dx>
       mov hd_drive_flag,FALSE
       mov dl,cpm_drive
       mov ah,15h                 ; Laufwerktyp ?
       int 13h
       jc  short @@exit           ; PC,XT
       cmp ah,2                   ; erkennt DiskWechsel
       jnz short @@exit
       mov hd_drive_flag,TRUE
@@exit:
       POPR <dx,ax>
       ret
tst_hd_drive endp

;
; der Interupt 1bH (Control-Break Interupt) muá auf eine Routine
; gelegt werden, die ein Abbruch Flag setzt
; Dieses wird dann bei jedem Bios Aufruf ausgewertet
;
patch_int proc
       PUSHR <ds,es,ax,bx,dx>
       mov al,CTRL_BREAK_INT
       DOS GET_VECTOR
       mov word ptr old_int1b,bx
       mov bx,es
       mov word ptr old_int1b+2,bx
       mov  dx,offset new_int1b
       push cs
       pop  ds
       mov al,CTRL_BREAK_INT
       DOS SET_VECTOR
       POPR <dx,bx,ax,es,ds>
       ret
patch_int endp

restore_int proc
       push ds
       lds dx,old_int1b
       mov al,CTRL_BREAK_INT
       DOS SET_VECTOR
       pop ds
       ret
restore_int endp

new_int1b proc
       push ax
       push ds
       mov  ax,emudata_seg
       mov  ds,ax
       mov  breakflag,TRUE
       pop  ds
       pop  ax
       iret
new_int1b endp

; wird auch von Z80EMU.OP76 HALT aufgerufen

prg_exit proc FAR
       PUSHR <ds,es,ax>
       PUSHR <bx,cx,dx,si>
       mov  ax,emudata_seg
       mov  ds,ax
       mov  ax,z80seg_adr
       mov  es,ax
       call restore_int
       mov  breakflag,FALSE

       call wrt_out_punch

       mov  cx,menu_strs_end-menu_strs   ; high(messages)
       mov  si,offset menu_strs          ; offs(messages)

       call box_call
       sal ax,1
       mov di,ax
       call menutab[di]

       call patch_int
       POPR <si,dx,cx,bx>
       POPR <ax,es,ds>
       ret
prg_exit endp


; Men durch ESC abgebrochen
m_esc proc
       ret
m_esc endp

; Menpunkt Emulation fortfahren
m_continue proc
      ret
m_continue endp

; Menpunkt Ramdisk speichern
m_save_rd proc
      call save_ramdisc
      ret
m_save_rd endp

; Menpunkt Quit
m_quit proc
; etwaige Zeichen im Punch Buffer noch ausschreiben
       call wrt_out_punch

       mov cl,31                  ; GotoXY(1,25)
       call conout
       mov cl,1
       call conout
       mov cl,25
       call conout
       mov  al,0
ABORT_EMU:
       push ax
       call ins_dos_disk
       pop ax
       DOS TERMINATE_EXE
m_quit endp

; Menpunkt Ramdisk speichern + Quit
m_save_rd_quit proc
       call save_ramdisc
       jmp short m_quit
m_save_rd_quit endp

; Menpunkt Punch buffer file l”schen
m_del_pf proc
     call del_punch_file
     ret
m_del_pf endp
; Menpunkt Reader initialisieren
m_init_rdr proc
      call reset_reader_buf
      ret
m_init_rdr endp

; Menpunkt OS Shell
m_shell proc
       call dos_shell
       ret
m_shell endp

; Menpunkt Diskettenparameter editieren
m_edit_disk_parm proc
       call edit_disk_pars
       cmp  good_dpb_edit,1
       jnz  short @@exit
       mov  cpm86dd_flag,FALSE
       mov  ah,0
       mov  al,dpb0.off-dpb0.spt  ; off
       call get_dpb_entrie        ; nach bx
       mov  dirtrack,bl

       cmp  in_boot_flag,TRUE
       jz   short @@exit          ; mit boot weitermachen
       call m_w_boot
@@exit:
       ret
m_edit_disk_parm endp

prepare_fname proc
       mov  di,offset fmt_file_name
       push di
       mov  cx,80
       mov  si,offset file_promt_str
       mov  bx,file_promt_str_end-file_promt_str
       call input_string
       pop  bx
       or   byte ptr [bx],0          ; Test ob was eingegeben, nein => Z
       ret
prepare_fname endp

; Menpunkt Diskettenparameter speichern
m_save_d_parm proc
       call prepare_fname
       jz short @@exit
       call save_disk_pars
@@exit:
       ret
m_save_d_parm endp

; Menpunkt Diskettenparameter laden
m_load_d_parm proc
       call prepare_fname
       jz short @@exit
       call load_disk_pars
       jmp  short m_w_boot
@@exit:
       ret
m_load_d_parm endp

; Menpunkt CP/M warm boot durchfhren
m_w_boot proc
       mov sp,stackpoi
       call patch_int
       mov al,1
       jmp bios88
m_w_boot endp

; Menpunkt About Author
m_info proc
       call about_author
       ret
m_info endp



; Fehler durch Ueberschreiten der DMA Segment Grenze
; siehe c't 4/90 S.412

dma_bound proc
       mov bx,STDERR
       mov cx,dma_txt_end-dma_txt
       mov dx,offset dma_txt
       DOS WRITE_TO_HANDLE
       mov al,1
       jmp ABORT_EMU
dma_bound endp

; out: ax=result
ins_dos_disk proc
       mov al,ms_current_drive
       cmp al,cpm_drive
       jnz short @@exit
       mov si,offset ins_dos_disk_txt
       mov cx,ins_dos_disk_txt_end-ins_dos_disk_txt
       call box_call
@@exit:
       ret
ins_dos_disk endp

; out: ax=result
ins_cpm_disk proc
       mov al,ms_current_drive
       cmp al,cpm_drive
       jnz short @@exit
       mov si,offset ins_cpm_disk_txt
       mov cx,ins_cpm_disk_txt_end-ins_cpm_disk_txt
       call box_call
@@exit:
       ret
ins_cpm_disk endp


disp_disk_err proc
       PUSHR <si,cx>
       pushf                      ; cy aufheben
       mov si,offset disk_err_txt
       mov cx,disk_err_txt_end-disk_err_txt
       call box_call
       POPR <cx,si>
       popf
       ret
disp_disk_err endp


disp_f_not_found proc
       pushf                      ; cy aufheben
       PUSHR <si,cx>
       mov si,offset f_not_found_txt
       mov cx,f_not_found_txt_end-f_not_found_txt
       call box_call
       POPR <cx,si>
       popf
       ret
disp_f_not_found endp

disp_disk_ful proc
       PUSHR <si,cx>
       mov si,offset disk_ful_txt
       mov cx,disk_ful_txt_end-disk_ful_txt
       call box_call
       POPR <cx,si>
       ret
disp_disk_ful endp


EXTRN EmuMenu_DoMenue:far,EmuMenu_EditDPB:far
EXTRN EmuMenu_InputString:far,EmuMenu_About:far,EmuMenu_init:far

; fmodula2 laedt merkwuerdigerweise am Anfang DS aus CS:0
; also muss man dort ds hinbringen
; Trotzdem sollte vor Aufruf einer Funktion DS richtig
; geladen sein.

patch_box proc
       mov  ax,DATA
       mov  [cs:0],ax
       ret
patch_box endp

box_call proc                     ; um Modula 2 UPs aufzurufen
;
; in:  ds:si => menuetext
;      cx    =  len(menuetext)
; out: ax = result
;
; auf dem STACK muss GENUG FREI sein, um zu erzeugendes WINDOW
; ABZUSPEICHERN
;
COMMENT @

DEFINITION MODULE EmuM2;

FROM SYSTEM IMPORT BYTE,WORD;

TYPE PhysDiskPars = RECORD
           cpm_drive       : BYTE;
					 first_phys_sec  : BYTE;
					 phys_tracks     : BYTE;
           phys_sec_pt     : BYTE;
           bytes_per_sec   : BYTE;
           autologin_flag  : BYTE;
       END;

dpb = RECORD
             spt  : WORD;
             bsh  : BYTE;
             blm  : BYTE;
             exm  : BYTE;
             dsm  : WORD;
             drm  : WORD;
             al0  : BYTE;
             al1  : BYTE;
             cks  : WORD;
             off  : WORD;
           END;
     DPBPtr = POINTER TO dpb;
     PDPPtr = POINTER TO PhysDiskPars;


PROCEDURE DoMenue(x,y:CARDINAL;messages:ARRAY OF CHAR;
                  VAR WinSave:ARRAY OF CHAR;VAR res:CARDINAL);
(* Pop Up Menue, obere linke Ecke des Rahmens bei x,y.
   messages = String mit darzustellenden Menuepunkten; Stringende = 0C.
   Menuepunkte werden durch | getrennt, auch nach letztem Menuepunkt
   MUSS | stehen.
   Jeder Menuepunkt darf durch Druecken eines einzigen Zeichens angewaehlt
   werden, das hell dargestellt wird. Diesem Zeichen muss der Backslash \
   vorangestellt werden.
   Es duerfen maximal 20 Menuepunkte sein.
   WinSave muss genug Platz enthalten, um Hintergrund + dessen Attribute
   abzuspeichern, = (Laengster Menuepunkt+2)*2*(Menuepunkte+2)
*)

PROCEDURE EditDPB(p:DPBPtr;q:PDPPtr;
                  VAR WinSave:ARRAY OF CHAR;VAR OK:BOOLEAN);
(* Len WinSave=20*18*2=720 *)

PROCEDURE InputString(VAR WinSave,s,p:ARRAY OF CHAR);
(* Es wird vorrausgesetzt, daá prompt p < 30 und string s < 30 *)


PROCEDURE About(VAR WinSave:ARRAY OF CHAR);
(* Author ausgeben *)


END EmuM2.


@

HIGH_WinSave=(LONGEST_MESSAGE+2)*2*(MESSAGE_COUNT+2)

IF HIGH_WinSave gt 2*STACK_SIZE/3
   %out WARNING: POPUP WINDOW REQUIRES MUCH OF STACK
ENDIF

       PUSHR <bx,cx,dx,di,si,bp,ds,es>
       sub  sp,HIGH_WinSave
       mov  bx,sp
       mov  ax,20
       push ax                 ; x
       mov  ax,7
       push ax                 ; y
       push cx                 ; high(messages)
       push ds                 ; seg(messages)
       push si                 ; offs(messages)
       mov  cx,HIGH_WinSave
       push cx                 ; high(WinSave)
       push ss                 ; seg(WinSave)
       push bx                 ; offs(WinSave)
       mov  ax,seg prg_ext_box_res
       push ax                 ; seg(prg_ext_box_res)
       mov  ax,offset prg_ext_box_res
       push ax                 ; offs(prg_ext_box_res)
;box_adr equ this byte
       mov  ax,DATA
       mov  ds,ax
       call EmuMenu_DoMenue
       add  sp,HIGH_WinSave
       POPR <es,ds>
       mov  ax,prg_ext_box_res
       POPR <bp,si,di,dx,cx,bx>
       ret
box_call endp


edit_disk_pars proc

HIGH_WinSave=800

IF HIGH_WinSave gt 2*STACK_SIZE/3
   %out WARNING: POPUP WINDOW REQUIRES MUCH OF STACK
ENDIF
       PUSHR <bx,cx,dx,di,si,bp,ds,es>
       sub  sp,HIGH_WinSave
       mov  bx,sp
       mov  ax,z80seg_adr
       push ax                 ; seg(p)  p:DPBPtr
       mov  ax,offset dpb0+ZBIOSDISPL
       push ax                 ; offs(p)
       mov  ax,seg cpm_drive
       push ax                 ; seg (q) q:PDPPtr;
       mov  ax,offset cpm_drive
       push ax                 ; offs (q)
       mov  cx,HIGH_WinSave
       push cx                 ; high(WinSave)
       push ss                 ; seg(WinSave)
       push bx                 ; offs(WinSave)
       mov  ax,seg good_dpb_edit
       push ax                 ; seg(ok)
       mov  ax,offset good_dpb_edit
       push ax                 ; offs(ok)
       mov  ax,DATA
       mov  ds,ax
       call EmuMenu_EditDPB
       add  sp,HIGH_WinSave
       POPR <es,ds,bp,si,di,dx,cx,bx>
       ret
edit_disk_pars endp

;PROCEDURE InputString(VAR WinSave,s,p:ARRAY OF CHAR);

input_string proc   ; ds:si = prompt, bx = len prompt
                    ; ds:di = string  cx = len string
LOCAL  prompt_str,lenpr,in_str,lenstr:WORD = AUTO_SIZE
       push bp
       mov  bp,sp
       sub  sp,AUTO_SIZE
       mov  prompt_str,si
       mov  in_str,di
       mov lenpr,bx
       mov lenstr,cx
HIGH_WinSave=800

IF HIGH_WinSave gt 2*STACK_SIZE/3
   %out WARNING: POPUP WINDOW REQUIRES MUCH OF STACK
ENDIF
       PUSHR <bx,cx,dx,di,si,bp,ds,es>
       sub  sp,HIGH_WinSave
       mov  bx,sp
       mov  ax,z80seg_adr

       mov  cx,HIGH_WinSave
       push cx                 ; high(WinSave)
       push ss                 ; seg(WinSave)
       push bx                 ; offs(WinSave)

       push lenstr             ; high(s)
       push ds                 ; seg(s)
       push in_str             ; offs(s)
       push lenpr              ; high(p)
       push ds                 ; seg(p)
       push prompt_str         ; offs(p)
       mov  ax,DATA
       mov  ds,ax
       call EmuMenu_InputString
       add  sp,HIGH_WinSave
       POPR <es,ds,bp,si,di,dx,cx,bx>
       mov  sp,bp
       pop  bp
       ret
input_string endp


load_disk_pars proc
LOCAL  handle:WORD = AUTO_SIZE
       push bp
       mov  bp,sp
       sub  sp,AUTO_SIZE
       call ins_dos_disk
       call @@fopen
       jnc  short @@ok
       cmp  ax,2
       CALLZ disp_f_not_found
       jc  short @@exit           ; nicht da
@@ok:
       call @@fread
       call @@fclose
       mov  autologin_flag,FALSE
@@exit:
       call ins_cpm_disk
       mov  sp,bp
       pop  bp
       ret

@@fopen:
       mov al,0
       mov dx,offset fmt_file_name
       DOS OPEN_FILE
       mov handle,ax
       ret

@@fclose:
       mov bx,handle
       DOS CLOSE_FILE
       CALLC disp_disk_err
       ret

@@fread:
       mov dx,offset tmp_word
       mov bx,handle
       mov cx,2
       DOS READ_FROM_HANDLE
       CALLC disp_disk_err
       mov bx,dx
       mov bx,[bx]
       cmp bx,'WJ'
       jnz  short @@ldexit         ; falscher Kenncode <> 'JW'

       ; cp/m Parameter laden

       mov dx,offset offset dpb0+ZBIOSDISPL
       mov bx,handle
       mov cx,size dpb
       push ds
       mov ax,z80seg_adr
       mov ds,ax
       DOS READ_FROM_HANDLE
       pop ds
       CALLC disp_disk_err

       ; physikalische Parameter laden

       mov dx,offset cpm_drive
       mov bx,handle
       mov cx,autologin_flag-cpm_drive
       DOS READ_FROM_HANDLE
       CALLC disp_disk_err

@@ldexit:
     ret
load_disk_pars endp

save_disk_pars proc
LOCAL  handle:WORD = AUTO_SIZE
       push bp
       mov  bp,sp
       sub  sp,AUTO_SIZE
       call ins_dos_disk
       cmp  ax,MESG_ESC
       jz   short @@exit
       call @@fopen
            jc short @@exit
       call @@fwrite
       call @@fclose
       call ins_cpm_disk
@@exit:
       mov  sp,bp
       pop  bp
       ret


@@fopen:
       mov cx,0
       mov dx,offset fmt_file_name
       DOS CREATE_FILE
       CALLC disp_disk_err
       mov handle,ax
       ret

@@fclose:
       mov bx,handle
       DOS CLOSE_FILE
       CALLC disp_disk_err
       ret

@@fwrite:

; erst Kennung schreiben

       mov bx,offset tmp_word
       mov [bx],'WJ'
       mov dx,bx
       mov bx,handle
       mov cx,2
       DOS WRITE_TO_HANDLE
       CALLC disp_disk_ful

      ; cp/m Parameter schreiben

       mov dx,offset offset dpb0+ZBIOSDISPL
       mov bx,handle
       mov cx,size dpb
       push ds
       mov ax,z80seg_adr
       mov ds,ax
       DOS WRITE_TO_HANDLE
       pop ds
       CALLC disp_disk_err

       ; physikalische Parameter schreiben

       mov dx,offset cpm_drive
       mov bx,handle
       mov cx,autologin_flag-cpm_drive
       DOS WRITE_TO_HANDLE
       CALLC disp_disk_err

     ret
save_disk_pars endp

about_author proc
       PUSHR <bx,cx,dx,di,si,bp,ds,es>
       sub  sp,HIGH_WinSave
       mov  bx,sp
       mov  cx,HIGH_WinSave
       push cx                 ; high(WinSave)
       push ss                 ; seg(WinSave)
       push bx                 ; offs(WinSave)
       mov  ax,DATA
       mov  ds,ax
       call EmuMenu_About
       add  sp,HIGH_WinSave
       POPR <es,ds,bp,si,di,dx,cx,bx>
       ret
about_author endp

clearz80 proc    ; loesche z80mem
;
; input: ax=seg z80 segment
; alle  register ok
;
       push es
       PUSHR <ax,cx,di>
       mov es,ax
       mov cx,8000h                      ; 32k words
       mov di,0
       mov ax,0
       rep stosw                         ; es:di := 0000h
       POPR <di,cx,ax>
       pop es
       ret
clearz80 endp

dos_exec proc
       mov   al,DOS_SCR_PAGE
       call  sel_scr_page
       mov bx,STDOUT
       mov cx,exit_str_end-exit_str
       mov dx,offset exit_str
       DOS WRITE_TO_HANDLE
       PUSHR <bx,cx,dx,di,si,bp,es>
       mov   sp_save,sp
       mov   ss_save,ss
       mov   bx,seg    exec_par_block
       mov   es,bx
       mov   bx,offset exec_par_block
       mov   dx,offset exec_fname
       mov   ax,seg    exec_fname
       mov   ds,ax
       mov   al,EMU_SCR_PAGE
       DOS   EXEC                 ; ax:=error code
       mov   bx,emudata_seg
       mov   ds,bx
       cli
       mov   ss,ss_save
       mov   sp,sp_save
       sti
       POPR  <es,bp,si,di,dx,cx,bx>
       cmp   ax,2
       jnz   short @@cc_found
       mov bx,STDERR
       mov cx,no_cc_str_end-no_cc_str
       mov dx,offset no_cc_str
       DOS WRITE_TO_HANDLE
@@press_esc:
       call conin
       cmp  al,ESC_KEY
       jnz  short @@press_esc
@@cc_found:
       mov   al,0
       call  sel_scr_page
       ret
dos_exec endp


dos_shell proc
LOCAL  handle:WORD,mem_adr:WORD = AUTO_SIZE
       PUSHR <ax,bx,cx,dx,si,di>
       push bp
       mov  bp,sp
       sub  sp,AUTO_SIZE

       call ins_dos_disk
       cmp  ax,MESG_ESC
       jz   short @@exit          ; ESC

       call @@fcreate
            jc short @@exit
       call @@fwrite
       call @@fclose
       call @@set_free
       call dos_exec
       call @@get_mem
       call @@fopen
       call @@fread
       call @@fclose
       mov  dx,offset temp_fname
       DOS  DELETE_FILE
       CALLC disp_disk_err
       call ins_cpm_disk
@@exit:
       mov  sp,bp
       pop  bp
       POPR <di,si,dx,cx,bx,ax>
       ret

@@set_free:
       push es
       mov  es,psp_adr
       mov  bx,prog_len
       sub  bx,1000h
       DOS  MODIFY_MEMORY
       mov  bx,mem_end
       sub  bx,1000h
       mov  ovl_base,bx
       pop  es
       ret

@@get_mem:
       push es
       mov  es,psp_adr
       mov  bx,prog_len
       DOS  MODIFY_MEMORY
       pop  es
       ret

@@fcreate:
       mov cx,2 ; Hidden
       mov dx,offset temp_fname
       DOS CREATE_FILE
       CALLC disp_disk_err        ; Abbruch nach Error in toplevel
       mov handle,ax
       ret

@@fopen:
       mov al,0
       mov  dx,offset temp_fname
       DOS OPEN_FILE
       CALLC disp_disk_err
       mov handle,ax
       ret

@@fclose:
       mov bx,handle
       DOS CLOSE_FILE
       CALLC disp_disk_err
       ret

@@fwrite:
       push ds
       mov  bx,handle
       mov  cx,0ffffh
       mov  ds,ovl_base
       mov  dx,0
       DOS WRITE_TO_HANDLE
       CALLC disp_disk_ful
       pop  ds
      ret
; end fwrite
@@fread:
       push ds
       mov  bx,handle
       mov  cx,0ffffh
       mov  ds,ovl_base
       mov  dx,0
       DOS READ_FROM_HANDLE
       CALLC disp_disk_err
       pop  ds
      ret
; end fread
dos_shell endp


init_ramdisc proc
       push es
       PUSHR <ax,cx>
       mov ax,ramdisklen          ; in paragraphen
       LOG2(64)                   ; nach cl (1024/16)
       shr ax,cl                  ; ax:=ramdisk len in KB
       and ax,0fff0h or 1100b     ; um Vielfache von 4K zu bekommen

       push ax
       LOG2(4)
       shr ax,cl                  ; ax/=4
       mov lasttrack_rd,al        ; Tracks zu 4 K
       pop ax
                                  ; dsm=ramdisklen in K/2 -1 , da bls=2k
       shr ax,1                   ; geht klar, da sowieso vielfaches von 4
       dec ax
       cmp ax,255
       jna short @@nobig
       mov ax,255                 ; mehr wie 510k geht im PC sowieso kaum
   @@nobig:
       mov dpb1.dsm,ax            ; blockzahl
       inc ax                     ; da KB=(dsm+1)*2
       shl ax,1
       mov ramdisklen,ax          ; KBzahl
       mov dpb1.bsh,4
       mov dpb1.blm,15
       mov dpb1.exm,1             ; blocksize=2048
       mov dpb1.drm,127           ; dir entries
       mov dpb1.al0,11000000b

                                  ; loesche ramdisc
       mov ax,rdseg_adr
       mov es,ax
       mov ax,ramdisklen
@@clrloop:
       cmp ax,64 ; k
       jb  short @@less64
       push ax
       mov cx,8000h               ; do 32k words
       mov di,0
       mov ax,0e5e5h
       rep stosw                  ; es:di := E5E5
       mov ax,es
       add ax,1000h
       mov es,ax
       pop ax
       sub ax,64
       jmp @@clrloop
@@less64:
       LOG2(512)
       shl ax,cl                  ; ax*=1024/2 (word)
       mov cx,ax
       mov di,0
       mov ax,0e5e5h
       rep stosw                  ; es:di := E5E5
       POPR <cx,ax>
       pop es
       call load_ramdisc
      ret
init_ramdisc endp

; speichere Ramdisk auf Platte
;
; der LOCAL Befehl legt lokale Auto-Vars auf dem Stack an mit
; Laenge AUTO_SIZE
;
save_ramdisc proc
LOCAL  handle:WORD = AUTO_SIZE

       push bp
       mov  bp,sp
       sub  sp,AUTO_SIZE
       call ins_dos_disk
       cmp  ax,MESG_ESC
       jz   short @@exit
       call @@fopen
            jc short @@exit
       call @@fwrite
       call @@fclose
       call ins_cpm_disk
@@exit:
       mov  sp,bp
       pop  bp
       ret


@@fopen:
       mov cx,0
       mov dx,offset ramdisc_fname
       DOS CREATE_FILE
       CALLC disp_disk_err
       mov handle,ax
       ret

@@fclose:
       mov bx,handle
       DOS CLOSE_FILE
       CALLC disp_disk_err
       ret

@@fwrite:
       ; erst mal Ramdiskgroesse schreiben, beim Laden muss sie stimmen

       mov bx,offset secbuf
       mov ax,ramdisklen
       mov [bx],ax
       mov dx,bx
       mov bx,handle
       mov cx,2
       DOS WRITE_TO_HANDLE
       CALLC disp_disk_ful

       mov bx,rdseg_adr
       mov ax,ramdisklen
@@wrtloop:
       cmp  ax,0                  ; fertig ?
                                  ; 0 oder negativ = Abbruch
       jle short @@wrtexit
       push ax
       cmp ax,32                  ; kilobyte
       mov  cx,8000h ; 32k
       ja  short @@more32
       LOG2(1024)                 ; nach cl
       shl ax,cl                  ; ax*=1024
       mov cx,ax                  ; cx = Laenge in Byte
@@more32:
       push bx
         push ds
         push bx
         pop  ds
         mov  bx,handle           ; klappt, da ueber SS:BP
         mov  dx,0 ; offset
         DOS WRITE_TO_HANDLE
         CALLC disp_disk_ful
         pop  ds
       pop bx
       pop ax
       add bx,800h                ; segment um 32k weitersetzen
       sub ax,32                  ; restlaenge -=32
      jmp @@wrtloop
@@wrtexit:
      ret
; end fwrite
save_ramdisc endp


; hole Ramdisk von Platte
;
; der LOCAL Befehl legt lokale Auto-Vars auf dem Stack an mit
; Laenge AUTO_SIZE
;
load_ramdisc proc
LOCAL  handle:WORD = AUTO_SIZE

       push bp
       mov  bp,sp
       sub  sp,AUTO_SIZE
       call @@fopen
       jc   short @@exit           ; nicht da

       call @@fread
       call @@fclose
@@exit:
       mov  sp,bp
       pop  bp
       ret


@@fopen:
       mov al,0
       mov dx,offset ramdisc_fname
       DOS OPEN_FILE
       mov handle,ax
       ret

@@fclose:
       mov bx,handle
       DOS CLOSE_FILE
       CALLC disp_disk_err
       ret

@@fread:
       mov dx,offset secbuf
       mov bx,handle
       mov cx,2
       DOS READ_FROM_HANDLE
       CALLC disp_disk_err
       mov bx,dx
       mov bx,[bx]
       cmp bx,ramdisklen
       ja  short @@rdexit         ; ueberhaupt zu gross

       mov ax,bx                  ; Laenge Ramdisk auf Platte
       mov bx,rdseg_adr
@@rdloop:
       cmp  ax,0                  ; fertig ?
       jle short @@rdexit
       push ax
       cmp ax,32
       mov  cx,8000h              ; 32k
       ja  short @@more32
       LOG2(1024)                 ; nach cl
       shl ax,cl                  ; ax*=1024
       mov cx,ax                  ; cx = Laenge in Byte
@@more32:
       push bx
         push ds
         push bx
         pop  ds
         mov  bx,handle           ; klappt, da ueber SS:BP
         mov  dx,0                ; offset
         DOS READ_FROM_HANDLE
         CALLC disp_disk_err
         pop  ds
       pop bx
       pop ax
       add bx,800h                ; segment um 32k weitersetzen
       sub ax,32                  ; restlaenge -=32
      jmp short @@rdloop
@@rdexit:
      ret
; end fread
load_ramdisc endp

bios88 proc
;
; bios88 ist handler fuer alle bios calls
; In:  al=bios nummer (warm boot = 1)
;      cx,bx,dx wie bei cp/m bios calls
; Out: di,ah zerstoert
;         si kann veraendert sein, falls bios change_si_flag
;            hat nach wboot
;
; waehrend aller bios routinen zeigt ds auf emudata_seg
;                                    es     z80cpu_seg
       mov di,emudata_seg
       mov ds,di

       mov z80_pc,si

       cmp breakflag,TRUE

       CALLZ prg_exit

       PUSHR <bp,dx>
; cx nicht pushen, da cl:=drive # bei wboot

       xor ah,ah                  ; bios # = index
       sal ax,1
       mov di,ax
         call cs:biostab[di]
       mov     si,z80_pc

       POPR  <dx,bp>

       cmp breakflag,TRUE

       CALLZ  prg_exit

       mov di,z80seg_adr
       mov ds,di
       mov es,di
       jmp bios88ret

bios88 endp


biostab label word
        dw boot                   ; cold start
        dw wboot                  ; warm start
        dw constat                ; console status
        dw conin                  ; console character in
        dw conout                 ; console character out
        dw list                   ; list character out
        dw punch                  ; punch character out
        dw reader                 ; reader character out
        dw home                   ; move head to home position
        dw seldsk                 ; select disk
        dw settrk                 ; set track number
        dw setsec                 ; set sector number
        dw setdma                 ; set dma address
        dw read                   ; read disk
        dw write                  ; write disk
        dw listst                 ; return list status
        dw sectran                ; sector translate
        rept 25-16-1
          dw wboot
        endm
        dw cpm3_move              ; move
        dw cpm_get_time           ; time


;
;
;  individuelle UPs um jede Funktion auszufuehren
;
boot proc

;simplest case is to just perform parameter initialization
       mov in_boot_flag,TRUE
       mov bx,offset on_sign_string
       call puts

       call instzbios

       cmp  fmt_file_flg,TRUE
       CALLZ load_disk_pars


       call read_sys  ; lade bdos aus file CPMSYS.CPM
       pushf
       call ins_cpm_disk
       popf
       jnc  short @@goodload ; laden aus File klappte
       call get_sys   ; BDOS mit BIOS fkt von Diskette laden
@@goodload:

; Copyright von dr ausgeben

       mov cl,27
       call conout
       mov cl,'k'                 ; pop cursor pos
       call conout
       mov cl,27
       call conout
       mov cl,'J'                 ; Rest vom Fenster loeschen
       call conout

       mov bx,offset cpm_bdos_buf+18h
       call puts

       mov bx,offset cr_lf_txt
       call puts
;
; berechne adresse von ccp,bdos und bios
; dabei wird die JMP CCPENTRY Anweisung bei CCP+0 benutzt
;
       mov di,offset cpm_bdos_buf+807h
       mov al,[di]
       cmp al,11h
       jnz short @@no_standard
       mov di,offset cpm_bdos_buf+1
       mov ax,[di]
       sub ax,35ch                ; ax:=start ccp
; CP/M CCP? dann al jetzt 0
       or al,al
       jz short @@is_standard
; sonst defaultmaessig e400 annehmen
@@no_standard:
       mov ax,0e400h
@@is_standard:
       mov ccp_adr,ax
       add  ax,806h
       mov bdos_adr,ax
       add  ax,(BDOSLEN-806h)
       mov bios_adr,ax

       call reset_conout

       mov di,IOBYTE
       mov byte ptr es:[di],0     ; clear IOBYTE
       mov di,CDISK
       mov byte ptr es:[di],0     ; select disk zero
       call reset_punch_buf
       call reset_reader_buf
       mov in_boot_flag,FALSE
       jmp  wboot
boot endp

;
; setze Punch buffer auf leer
;

reset_punch_buf proc
       mov punch_buf_entries,0
       mov punch_buf_ptr,offset punch_buf
       ret
reset_punch_buf endp


;
; setze Reader buffer auf nicht belegt
;

reset_reader_buf proc
       mov reader_empty_flag,FALSE     ; noch nicht lesen versucht
       mov reader_buf_entries,0
       mov reader_buf_ptr,offset reader_buf
       mov reader_file_pointer,0
       mov reader_file_pointer+2,0
       ret
reset_reader_buf endp

;
; Stringausgabe
; In: bx zeigt auf String, Stringende = \0
;
puts proc
@@loop:
       mov cl,[bx]
       or  cl,cl
       jz  short @@exit
       inc bx
       push bx
       call conout
       pop  bx
       jmp short @@loop
@@exit:ret
puts endp

instzbios proc
;
; installiere z80 bios
; und discparameter
;

       PUSHR <si,di,cx>

         mov  si,offset zbiosbeg
         mov  di,ZBIOS
         mov  cx,zbiosend-zbiosbeg
         cld
         rep movsb                ; es:di := ds:si

       POPR <cx,di,si>
       ret
instzbios endp


instjp proc
;
; installiere z80 bios Sprungleiste bei bios_adr
;
; register o.k.
;
; darf nicht aufgerufen werden, bevor bios_adr
; von boot aufgesetzt wurde
;

       PUSHR <si,di,cx>

         mov  si,offset wboote-3
         mov  di,bios_adr
         mov  cx,zvecend-wboote+3
         cld
         rep movsb                ; es:di := ds:si

       POPR <cx,di,si>

       ret
instjp endp


read_sys  proc
;
; lade cp/m bdos aus file CPMSYS.CPM in buffer
; um es durch warm boot zu benutzen
;
; das File entspricht dem durch MOVCPM erzeugten Speicherauszug
; dabei beginnt das eingentlich System erst ab Offset 880h

MOVCPM_OFFS = 880H

     PUSHR <ax,bx,cx,dx,si>
       mov al,0
       mov dx,offset cpmsys_fileName
       DOS OPEN_FILE
       jc  short @@exit
       mov si,ax
       ; setze Filepointer auf CCP Anfang im File
       mov cx,0                   ; cx:dx: offset
       mov dx,MOVCPM_OFFS
       mov al,0                   ; offset from start
       mov bx,si
       DOS MOVE_FILE_POINTER
;
; emudata_seg:buffer := CPMSYS.CPM
;
       mov cx,BDOSLEN             ; Laenge
       mov bx,si                  ; handle
       mov dx,offset cpm_bdos_buf
       DOS READ_FROM_HANDLE
       jc  short @@exit
       mov bx,si
       DOS CLOSE_FILE
       CALLC disp_disk_err
@@exit:
       POPR <si,dx,cx,bx,ax>
       ret
read_sys  endp


; System von SystemSpuren laden

; while sector in systemsectoren do
;    lies sector
;    if an Adresse     String COPYRIGHT 1979 ..
;       sys=naechste SYS_SECS sektoren
;       lies sys
;       exit
;       mache Checksumme ueber sys
;       if falsch
;          "no sys"
;       endif
;    endif
; endwhile
; "no sys"

get_sys proc
LOCAL  sys_recs,last_sys_rec:WORD = AUTO_SIZE

       push bp
       mov  bp,sp
       sub  sp,AUTO_SIZE
@@get_sys_loop:
       call login_disc
       mov  ah,0
       mov  al,dpb0.off-dpb0.spt  ; off
       call get_dpb_entrie        ; nach bx
       push bx
       mov  ah,0
       mov  al,dpb0.spt-dpb0.spt  ; spt
       call get_dpb_entrie        ; nach bx
       pop  ax
       mul  bx                    ; ax:=system records
       mov  sys_recs,ax
       mov  cx,0
; im Systembereich nach String "COPYRIGHT (C) 1979 " suchen
@@srch_loop:
       cmp  cx,sys_recs
       jnc  short @@read_err      ; schon ausserhalb sys
       push cx  ; sector
       push cx
       mov  cx,DEFAULT_DMA
       call setdma
       pop  cx
       mov  ah,cpm_drive
       call set_abs_sect
       call read
       pop  cx
       cmp  al,1
       jz   short @@read_err
       call @@compare
       jz   short @@found
       inc  cx
       jnz  @@srch_loop
@@found:
       mov  bx,DEFAULT_DMA
       push cx
       add  cx,SYS_SECS
       mov  last_sys_rec,cx
       pop  cx
@@loop:
       push cx  ; sector
       push bx  ; dma
       push cx
       mov  cx,bx
       call setdma
       pop  cx
       mov  ah,cpm_drive
       call set_abs_sect
       call read
       pop  bx
       pop  cx
       cmp  al,1
       jz   short @@read_err
       add  bx,RECORD_LEN
       inc  cx
       cmp  cx,last_sys_rec
       jnz  @@loop

       jmp  short @@good_load

@@read_err:
       mov  bx,offset no_sys
       call puts
       call conin
       jmp  @@get_sys_loop

@@good_load:

; System aus CP/M Segment nach Datensegment uebertragen

       PUSHR <si,di,bx,cx,ax>
       push ds
       push es
         mov di,offset cpm_bdos_buf
         mov si,DEFAULT_DMA
         mov ax,z80seg_adr
         mov ds,ax
         mov ax,emudata_seg
         mov es,ax
         mov cx,BDOSLEN/2
         cld
         rep movsw                ; es:di := ds:si
       pop es
       pop ds
       POPR <ax,cx,bx,di,si>
       mov  sp,bp
       pop  bp
       ret
@@compare:
       PUSHR <si,di,cx,ax>
       mov  si,offset bdos_str
       mov  di,DEFAULT_DMA+10H
       mov  cx,bdos_str_end-bdos_str
@@cp_loop:
       mov al,[es:di]
       mov ah,[si]
       inc si
       inc di
       cmp al,ah
       jnz  short @@cp_exit
       loop @@cp_loop
@@cp_exit:
       POPR <ax,cx,di,si>
       ret

get_sys endp
;

set_abs_sect proc    ; absoluten Sektor in cx, drive ah setzen
       push cx
       mov  al,0                  ; spt
       call get_dpb_entrie        ; nach bx
       pop  ax
       mov  dx,0
       div  bx
       push dx
       mov  cx,ax
       call settrk
       pop  cx
       call setsec
       ret
set_abs_sect endp


wboot proc

     push   di
       call   copy_bdos
       call   instjp              ; bios Sprungleiste installieren
;
; 0000h := JP WBOOT
; 0005h := JP BDOS
;
       mov     di,0               ; start cp/m memory
       mov     byte ptr es:[di+0],0c3h
       mov     bx,bios_adr
       add     bx,3               ; bx:=warmboot
       mov     es:[di+1],bx       ; set address field for jmp at 0
;
       mov     byte ptr es:[di+5],0c3h
       mov     bx,bdos_adr        ; bdos entry point
       mov     es:[di+6],bx       ; address field of jump at 5 to bdos 
;
       mov     cx,DEFAULT_DMA     ; default dma address is 80h
       call   setdma

       call login_disc ; physikalisches Login
;
       mov     ax,ccp_adr
       mov     z80_pc,ax          ; jp ccp

       mov     di,CDISK
       mov     cl,es:[di]         ; get current disk number & send to ccp 
     pop di
     ret                          ;go to cp/m for further processing

copy_bdos:
       PUSHR <si,di,cx>

         mov di,ccp_adr
         mov si,offset cpm_bdos_buf
         mov cx,BDOSLEN/2
         rep movsw                ; es:di := ds:si

       POPR <cx,di,si>
       ret

wboot  endp
;

;
;
constat proc
;
; console status, return 0ffh if character ready, 00h,Z_flag if not
;
; Out: al=keyboard status
;
        push bx
        mov ah,1                  ; keybord status
        int 16h
        mov al,0ffh
        jnz short @@exit
        mov al,0
@@exit:
        pop bx
        ret
constat endp

; conin im Modul CONOUT

;
; test auf ^C, wenn ja, warm boot
;
tst_ctrl_c proc
       push ax
       call constat
       jz   short @@exit
       call conin
       cmp  al,3                  ; ^C
       clc                        ; cy = no error
       jnz  short @@exit
       mov  bx,offset control_c_txt
       call puts
       stc
@@exit:
       pop ax
       ret
tst_ctrl_c endp
;
; list character in register c auf Drucker
;
list proc
       call listst
       jnz  short @@ok
       call tst_ctrl_c
       jnc  short list
       mov  z80_pc,0              ; Z80 macht ab Adresse 0 weiter
       ret
@@ok:
       mov al,cl                  ; char nach register a
       xor dx,dx                  ; lpt 0
       xor ah,ah                  ; list character
       push bp
       int 17h
       pop bp
       cmp ah,10h                 ; ok
       jnz short list
       ret
list endp
;
; return list status (0,Z_flag if not ready, 1 if ready)
;
listst proc
       push dx
       xor dx,dx                  ; lpt 0
       mov ah,2                   ; test printer
       int 17h
       mov al,1
       cmp ah,90h                 ; on line und not busy
       jz short @@lret
       dec al
@@lret:
       or  al,al
       pop dx
       ret
listst endp

date_dos_2_cpm proc  ; out: AX=tage seit 1.Jan. 1978
    push bx
    push cx
    push dx
    DOS GET_DATE
    push dx
    mov bx,1978
    inc cx      ; ein Jahr zuviel, fr Test ob akt. Schaltjahr
    MOV AX,0
@@add_loop:
    cmp  bx,cx  ; aktuelles Jahr erreicht ?
    jz short @@years_done

    mov  dx,bx
    and  dx,11b ; 0 => durch 4 teilbar
    sub  dx,1   ; 0 => cy
    mov  dx,365
    adc  dx,0   ; wird unten zum abziehen gebraucht
    add  ax,dx  ; 0 => 366

    inc  bx
    jmp  @@add_loop
@@years_done:
    sub ax,dx  ; da ein Jahr oben zuviel, 365 oder 366 wieder weg
    pop dx   ; mon/tag
    mov ch,0
    mov cl,dl
    add ax,cx ; tag
    mov cl,dh ; monat
    dec cl    ; da aktueller Monat schon dran
    jz  short @@is_jan
    mov bx,offset month_tab
@@add_months:
    mov dl,[bx]    ; verschieden lange monate
    inc bx
    mov dh,0
    add ax,dx
    loop @@add_months
@@is_jan:
    pop dx
    pop cx
    pop bx
    ret
date_dos_2_cpm endp

cpm3_move proc   ; move wie bei cpm plus bios
    push ds
    push es
    pop  ds
    mov  si,bx
    mov  di,dx
    add  bx,cx      ; hl=blockende
    cmp  bx,dx      ; gr”áer Ziel ?
    jnb   short @@topdown
    cld
    rep movsb
    jmp short @@exit
@@topdown:
    add si,cx
    add di,cx
    mov bx,si
    mov dx,di
    dec si
    dec di
    std
    rep movsb
    pop ds
    ret
@@exit:
    mov bx,si
    mov dx,di
    pop ds
   ret
cpm3_move endp


cpm_get_time proc   ; dos zeit nach cpm plus zeit
    PUSHR <AX,CX,DX>
    cmp cl,0
    jnz short @@exit ; set time ignorieren
    call date_dos_2_cpm
    push ds
    push es
    pop  ds
    mov @date+ZBIOSDISPL,ax
    DOS GET_TIME
    mov dl,10
    mov al,cl
    mov ah,0         ; sonst divide overflow
    div dl
    mov cl,4
    shl al,cl
    or  al,ah
    mov @min+ZBIOSDISPL,al
    mov al,ch
    mov ah,0
    div dl
    shl al,cl
    or  al,ah
    mov @hour+ZBIOSDISPL,al
    mov al,dh
    mov ah,0
    div dl
    shl al,cl
    or  al,ah
    mov @sec+ZBIOSDISPL,al
    pop ds
    mov bx,(offset @date)+ZBIOSDISPL
@@exit:
    POPR <DX,CX,AX>
    ret
cpm_get_time endp
        
del_punch_file proc
       mov dx,offset punch_fileName
       DOS DELETE_FILE
       ret
del_punch_file endp

;
; punch character in register c
; wird durch Output in File auf MsDos Disk ersetzt
; Buffer wird auf File geschrieben, falls er voll ist
; und  auch am Ende des Emulator-
; laufs
;
punch proc
;
; fill buffer until full or eof
; open file
; if not exist file
;    create file
; endif
; go to end of file
; append character
; close file
; empty buffer
;
; falls punch von bios88 aufgerufen wurde, ist al<>0
; nach Aufruf von wrt_out_punch aber al=0

     PUSHR <ax,bx,cx,dx,si>
       cmp al,0
       jz  short @@write_out
       mov bx,punch_buf_ptr
       mov [bx],cl                ; buf:=character
       inc bx
       mov punch_buf_ptr,bx
       inc punch_buf_entries
       cmp punch_buf_entries,PUNCH_BUF_SIZE
       jnz short @@exit
@@write_out:
       call ins_dos_disk
       cmp  ax,MESG_ESC
       jz   short @@exit
       mov al,1
       mov dx,offset punch_fileName
       DOS OPEN_FILE
       jnc short @@file_found
           mov cl,0 ; write only
           DOS CREATE_FILE
           CALLC disp_disk_err
@@file_found:

       mov bx,ax                  ; handle
       mov cx,0
       mov dx,0
       mov al,2                   ; offset from end
       DOS MOVE_FILE_POINTER

       mov cx,punch_buf_entries
       mov dx,offset punch_buf
       DOS WRITE_TO_HANDLE
       CALLC disp_disk_ful

       DOS CLOSE_FILE

       call reset_punch_buf
       call ins_cpm_disk
@@exit:
       POPR <si,dx,cx,bx,ax>
       ret
punch  endp

wrt_out_punch proc
       cmp punch_buf_entries,0
       jz  short @@exit
       xor  al,al
       call punch
@@exit:
       ret
wrt_out_punch endp
;
;
reader proc       ;read character into register a from reader device

; if buffer empty
;    open file
;    if file not present
;       exit
;    else
;       fill buffer
;    endif
; endif
; read char

       cmp reader_empty_flag,TRUE
       jz  short @@empty
       cmp reader_buf_entries,0
       jnz short @@do_read

; Buffer fuellen
         mov al,0
         mov dx,offset reader_fileName
         DOS OPEN_FILE
         jc  short @@empty
         push ax
         mov bx,ax
         mov dx,reader_file_pointer
         mov cx,reader_file_pointer+2
         PUSHR <dx,cx>
          add dx,READER_BUF_SIZE
          adc cx,0
          mov reader_file_pointer,dx
          mov reader_file_pointer+2,cx
         POPR  <cx,dx>
         mov al,0                   ; offset from start
         DOS MOVE_FILE_POINTER
         pop bx                     ; handle
         mov cx,READER_BUF_SIZE
         mov dx,offset reader_buf
         DOS READ_FROM_HANDLE
         push ax
         DOS CLOSE_FILE
         pop ax
         cmp ax,0
         jz  short @@empty

 ; buffer erfolgreich gefuellt
         mov reader_empty_flag,FALSE
         mov reader_buf_entries,ax
         mov reader_buf_ptr,offset reader_buf

@@do_read:
         mov bx,reader_buf_ptr
         mov al,[bx]
         inc bx
         mov reader_buf_ptr,bx
         dec reader_buf_entries
         jmp short @@exit
@@empty:
       mov reader_empty_flag,TRUE
       mov al,CPM_EOF
       mov bx,0ffffh       ; hl=FFFF  == Dos File Ende
       ret
@@exit:
       mov bx,0            ; a=hl=gelesenes byte
       mov bl,al
       ret

reader endp


; In:  ah=drive
;      al=offset im dpb
; Out: bx=word dpb[al]

get_dpb_entrie proc
       push si
       mov  bl,ah
       call get_dph_adr           ; bx:=dph
       xor  ah,ah
       mov  si,ax
       mov  bx,[es:bx+10]         ; bx:=dpb
       mov  bx,[es:bx+si]         ; bx:=dpb[ax]
       pop  si
       ret
get_dpb_entrie endp

;
; select disk in register c
;
seldsk proc
       cmp cl,NDISKS-1
       mov bx,0000h               ; 0 = Drive nicht existent
       ja  short @@exit
       mov diskno,cl
       mov bl,cl
       call get_dph_adr
@@exit:
       ret
seldsk endp

;  In:  bl:=Drive #
;  Out: bx:= disk parameter header address
;
get_dph_adr proc
       mov bh,0                   ; high order zero
       push cx
       LOG2(16)                   ; nach cl
       sal bx,cl                  ; bx*=16 (size of each header)
       pop cx
       add bx,offset dph_base+ZBIOSDISPL
       ret                        ;hl=.dph_base(diskno*16)
get_dph_adr endp

;
;  move to the track 00 position of current drive
;  translate this call into a settrk call with parameter 00
;
home proc
       mov cx,0                   ; select track 0
       call settrk
       mov  home_flag,TRUE        ; zur Auswertung durch read_track
       ret
home endp

;
; set track given by register bc
;
settrk proc
       mov word ptr track,cx
       ret
settrk endp
;
; set sector given by register bc
;
setsec proc
       mov word ptr sector,cx
       ret
setsec endp

;
;translate the sector given by bc using the
;translate table given by de
;
sectran proc

       mov  bx,cx
       or   dx,dx                 ; table ptr == 0 ?
       jz   short @@exit
       add  cx,cx                 ; index *= 2
       mov  bx,dx                 ; hl=trans
       add  bx,cx
       mov  bx,[es:bx]            ;hl=trans[sector]
@@exit:
       ret                        ;with value in hl
sectran endp
;
; set dma address given by registers b und c
;
setdma proc
       mov dmaad,cx
       ret
setdma endp


calcadr proc
;
; ax:bx := record adresse in Ramdisk
; illegal sec: cy
;
; 1. 64k track  0..15
; 2. 64k track 16..31
; ..
       mov al,track
       cmp al,lasttrack_rd
       ja short @@badrec

       mov al,sector
       cmp al,LAST_SEC1
       ja short @@badrec

; phys recordnum = track * spt + sector

       PUSHR <cx,dx>
       mov  ah,RMD
       mov  al,0                  ; spt
       call get_dpb_entrie
       mov  cx,bx                 ; cx:=spt B:
       mov  ax,word ptr track
       mul  cx
       add  ax,word ptr sector    ; ax:=phys record num
       mov  cx,512                ; 64K = 512 records @ 128 Bytes
       mov  dx,0
       div  cx                    ; ax:= # of 64K, dx:=recordnum
       mov  cx,1000h
       push dx
       mul  cx
       pop  dx
       mov  bx,rdseg_adr
       add  bx,ax
       mov  ax,dx
       LOG2(RECORD_LEN)
       shl  ax,cl
       xchg bx,ax
       POPR <dx,cx>
      clc
@@exit:
      ret
@@badrec:
      stc
      jmp short @@exit
calcadr endp

;
read proc
;
; lies einen record von 128 bytes
;
       cmp  diskno,RMD
       jnz  short @@rfdisc
       call calcadr               ; ax:bx := record addresse
       jc   short @@badsec
       call rdrec
@@exit:xor al,al                  ; o.k.
       ret
@@badsec:
       mov al,1
       ret

@@rfdisc:
       call is_valid_rec          ; ax=rec/sec
       jc   short @@badsec

       call read_track
       jc   short @@badsec
;
; record num ist in ah
; ax:bx := adresse Track Buffer
;
       mov  al,READ_OP
       call get_adr_in_buf        ; nach bx
       mov  ax,ds                 ; ax := seg track buffer
       call rdrec
       jmp  short @@exit
read endp

; berechne Sektoradresse im Buffer
; a=sector*128+buf
; input: al=READ_OP / WRITE_OP

get_adr_in_buf proc
      PUSHR <AX,CX>
       call is_dir_track
       mov  bx,offset dirtrbuf
        jz  short @@calc
       mov  bx,offset secbuf
       cmp  al,READ_OP
        jz  short @@calc
       mov  bx,offset wrtbuf
@@calc:
       xor  ax,ax
       mov  al,sector
       LOG2(RECORD_LEN)                  ; nach cl
       shl  ax,cl                 ; ax *= 128
       add  bx,ax
      POPR <CX,AX>
      RET
get_adr_in_buf endp

is_dir_track proc
      push ax
       mov  al,track
       cmp  al,dirtrack
      pop  ax
      ret
is_dir_track endp

;
;
;
is_valid_rec proc
; Out:  cy   sector illegal
;
; test auf gueltigen track/sector auf DISC
;
COMMENT *
      mov al,track
      cmp al,LAST_TRCK0
      stc
      ja  short @@exit
      mov al,sector
      cmp al,LAST_SEC0
      stc
      ja short @@exit
; ok, clear cy
*
      clc
@@exit:
      ret
is_valid_rec endp

;
; lies ganzen Track, falls noch nicht im Speicher
;
read_track proc
;
; Error: cy=1
;
      PUSHR <ax,bx,cx,dx>

; der DirTrack wird nur gelesen nach HOME

      mov  al,track
      cmp  al,dirtrack
      jnz  short @@nodir
      cmp  home_flag,TRUE
      mov  home_flag,FALSE
      mov  bx,offset dirtrbuf
      jz  short @@read_dir_track
      clc
      jmp short @@exit
@@nodir:
       cmp  al,last_track_read
       jz   short @@exit

           mov  bx,offset secbuf
           mov  last_track_read,al
@@read_dir_track:
           mov  r_track,al

           call read_phys_track
           ; ab jetzt CY nicht mehr veraendern !

@@exit:
      POPR <dx,cx,bx,ax>
      ret
read_track endp

;
; lies ganzen Track, in den geschrieben werden soll, ein,
;  falls noch nicht im Speicher
;
read_w_track proc
;
; Error: cy=1
;
      PUSHR <ax,bx,cx,dx>
      ; der DirTrack ist sowieso im Speicher und braucht
      ; nicht gelesen werden
      mov  al,track
      cmp  al,dirtrack
      jz  short @@exit

      cmp  al,last_track_written
      jz   short @@exit

      ; falls noetig alten Track im WriteBuffer erst
      ; ausschreiben
      call write_out_old_track

      mov  al,track
      mov  bx,offset wrtbuf
      mov  last_track_written,al
      mov  r_track,al
      call read_phys_track

      ; ab jetzt CY nicht mehr veraendern !

@@exit:
      POPR <dx,cx,bx,ax>
      ret
read_w_track endp

; physikalische Sektorlaenge herstellen
dos_phys_len proc
       PUSHR <es,ax,di>
       mov  ax,0
       mov  es,ax
       les  di,[es:78h]
       mov  al,ms_phys_sec_len
       mov  es:[di+3],al
       POPR <di,ax,es>
       ret
dos_phys_len endp

; physikalische Sektorlaenge herstellen
cpm_phys_len proc
       PUSHR <es,ax,di>
       mov  ax,0
       mov  es,ax
       les  di,[es:78h]
       mov  al,cpm_phys_sec_len
       mov  es:[di+3],al
       POPR <di,ax,es>
       ret
cpm_phys_len endp

normalize_pointer proc ; es:bx
       PUSHR <cx,ax>
        mov ch,bl
        and ch,0fh
        LOG2(16)         ; NACH CL
        shr bx,cl        ; bx:=16
        mov ax,es
        add ax,bx
        mov es,ax
        mov bx,0
        mov bl,ch
       POPR <ax,cx>
       ret
normalize_pointer endp

read_phys_track proc
      PUSHR <ax,bx,cx,dx>

; der naechste Test sollte eigentlich nicht noetig sein, aber
; mein 386 Ami Bios weigert sich sonst, den gleichen Track nochmal
; zu lesen; jedenfalls kann auch gleich ein Diskwechsel festgestellt
; werden
      call tst_disk_change
      jz   short @@no_change
      mov  last_track_read,0ffh
@@no_change:

      mov  cl,retry_count
      xor  ch,ch
@@tryread:
        call cpm_phys_len
        push es
        mov  ax,emudata_seg
        mov  es,ax                ; ist sonst z80cpu_seg
        push cx

          mov  dl,cpm_drive       ; diskno
          mov  dh,side
          mov  ch,r_track
          call track_trans
          mov  cl,first_phys_sec  ; physical sector number
          mov  al,phys_sec_pt     ; lies ganzen Track auf einmal
          mov  ah,2               ; read sector
;          call normalize_pointer
          int 13h                 ; es:bx := track
        pop cx
        pop es
        call dos_phys_len
        jnc  short @@exit
        cmp  ah,9
        jz   dma_bound
; Fehler, Disk Reset und nochmal versuchen
        mov ah,0
        mov dl,cpm_drive
        int 13h                   ; disk reset
      loop  @@tryread
      stc                         ; Error, cy
@@exit:
      POPR <dx,cx,bx,ax>
      ret
read_phys_track endp

; Trackuebersetzung nur fuer CP/M 86 DS DD noetig
; muss vor phys. read/write Ops aufgerufen werden
;
; in: ch: zu uebersetzender Track
;     dh: Disk Side

track_trans proc
          cmp  ch,phys_tracks
          jb  short @@exit
          cmp  cpm86dd_flag,TRUE
          jnz short @@nocpm86dd
          push ax
          mov  al,ch
          mov  ch,79
          sub  ch,al
          pop  ax
          inc  dh                 ; Seite 1
          jmp  short @@exit
@@nocpm86dd:
          sub ch,40
          inc  dh                 ; Seite 1
@@exit:
          ret
track_trans endp

tst_disk_change proc
          cmp hd_drive_flag,FALSE
          jz  short @@exit        ; kein Diskwechsel => Z
          mov ah,16h
          mov dl,cpm_drive
          int 13h  ; Test auf Wechsel
          cmp ah,0                ; kein Diskwechsel => Z
@@exit:
          ret
tst_disk_change endp

; falls noch nicht geschriebener Track im Speicher ist,
; der kein DirTrack ist, ausschreiben

write_out_old_track proc
      cmp  write_flag,TRUE
      jnz short @@exit
      mov  al,last_track_written
      mov  w_track,al
      mov  outbuf_ptr,offset wrtbuf
        call write_phys_track
      mov  write_flag,FALSE
@@exit:
      ret
write_out_old_track endp

;
; den Dir Track auf Disk schreiben
;
write_out_dir proc
      call write_out_old_track    ; falls noetig
      mov  al,dirtrack
      mov  w_track,al
      mov  outbuf_ptr,offset dirtrbuf
          call write_phys_track
      ret
write_out_dir endp

write_phys_track proc
      PUSHR <ax,bx,cx,dx>
      mov  cl,retry_count
      xor  ch,ch
@@trywrit:
        call cpm_phys_len
        push es
        mov  ax,emudata_seg
        mov  es,ax
        push cx
          mov  dl,cpm_drive          ; diskno
          mov  dh,side
          mov  ch,w_track
          call track_trans
          mov  cl,first_phys_sec  ; physical sector number
          mov  bx,outbuf_ptr
          mov  al,phys_sec_pt
;          call normalize_pointer
          mov  ah,3               ; read sector
          int 13h                 ; track := es:bx
        pop cx
        pop es
        call dos_phys_len
        jnc  short @@exit
        cmp  ah,9
        jz   dma_bound
        mov ah,0
        mov dl,cpm_drive
        int 13h                   ; disk reset
      loop  @@trywrit
      stc                         ; Error,  cy
@@exit:
      POPR <dx,cx,bx,ax>
      ret
write_phys_track endp

;
; versuchen, richtiges physikalisches Format zu finden
; und disc parameter block herrichten
;
; falls autologin_flag = FALSE , kann das Zeitaufwendige login
; entfallen

login_disc proc
      push bx
      call tst_hd_drive
      mov last_track_read,0ffh    ; Track Buffer auf leer setzen
                                  ; damit bei read_track gelesen wird
      mov write_flag,FALSE            ; sonst wird beim naechsten write
                                  ; erst alter Schrott ausgeschrieben
      cmp  autologin_flag,TRUE
      jnz  @@exit
      mov  cpm_drive,PHYS_DRV

      mov cpm86dd_flag,FALSE

      mov retry_count,2           ; Leseversuche herabsetzen,
                                  ; damit bei falschem
                                  ; Format nicht zuviel Zeit verschwendet wird

@@loop:

       cmp breakflag,TRUE

       CALLZ  prg_exit

; erst mal mit dem zuletzt verwendeten Format versuchen
; aber nicht, wenn letztes CP/M 86 war, da sonst das
; aehnliche Format mit 9 Sektoren nicht erkannt wird

      cmp byte ptr phys_sec_pt,8
      jz  short @@tst_cpm86

      mov al,pspt_last
      mov phys_sec_pt,al
      mov al,phys_tracks_last
      mov phys_tracks,al
      mov al,frstps_last
      mov first_phys_sec,al       ; physical sector
      mov al,dirtr_last
      mov dirtrack,al
      inc al
      mov r_track,al
      mov bx,offset secbuf
      call read_phys_track
      mov  bx,dpb_last
      jnc  @@goodfmt


;
; CP/M 86 format ?
;
; Bedingungen: Seite 0 muss Sektoren 1-8 haben, aber nicht 1-9
;              Seite 1 muss Sektoren 1-8 haben

; setze 9 Phys. Sektoren, bei cp/m 86 mit 8 Sektoren pro Track muss
; es einen Lesefehler geben

@@tst_cpm86:
      mov phys_sec_pt,9
      mov phys_tracks,40
      mov first_phys_sec,1h
      mov r_track,1+1
      mov dirtrack,1
      mov bx,offset secbuf
      call read_phys_track
      jnc  short @@nocpm86

      mov phys_sec_pt,8
      mov phys_tracks,40
      mov first_phys_sec,1h
      mov r_track,1+1
      mov dirtrack,1
      mov bx,offset secbuf
      call read_phys_track
      jc  short @@nocpm86


  ; Teste noch Seite 2 mit SektorNummer > 39

      mov phys_sec_pt,8
      mov phys_tracks,40
      mov first_phys_sec,1h

      mov r_track,40
      mov dirtrack,1
      mov bx,offset secbuf
      call read_phys_track
      mov  bx,offset dpb_cpm86DS
      mov  cpm86dd_flag,TRUE
      jnc  @@goodfmt
      mov  cpm86dd_flag,FALSE
      mov  bx,offset dpb_cpm86SS
      jmp  @@goodfmt

@@nocpm86:

;
; cpc system format ?
;
      mov phys_sec_pt,9
      mov phys_tracks,40
      mov first_phys_sec,41h
      mov r_track,2+1
      mov dirtrack,2
      mov bx,offset secbuf
      call read_phys_track
      mov  bx,offset dpb_cpc
      jnc  short @@goodfmt
;
; data format ?
;
      mov phys_sec_pt,9
      mov phys_tracks,40
      mov first_phys_sec,0c1h
      mov r_track,0+1
      mov dirtrack,0
      mov bx,offset secbuf
      call read_phys_track
      mov  bx,offset dpb_dta
      jnc  short @@goodfmt

@@tsteigfrm:
;
; is it own format
;
      mov phys_sec_pt,9
      mov phys_tracks,40
      mov first_phys_sec,1
      mov r_track,0+1
      mov dirtrack,0
      mov bx,offset secbuf
      call read_phys_track
      mov  bx,offset dpb_eig
      jnc  short @@goodfmt

@@badfmt:
      mov  bx,offset bad_format
      call puts
      call conin
      cmp  autologin_flag,TRUE    ; waehrend conin kann Edit DPH gewes. sein
      jnz  short @@exit
      jmp  @@loop

@@goodfmt:

; physikalisches Format merken

       mov al,phys_sec_pt
       mov pspt_last,al
       mov al,first_phys_sec
       mov frstps_last,al
       mov al,dirtrack
       mov dirtr_last,al
       mov dpb_last,bx

       mov  si,bx
       mov  di,offset dpb0+ZBIOSDISPL
       mov  cx,size dpb
       cld
       rep movsb                  ; es:di := ds:si

@@exit:
       pop bx
       mov retry_count,5
       ret
login_disc endp


write proc
;
; write a record of 128 bytes
;
       cmp  diskno,RMD
       jnz  short wtodisc
       call calcadr               ; ax:bx := record adress
       jc   short @@bad
       call wrtrec                ; buffer := record
       jc   short @@bad
       xor  al,al                 ; write ok
       ret
@@bad: mov al,1
       ret
write endp

wtodisc proc
       push cx
       call is_valid_rec          ; ax=rec/sec
       pop  cx
       jc   short @@badsec


; if track noch nicht im Buffer
;    if noch alter Track im Buffer
;       alten Track ausschreiben
;    endif
;    lies neuen Track ein
; endif
; if track ist dirtrack
;    if noch alter Track im Buffer
;       alten Track ausschreiben
;    endif
;    dirtrack ausschreiben
; else
;    Setze Flag, daá Sektor geschreiben wurde
; endif
;

       push cx                    ; bdos info, ob dirsectoren
       call read_w_track
       pop  cx
       jc   short @@badsec
;
; record # ist in ah
;

       push cx
       mov  al,WRITE_OP
       call get_adr_in_buf
       mov  ax,ds                 ; ax:= seg track buffer
       call wrtrec
       pop  cx
       cmp  cl,1
       jz   short @@isdir1
       call is_dir_track
       jz   short @@isdir1

       mov  write_flag,TRUE
       mov  al,track
       mov  last_track_written,al
       mov  al,0                  ; o.k.
       jmp  short @@exit

@@isdir1:
       call write_out_dir
       jc short @@badsec
       mov  al,0
       jmp  short @@exit
@@badsec:
      mov al,1
@@exit:
      ret
wtodisc endp

;
; In: ax:bx = source adr
;
rdrec proc
;
       PUSHR <si,di,bx,cx,ax>
       push ds
         mov si,bx
         mov di,dmaad             ; ds MUSS =  emudata_seg sein
         mov ds,ax
         mov cx,RECORD_LEN/2             ; record laenge in words
         cld
         rep movsw                ; es:di := ds:si
       pop ds
       POPR <ax,cx,bx,di,si>
       ret
rdrec endp
;
; In: ax:bx=target adr
;
wrtrec proc
       PUSHR <si,di,cx,ax>
       push es
       push ds

       mov si,dmaad               ; ACHTUNG: der MOV muss
                                  ; vor DS Segmentwechsel sein
       mov di,bx
       mov bx,ax                  ; ramdisc
       mov ax,es                  ; cp/m
       mov es,bx
       mov ds,ax

       mov cx,RECORD_LEN/2               ; record laenge in words
       cld
       rep movsw                  ; es:di := ds:si
       pop ds
       pop es
       POPR <ax,cx,di,si>
       ret
wrtrec endp

emulator_seg ends


end init



