home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / cpm / news / cpmnet81.sep < prev    next >
Text File  |  1994-07-13  |  46KB  |  1,064 lines

  1. >>>>>>>>>>>>>>>>>>>>> CP/M-Net News <<<<<<<<<<<<<<<<<<<<<<<<
  2.  
  3. ============================================================
  4. Number 9             September, 1981       Volume 1, Issue 9
  5. ============================================================
  6.  
  7.                       In This Issue
  8.                       =============
  9.  
  10.           Assembly Language Interface to PL/I-80
  11.           By:  Michael J. Karas, MICRO RESOURCES
  12.  
  13.                CP/M-Net "Tip-of-the-Month"
  14.  The Absolute Minimum Typing, to Run a CP/M Submit File...
  15.              By: Mike Karas and Kelly Smith
  16.  
  17.  Printed  monthly  (at worst quarterly) to inform user's  of 
  18. RCPM Systems to the latest software news,  information,  and 
  19. updates   of   public   domain   software   accessible   via 
  20. telephone/modem transfer.  Yearly subscription for copies of 
  21. the  CP/M-Net News may be obtained by mailing $18.00  (check 
  22. or money orders only) to Kelly Smith,  CP/M-Net,  3055  Waco 
  23. Street,  Simi Valley,  California 93063.  CP/M-Net is a non-
  24. profit  orginization and all money received on subscriptions 
  25. are utilized for the sustaining and enhancments of the CP/M-
  26. Net System.
  27.  
  28.  If  you  would  like to contribute an  article,  include  a 
  29. column  containing your area of interest and  expertise,  or 
  30. participate  in an open forum for conversation and  transfer 
  31. of  ideas,  feel free to send it to the CP/M-Net System  and 
  32. indicate that you would like it to be included in the  CP/M-
  33. Net  News...if possible,  use WordStar (trademark,  MicroPro 
  34. International)  or  Electric  Pencil   (trademark,   Micheal 
  35. Shrayer) in 60 column format.
  36.  
  37. Note:      CP/M is a registerd trademark of Digital Research
  38.  
  39. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  40.  
  41.            Assembly Language Interface to PL/I-80
  42.            ======================================
  43.  
  44.                     by: Michael J. Karas
  45.                        MICRO RESOURCES
  46.                       2468 Hansen Court
  47.                 Simi Valley, California 93065
  48.  
  49.  
  50.      The  advent  of  the  PL/I-80  high  level  programming 
  51. language for CP/M based machines has opened up the wide wide 
  52. world of easy, sophisticated, and structured programming for 
  53. business,   commercial  and  scientific  applications.   The 
  54. language  features offer the most capable development  tools 
  55. of any microprocessor programming language available in  the 
  56. marketplace  today.  Through  extended use of the  language, 
  57. others like my self, are going to find out that the features 
  58. of PL/I-80 offer far more than just programming niceity  for 
  59. applications software development.  The structure,  speed of 
  60. execution,   self  documentation  features,  and  full  CP/M 
  61. compatability  make  PL/I-80  the obvious  choice  for  many 
  62. systems utility and dedicated function processor programming 
  63. exercises. 
  64.  
  65.      The one main disadvantage of any high level language is 
  66. the isolation from the "guts" of the computer machinery that 
  67. results for the programmer.  Don't get me wrong in that this 
  68. is  an altogether disadvantage.  After all,  it is the whole 
  69. idea  behind the use of a high level language in  the  first 
  70. place.  But  for  certain  aspects of  systems  utility  and 
  71. dedicated function processor programming, more access to the 
  72. internals  of the computer are generally needed.  The normal 
  73. manner  to obtain this capability is through development  of 
  74. programs  in machine language.  But we all know what a  pain 
  75. that  can  be for very large programs in  the  thousands  of 
  76. bytes  in  size.  It  always  seems that  when  an  assembly 
  77. language program gets over about 2000 bytes, that there is a 
  78. screaming need for easy access to disk files,  the  operator 
  79. interfaces,  and  simplified development of complex  logical 
  80. structures.  PL/I-80 offers all of the things the heavy duty 
  81. assembly  language programmer needs to make life  easy.  The 
  82. next  obvious  question is...  How do I handle that  special 
  83. interface?..or  How can I do IN and OUT in PL/I-80?..or  How 
  84. can  PL/I-80  access  the SYSTEM tracks  of  a  CP/M  floppy 
  85. disk?...and the list of questions goes on and on and on. The 
  86. answer  to  all  these  questions is  to  use  the  assembly 
  87. language  interface facilities offered through the mechanism 
  88. of external procedure calls in PL/I-80.
  89.  
  90.  
  91. CP/M is a registered TRADEMARK and PL/I-80,  RMAC, and LINK-
  92. 80  are TRADEMARKS of Digital Research Inc.  Pacific  Grove, 
  93. California.
  94.  
  95.      The  Digital Research LINK-80 manual describes  how  it 
  96. all works but doesn't make it very easy to understand.  Here 
  97. in example form,  I intend to present an easy explanation of 
  98. how  to implement an assembly language interface to PL/I-80. 
  99. I have chosen to implement "hooks" to allow direct access to 
  100. the CP/M BIOS vector table of CP/M 2.2 to allow the  PL/I-80 
  101. programmer  to  make those special system  utility  packages 
  102. with  a  minimum  of  pain yet leaving the  avenue  open  to 
  103. perform  all those special I/O functions that  the  standard 
  104. CP/M  BDOS interface never seems to have enough of.  I  have 
  105. chosen  to write a diskette copy utility program in  PL/I-80 
  106. and  let the compiler make it easy to write all the operator 
  107. prompting  messages  and  to  allow  quick  human   readable 
  108. presentation  of the logical structure of the program  while 
  109. at  the  same time allowing direct high speed access to  the 
  110. disk drivers in the BIOS.
  111.  
  112.      The  start  of this project resulted  in  the  assembly 
  113. language module given below.  This module provides interface 
  114. translation   between   the   external   procedure   calling 
  115. mechanisms of PL/I-80 and the entry/exit requirements of the 
  116. various  assembly language hardware interface drivers of the 
  117. CP/M  2.2 BIOS.  Detailed discussion of all aspects  of  the 
  118. interface  conventions  are  well beyond the scope  of  this 
  119. article but a few general comments are in order. Readers who 
  120. desire  to  "understand to the MAX" are  encouraged  to  (1) 
  121. study the examples given here carefully while (2) consulting 
  122. the PL/I-80 LINK-80 Users Manual and (3) consulting the CP/M 
  123. 2.2 System Alteration Guide.  As a start, to become the most 
  124. familiar  with the mechanisms,  I would suggest implementing 
  125. the examples given here on your own computer system. It only 
  126. takes a few days or evenings and the experience can be quite 
  127. rewarding.
  128.  
  129.      The  interface  to the BIOS,  in general,  is  done  as 
  130. follows:  The  location  of the BIOS vector table  of  entry 
  131. points  is determinable in any CP/M system by looking at the 
  132. address  in locations 1 and 2 of low  memory.  This  address 
  133. gives  the  pointer  to the warm boot address  of  the  BIOS 
  134. vector table.  All other BIOS entry points are accessable by 
  135. adding  an  offset  to the warm boot  pointer  vector.  Some 
  136. functions are console status,  line printer output, and disk 
  137. track  selection.  All of the offsets necessary are given in 
  138. the  assembly  language  source  code  below.   The  typical 
  139. procedure  to  get  to a BIOS  subroutine,  with  8080  type 
  140. machine language, would be like this:
  141.  
  142. function:
  143.  
  144.      lhld 0001      ;get warm boot vector table pointer
  145.      lxi d,offset   ;get offset for desired BIOS entry point
  146.      dad d          ;make address of entry point
  147.      pchl           ;branch indirect through (HL) to BIOS 
  148.                     ;subroutine 
  149.  
  150.      If the label entry "function" had been CALLed from some 
  151. main  program,  then  the  return at the  end  of  the  BIOS 
  152. subroutine  would get program execution back to the  calling 
  153. program assuming we use the callers stack all along the way. 
  154. If   the  entry  to  the  interface  routine  required  post 
  155. processing  of BIOS return parameters before returning  back 
  156. to the main calling program,  then the above program segment 
  157. becomes: 
  158.  
  159. function:
  160.  
  161.      lhld 0001      ;get warm boot vector table pointer
  162.      lxi d,offset   ;get offset for desired BIOS entry point
  163.      dad d          ;make address of entry point
  164.      lxi d,backhere ;put return address to here on stack
  165.      push d
  166.      pchl           ;branch indirect through (HL) to BIOS 
  167.                     ;subroutine 
  168.     backhere:
  169.           <.... post processing
  170.               instructions....>
  171.     ret            ;back to main program call point
  172.  
  173.      All  parameter information entered at the BIOS  follows 
  174. the following general set of rules:
  175.  
  176. a)  If entry parameter is a single byte it is placed in  the 
  177.     (C) register.
  178.  
  179. b)  If entry parameter is a double byte or an address it  is 
  180.     entered in the (BC) register pair.
  181.  
  182. c)  Return parameters of one byte values are brought back in    
  183.     the (A) register.
  184.  
  185. d)  Return address of double byte parameters are returned in 
  186.     the (HL) register.
  187.  
  188.      For now, that should give the reader the information to 
  189. understand  the  BIOS part of the assembly  language  module 
  190. given below.  The full set of PL/I-80 rules are complicated, 
  191. so  I  will  only touch on the concept here as  to  the  few 
  192. interface types used by the BIOS module to return parameters 
  193. to  PL/I-80.  For  character string  variables,  the  return 
  194. parameter is put on the stack with the length of the  string 
  195. given  back in the (A) register.  This scheme applies to the 
  196. CONIN and READER routines which bring a byte character  back 
  197. from  the BIOS.  These push the character onto the stack and 
  198. send a 01 length back to the calling PL/I-80 program.
  199.  
  200.      If the return parameter is a PL/I-80 data type that  is 
  201. one  byte in size,  but a numerical type variable,  then the 
  202. value  is  returned to PL/I-80 in the  (A)  register.  If  a 
  203. numerical  type variable of two bytes or an address is being 
  204. passed back then the PL/I-80 program gets the parameter back 
  205. in the (HL) register pair. 
  206.  
  207.      All  entry  data sent from the PL/I-80 program  to  the 
  208. assembly language module comes as follows, if a parameter is 
  209. defined  for  passage to the BIOS.  The rule  is  that  when 
  210. called, the assembly language program receives an address in 
  211. the  (HL)  register  pair that points to a table  of  memory 
  212. locations  (in our case a single pair).  These two bytes  of 
  213. memory contain another address that points right at the data 
  214. parameter.  The  type,  size,  and number of parameters  are 
  215. defined  by design of the assembly language program and  the 
  216. statement of external entry point characteristics.  No  type 
  217. coding  is passed or error check done.  In other words  both 
  218. sides  have to agree to what is trying to be done before the 
  219. call and return parameter passing will work properly.
  220.  
  221.      The program module "PLIBIOS.ASM", given below in source 
  222. code form,  establishes the names for sixteen small programs 
  223. that  translate procedure calls from within a  main  PL/I-80 
  224. program  to the forms required by the BIOS.  The  subsequent 
  225. module  below,  "BIOSCALL.DCL"  is  a small file  the  fully 
  226. defines,  in a manner compatible with the assembly  language 
  227. program,  the form that the PL/I-80 program must use to call 
  228. the  entry  point definitions to make the parameter  passing 
  229. work.
  230.  
  231.      The assembly language program is meant to be  assembled 
  232. into  a  relocatable file of the ".REL" type by use  of  the 
  233. Digital  Research  relocating macro  assembler,  RMAC.  This 
  234. assembler, seeing the psuedo opcodes "PUBLIC" on the program 
  235. names builds the .REL file with a table to tell  the,  later 
  236. utilized,  link  program where the relative addresses of the 
  237. subroutines in the module are located.  After the host PL/I-
  238. 80  program,  like the disk copy utility given later in  the 
  239. article,  is compiled into a relocatable object module, that 
  240. .REL  module  also  contains  a  table  of  subroutine  call 
  241. addresses  which  were known to be external to  the  PL/I-80 
  242. program. The external nature if the labels is defined by the 
  243. included  declare  statement from  the  file  "BIOSCALL.DCL" 
  244. which  simply  told the compiler that I intended to  provide 
  245. another  ".REL" module that had these program names  in  it, 
  246. specifically "PLIBIOS.REL". The Link program LINK 80 is used 
  247. to hook the two modules together and make an absolute object 
  248. module  out  of  them.  The absolute object  module  happens 
  249. simply to be the executable CP/M type ".COM" file.
  250.  
  251.  
  252. File PLIBIOS.ASM
  253.  
  254.  
  255. ;***********************************************************
  256. ;  Direct CP/M 2.2 Bios access Interface Module For PL/I-80
  257. ;***********************************************************
  258. ;
  259. ;      This module to be asembled with Digital Research RMAC 
  260. ;  provides  interface conversion between PL/I-80 calls  and 
  261. ;  function references and the Bios vector table entries.The 
  262. ;  file   "BIOSCALL.DCL" should be included in your  PL/I-80 
  263. ;  source   program  with a %include statement  to  properly 
  264. ;  establish  the  external  entry  point names and  calling 
  265. ;  format  definition.   Most bios entry point routines  are 
  266. ;  treated  as  function call modules.    The user of  these 
  267. ;  routine  in  PL/I-80  calls  should be  aware  that  some 
  268. ;  differences     exist    between   the  CP/M   2.2   BIOS 
  269. ;  implementations that  are available from various software 
  270. ;  and   hardware  vendors.  In  particular,  not  all  BIOS 
  271. ;  routines   perform sector  translation with  the  SECTRAN 
  272. ;  routine.   Secondly, If the BIOS performs the function of 
  273. ;  physical   sector  deblocking,  and  the sector translate 
  274. ;  function  is   not used,   then  logical  sector  numbers 
  275. ;  entered   at  the  BIOS settrk  entry point  may  require 
  276. ;  numbering   from  zero  instead of the usual  start  with 
  277. ;  locical sector number 1.   This assembly language routine 
  278. ;  makes  no  assumption about the sector number start. Also 
  279. ;  no  translation is done. The calling PL/I-80 program must 
  280. ;  anticipate  the  characteristics of the host system BIOS. 
  281. ;  (BEWARE:  especially with tricky utilities being upgraged 
  282. ;  from   CP/M  1.4,  including  Digital  Research's  SYSGEN 
  283. ;  program).   Hopefully CP/M 3.0 coming out soon will fully 
  284. ;  resolve this problem.    Note that CP/M 3.0 will probably 
  285. ;  fully   eliminate  the need to have direct BIOS access at 
  286. ;  all.
  287. ;
  288. ;***********************************************************
  289. ;
  290. ;
  291. ;
  292.         name    'PLIBIOS'
  293.         title   'Direct CP/M BIOS Vector Calls From PL/I-80'
  294. ;
  295. ;
  296. ;***********************************************************
  297. ;*                                                         *
  298. ;*      bios direct calls from pl/i for direct i/o         *
  299. ;*                                                         *
  300. ;***********************************************************
  301. ;
  302.         public  cboot           ;cold boot reload of cp/m entry
  303.         public  wboot           ;warm boot reload of ccp
  304.         public  cstat           ;return byte for console status
  305.         public  conin           ;return character input from console
  306.         public  conout          ;write character to console
  307.         public  list            ;write character to list device
  308.         public  punch           ;write character to punch device
  309.         public  reader          ;return character input from reader
  310.         public  home            ;home selected disk unit
  311.         public  seldsk          ;select disk unit and return parameter
  312.                                 ;table pointer
  313.         public  settrk          ;select track 
  314.         public  setsec          ;select sector number
  315.         public  setdma          ;set disk i/o data buffer pointer
  316.         public  read            ;read selected sector and return status
  317.         public  write           ;write selected sector and return status
  318.         public  lstat           ;return byte for console status
  319.         public  sectran         ;translate sector number
  320. ;
  321. ;
  322. ;     Bios vector table offsets from the warm boot vector to 
  323. ; access  the  vector table entry points off the loc 1 and 2 
  324. ; warm boot vector.
  325. ;
  326. cbooto  equ     -3              ;cold boot offset
  327. wbooto  equ     0               ;warm boot offset
  328. cstato  equ     3               ;console status offset
  329. conino  equ     6               ;console input offset
  330. conouto equ     9               ;console output offset
  331. listo   equ     12              ;list device output offset
  332. puncho  equ     15              ;punch device offset
  333. readero equ     18              ;reader ddevice offset
  334. homeo   equ     21              ;disk home offset
  335. seldsko equ     24              ;select disk offset
  336. settrko equ     27              ;select disk offset
  337. setseco equ     30              ;select sector offset
  338. setdmao equ     33              ;set dma address offset
  339. reado   equ     36              ;read logical sector offset
  340. writeo  equ     39              ;write logical sector offset
  341. lstato  equ     42              ;list status offset
  342. strano  equ     45              ;sector translate offset
  343. ;
  344. ;
  345. ; Define position of CP/M 2.2 warm boot vector location
  346. ;
  347. wbvect  equ     0               ;addr of warm boot jump
  348. ;
  349. ;
  350. ;***********************************************************
  351. ;*                                                         *
  352. ;*       general purpose routines used upon entry          *
  353. ;*                                                         *
  354. ;***********************************************************
  355. ;
  356. ;
  357. ; get single byte parameter to register c
  358. ;
  359. getp1:
  360.         mov     e,m             ;low (addr)
  361.         inx     h
  362.         mov     d,m             ;high(addr)
  363.         xchg                    ;hl = .char
  364.         mov     c,m             ;to register c
  365.         ret
  366. ;
  367. ;
  368. ; get single word value to BC
  369. ;
  370. getp2:
  371.         call    getp1           ;get low byte of parameter
  372.         inx     h
  373.         mov     b,m             ;get high byte as well
  374.         ret
  375. ;
  376. ;
  377. ; transfer control to indexed bios vector table entry
  378. ; enter with DE = entry offset index
  379. ;
  380. gobios:
  381.         lhld    wbvect+1        ;get cp/m warm boot vector
  382.         dad     d               ;add in vector table offset
  383.         pchl                    ;go to table entry
  384. ;
  385. ;
  386. ;
  387. ;***********************************************************
  388. ;                                                          *
  389. ;      direct bios access routines for pl/i-80 calls       *
  390. ;                                                          *
  391. ;***********************************************************
  392. ;
  393. ;
  394. ;
  395. ; routine to enter bios cold boot to completely reload cp/m
  396. ;
  397. cboot:
  398.         lxi     d,cbooto        ;get offset
  399.         jmp     gobios          ;on our way
  400. ;
  401. ;
  402. ; routine to enter bios warm boot to reload ccp and bdos
  403. ;
  404. wboot:
  405.         lxi     d,wbooto        ;get offset
  406.         jmp     gobios          ;go reload
  407. ;
  408. ;
  409. ; routine to return console status byte
  410. ; return byte value to stack
  411. ;
  412. cstat:
  413.         lxi     d,cstato        ;status offset
  414.         jmp     gobios          ;return bit(8) in (a) from bios
  415. ;
  416. ;
  417. ; routine to get character from the console for input
  418. ;
  419. conin:
  420.         lxi     d,conino        ;conin offset
  421. ;
  422. ret1chr:                        ;entry point for one character 
  423.                                 ;return to pl/1-80 on stack
  424.         call    gobios          ;go get the status to (a)
  425.         pop     h               ;return address
  426.         push    psw             ;character to stack
  427.         inx     sp              ;delete flags
  428.         mvi     a,1             ;character length is 1
  429.         pchl                    ;back to calling routine
  430. ;
  431. ;
  432. ; routine to output one character to the console
  433. ;
  434. conout:
  435.         call    getp1           ;get single output char to (c)
  436.         lxi     d,conouto       ;console output offset
  437.         jmp     gobios          ;return to pl/1 direct from bios
  438. ;
  439. ;
  440. ; routine to output one character to the list device
  441. ;
  442. list:
  443.         call    getp1           ;get single output char to (c)
  444.         lxi     d,listo         ;list output offset
  445.         jmp     gobios          ;return to pl/i direct from bios
  446. ;
  447. ;
  448. ; output routine to send one character to the punch
  449. ;
  450. punch:
  451.         call    getp1           ;get single output char to (c)
  452.         lxi     d,puncho        ;punch output offset
  453.         jmp     gobios          ;return to pl/i direct from bios
  454. ;
  455. ;
  456. ; routine to get character from the reader for input
  457. ;
  458. reader:
  459.         lxi     d,readero       ;reader input offset
  460.         jmp     ret1chr         ;let common code do rest
  461. ;
  462. ;
  463. ; routine to get list device status 
  464. ;
  465. lstat:
  466.         lxi     d,lstato        ;list status offset
  467.         jmp     gobios          ;return bit(8) status in (a) from bios
  468. ;
  469. ;
  470. ; home selected disk entry point
  471. ;
  472. home:
  473.         lxi     d,homeo         ;home offset
  474.         jmp     gobios          ;return direct from bios
  475. ;
  476. ;
  477. ; entry  point  to select disk and return  parameter  table 
  478. ; pointer for selected drive in (hl)
  479. ;
  480. seldsk:
  481.         call    getp1           ;get single byte drive select byte
  482.         lxi     d,seldsko       ;select disk offset
  483.         jmp     gobios          ;return addr pointer in (hl) from bios
  484. ;
  485. ;
  486. ; routine to send double byte track number to bios
  487. ;
  488. settrk:
  489.         call    getp2           ;get track number to (bc)
  490.         lxi     d,settrko       ;set track offset
  491.         jmp     gobios          ;return through bios
  492. ;
  493. ;
  494. ; routine to send double byte sector number to bios
  495. ;
  496. setsec:
  497.         call    getp2           ;get sector number to (bc)
  498.         lxi     d,setseco       ;set sector offset
  499.         jmp     gobios          ;return through bios
  500. ;
  501. ;
  502. ; routine to send data buffer pointer to bios
  503. ;
  504. setdma:
  505.         call    getp2           ;get dma address to (bc)
  506.         lxi     d,setdmao       ;setdma offset
  507.         jmp     gobios          ;return through bios
  508. ;
  509. ;
  510. ; sector translate routine.
  511. ;
  512. sectran:
  513.         call    getp2           ;get logsec to (bc)
  514.         lxi     d,strano        ;sectran offset
  515.         jmp     gobios          ;return through bios
  516. ;
  517. ;
  518. ; routine to read sector of 128 bytes
  519. ;
  520. read:
  521.         lxi     d,reado         ;read sector offset
  522.         jmp     gobios          ;return error status through bios
  523. ;
  524. ;
  525. ; write sector routine. entry parameter of write type
  526. ;
  527. write:
  528.         call    getp1           ;get write type to (c)
  529.         lxi     d,writeo        ;write sector offset
  530.         jmp     gobios          ;return error status through bios
  531. ;
  532. ;
  533. ;+++...end of file
  534.  
  535.  
  536.  
  537.      The following short file is a single statement  PL/I-80 
  538. declare statement that defines in PL/I-80 syntax,  all entry 
  539. points  that  are given in the preceeding assembly  language 
  540. program.  The file is used so that the same set of  declared 
  541. variable  names  can  be  used in  multiple  PL/I-80  source 
  542. programs  without  having  to type them  in  all  the  time, 
  543. risking  making errors or ommissions.  The PL/I-80  lanugage 
  544. contains  a  feature  called the "%include"  statement  that 
  545. allows the following file to be put in as part of the source 
  546. code as simple as typing the following code as part of  your 
  547. program:
  548.  
  549.      %include 'bioscall.dcl';
  550.  
  551.      This  will  insert the following text into the  program 
  552. source  code at the point where the %include  appears.  Note 
  553. that in the above syntax the file "BIOSCALL.DCL" is expected 
  554. to  be  contained  upon the same disk drive  as  the  source 
  555. program.   Explanation  of  the  statement  formats  of  the 
  556. declared  entry points will be left for the examples  below. 
  557. An  example of the "%include" function appears in  the  disk 
  558. copy program below.
  559.  
  560.  
  561. File "BIOSCALL.DCL"
  562.  
  563. /* Define names and procedure characteristics for the     */
  564. /* MICRO RESOURCES Direct BIOS Access Procedures of       */
  565. /* "PLIBIOS.ASM" and PL/I-80 linkable module "PLIBIOS.REL'*/
  566.  
  567.  
  568.    dcl
  569.            cboot   entry,
  570.            wboot   entry,
  571.            cstat   entry           returns (bit(8)),
  572.            conin   entry           returns (char(1)),
  573.            conout  entry           (char(1)),
  574.            list    entry           (char(1)),
  575.            punch   entry           (char(1)),
  576.            reader  entry           returns (char(1)),
  577.            home    entry,
  578.            seldsk  entry           (binary fixed(7)),
  579.            settrk  entry           (binary fixed(15)),
  580.            setdma  entry           (ptr),
  581.            read    entry           returns (bin fixed(7)),
  582.            write   entry           (bin fixed(7)) 
  583.                                        returns (bin fixed(7)),
  584.            lstat   entry           returns (bit(8)),
  585.            sectran entry           (bin fixed(15))
  586.                                        returns (bin fixed(15));
  587.  
  588.      An  example of how to make use of the assembly language 
  589. BIOS  access  routines is presented below in the form  of  a 
  590. PL/I-80  source program that is a floppy disk copy  program. 
  591. First  let  me describe the intent of the program  and  then 
  592. show  a few examples of how the external BIOS  entry  points 
  593. are accessed.  The diskette copy program allows full copying 
  594. of  a  diskette  in Drive A:  to the diskette in  Drive  B:. 
  595. Tracks are buffered in memory in track buffer arrays so that 
  596. a whole track may be read at a time to make the program  run 
  597. reasonably  fast.  (Incidently,  this  copy  operation  runs 
  598. almost  as  fast  as  most assembly  language  copy  utility 
  599. programs  I've seen except for those that buffer  more  that 
  600. one track at a time).  Tracks are read sequentially from the 
  601. HOME  position of Drive A:  and written to the corresponding 
  602. positions of Drive B:.  After writing,  the Track is  reread 
  603. from Drive B: and compared byte for byte with the image read 
  604. in  originally from Drive A:.  Any errors are reported as to 
  605. track  and  sector  number to inform  the  operator  of  any 
  606. difficulties encountered along the way. At the completion of 
  607. the  copy process the operator is prompted to see if another 
  608. copy  is desired before rebooting the system disk  in  Drive 
  609. A:.
  610.  
  611.      All  console  interface and program logic structure  is 
  612. implemented  in the easy to program  PL/I-80  syntax.  Speed 
  613. dependant  and  hardware specific I/O access in the  program 
  614. makes  use  of the BIOS access external  entry  points.  The 
  615. following  examples  show  some of  the  external  procedure 
  616. references made to the entry points defined in "PLIBIOS". In 
  617. the  following paragraphs some familiarity with the  PL/I-80 
  618. language structure is assumed.  A tutorial on the  structure 
  619. and  syntax of the language is somewhat beyond the  intended 
  620. subject of this article.
  621.  
  622. EXAMPLE ONE -  Calling The CONSOLE INPUT ENTRY POINT (CONIN)
  623.  
  624.      The  console  input  entry  point  is  declared  as  an 
  625. external  entry  point in the BIOSCALL.DCL include  file  as 
  626. follows:
  627.  
  628.      DCL conin     entry returns(char(1));
  629.  
  630.      This   defines  conin  as  a  function  procedure  with 
  631. procedure invocation by name in an expression.  The implicit 
  632. CALL to the routine will return the declared type of  value, 
  633. CHAR(1) in this case,  to the point of the call. The example 
  634. from  the disk copy program has the conin call at the  point 
  635. of  waiting  for an operator response.  A character  string, 
  636. declared as follows:
  637.  
  638.      DCL  resp    char(80)  /* operator response buf */ ;       
  639.  
  640. ...is  designated to receive one console character with  the 
  641. following   statement.   The  NULL   parentheses   reference 
  642. designates  conin  as  a function procedure with  no  passed 
  643. parameters.  In this case  resp
  644.  
  645.      resp=conin();
  646.  
  647. ...becomes  a string of one entered character padded on  the 
  648. right with 79 blanks.
  649.  
  650.  
  651. EXAMPLE TWO - Calling the SET SECTOR ENTRY POINT (SETSEC)
  652.  
  653.      The set sector function queues the BIOS to a sector  to 
  654. read  on  a  subsequent  call  to  the  read  routine.  This 
  655. procedure  is  very similar in operation to the  set  track, 
  656. select  disk,  and set dma address entry points in that  all 
  657. are  defined  in the BIOSCALL.DCL file with declares of  the 
  658. form:
  659.  
  660.      DCL   settrk  entry           (bin fixed(15));
  661.  
  662.      This defines settrk as a subroutine procedure that  has 
  663. an  entry parameter consisting of a double byte binary fixed 
  664. point number that must be referenced with the following type 
  665. of statement:
  666.  
  667.      CALL settrk(n);
  668.  
  669. ...where  n is a variable of the fixed binary  variety  that 
  670. contains  the value of the track number to send to the BIOS. 
  671. Note  that  no  return  parameters  are  involved  with  the 
  672. procedure invocation.  Makes direct BIOS access quite simple 
  673. doesn't it !
  674.  
  675. EXAMPLE THREE - Calling the DISK READ SECTOR ENTRY (READ)
  676.  
  677.      The  read sector entry point is defined as  a  function 
  678. procedure  in  that  it  returns  a  binary  fixed(7)  value 
  679. pertaining  to  the  status  of  the  disk  read  operation. 
  680. (Defined   by  standard  BIOS  entry  definition,   assembly 
  681. language structure of the PLIBIOS program,  and the  declare 
  682. in the BIOSCALL.DCL file as follows:
  683.  
  684.      DCL  read      entry returns(bin fixed(7));
  685.  
  686.      Since read is a function call,  the read entry point is 
  687. referenced by name and the binary fixed(7) return error code 
  688. is  returned  to  the point of invocation simply as  in  the 
  689. statement  below.  This one statement causes the read  of  a 
  690. previously  selected logical 128 byte sector into the buffer 
  691. defined  by  a previous "setdma" call and at the  same  time 
  692. defines what action to perform if the read was  unsuccessful 
  693. and returned a non-zero error code.
  694.  
  695.  
  696.      if read() ^= 0 then do;
  697.                 put edit('Read Error On Control Disk - Track ',
  698.                           trkno,' Sector ',secno)
  699.                           (col(1),a,f(3),a,f(3));
  700.                 put skip(2) edit('Restart Copy ',
  701.                           'Process with new Source Diskette.')
  702.                            (col(1),a,a);
  703.                 go to copyloop;
  704.                 end;
  705.  
  706.      Other  BIOS  entry  points are referenced in  a  manner 
  707. similar to the examples above.  The "write" for instance has 
  708. an  entry parameter as well as a return error code like  the 
  709. read call.  The write entry parameter informs the  BIOS,  if 
  710. physical sector deblocking is done,  of the type of write to 
  711. perform (see the Digital Research CP/M 2.2 System Alteration 
  712. Guide  for more information on the exact definition of  BIOS 
  713. entry  point and return value definitions).  A special  note 
  714. concerning  the select disk entry point "seldsk" is that  it 
  715. returns  a  pointer value that defines the position  of  the 
  716. disk  parameter  table for the drive selected in  the  entry 
  717. parameter.  (Note  that a PL/I-80 NULL pointer return  value 
  718. defines an illegal drive designator).
  719.  
  720.      Careful  examination  of  the  following  program  will 
  721. reveal  all instances of direct BIOS entry point invocation. 
  722. The  rest  of  understanding of this program is  left  as  a 
  723. learning exercise for the interested reader.
  724.       
  725.  
  726. File dskcpy.pli
  727.  
  728. /***********************************************************
  729.  Full disk copy program to demonstrate the direct CP/M  Bios 
  730. access  facilities  presented in the  accompanying  article, 
  731. "Assembly Language Interface to PL/I-80."                           
  732.  
  733.  Track by track copying is utilized through the direct  bios 
  734. access  facilities  of the linked assembly language  program 
  735. "PLIBIOS".
  736. ***********************************************************/
  737.  
  738. dskcpy:
  739.         proc options(main);
  740.  
  741.         %replace
  742.                 trk_per_disk    by      77, /* logical bios tracks/diskette */
  743.                 sec_per_trk     by      26; /* logical cp/m sectors/track */
  744.  
  745.         /* external CP/M bios entry points */
  746.         %include 'bioscall.dcl';        /* bring in the bios entry point 
  747.                                            definition file that defines
  748.                                            bios function entry points*/
  749.         dcl
  750.                 resp                    char(80), /* operator response buf */
  751.                 inbuf(sec_per_trk)      char(128)
  752.                                            based(p), /* input buffer */
  753.                 outbuf(sec_per_trk)     char(128)
  754.                                            based(q), /* output buffer */
  755.                 trkno                   bin fixed(15),
  756.                 secno                   bin fixed(15),
  757.                 (i,j,k,l)               bin fixed(15), 
  758.                 (p,q,s)                 pointer,
  759.                 e5var                   bit(8) based(s),
  760.                 e5char                  char(1) based(s),
  761.                 e5trk                   bit(1),
  762.                 skew(sec_per_trk)       bin fixed(15) static init(
  763.                                         1,7,13,19,25,5,11,17,23,3,09,15,21,
  764.                                         2,8,14,20,26,6,12,18,24,4,10,16,22),
  765.                 e5string                char(128),
  766.                 partab                  pointer; /* disk parameter table ptr */
  767.                 
  768.  
  769.         /* Print initial message for Diskette copy from drive A: to B: */
  770.  
  771.         put edit('MICRO RESOURCES Diskette Copy program in PL/I-80 ',
  772.                  '^m^j',
  773.                  'Version 1.1 of August 3,1981')
  774.                  (col(1),3(a));
  775.                  
  776. copyloop:
  777.         put skip edit('Insert Source Diskette in Drive A:',
  778.                       '^iDepress <cr> when ready ')(col(1),a);
  779.         resp=conin();
  780.  
  781. newdisk:
  782.         put skip(1) edit('Insert Destination Diskette in Drive B:',
  783.                          '^iDepress <cr> when ready or <ctl-C> to quit')
  784.                         (col(1),a);
  785.         resp=conin();
  786.         if resp='^C' then do;
  787.                         put edit('Remember to Re-Insert your CP/M ',
  788.                                 'System Diskette in Drive A:',
  789.                                 '^iDepress <cr> when ready ')
  790.                                 (col(1),a,a);
  791.                         resp=conin();
  792.                         stop;
  793.                         end;
  794.  
  795.         put skip(1) edit('Copy in Progress...')(col(1),a);
  796.  
  797.         allocate inbuf set(p);  /* setup buffer storage */
  798.         allocate outbuf set(q);
  799.         allocate e5var set(s);          /* setup simple 0EH insertion */
  800.  
  801.         partab=seldsk(0);               /* select disk a: (0) */
  802.         call home;                      /* restore it */
  803.         partab=seldsk(1);               /* select disk b: (1) */
  804.         call home;                      /* restore that too */
  805.  
  806.         e5trk='0'b;                     /* e5 end track scan switch off */
  807.         e5var='e5'b4;
  808.  
  809.         do i=1 to 128;                  /* fill e5 record check string */
  810.         substr(e5string,i,1)=e5char;
  811.         end;
  812.  
  813. trklp:
  814.         do trkno = 0 to trk_per_disk-1 while( ^ e5trk);
  815.         
  816.         partab=seldsk(0);               /* select source */
  817.         call settrk(trkno);
  818.  
  819.         j=0;                            /* set empty sector counter off */
  820.  
  821.         /* read source track loop */
  822.  
  823.                 do secno = 1 to sec_per_trk;
  824.                 call setdma(addr(inbuf(secno)));
  825.                 call setsec(skew(secno));     
  826.                                         /* sectors are zero based     */
  827.                                         /* for some CP/M 2.2 BIOS's   */
  828.                                         /* if so replace (secno) with */
  829.                                         /* (secno-1)                  */
  830.  
  831.                 if read() ^= 0 then do;
  832.                                 put edit('Read Error On Control Disk - Track ',
  833.                                         trkno,' Sector ',secno)
  834.                                         (col(1),a,f(3),a,f(3));
  835.                                 put skip(2) edit('Restart Copy ',
  836.                                         'Process with new Source Diskette.')
  837.                                         (col(1),a,a);
  838.                                 go to copyloop;
  839.                                 end;
  840.                 end;
  841.  
  842.         partab=seldsk(1);               /* select destination */
  843.         call settrk(trkno);
  844.  
  845.         /* write destination track loop */
  846.  
  847.                 do secno = 1 to sec_per_trk;
  848.                 call setdma(addr(inbuf(secno)));
  849.                 call setsec(skew(secno));
  850.                                         /* sectors are zero based     */
  851.                                         /* for some CP/M 2.2 BIOS's   */
  852.                                         /* if so replace (secno) with */
  853.                                         /* (secno-1)                  */
  854.                 if write(2) ^= 0 then do;
  855.                                 put edit ('Write Error On Destination',
  856.                                         ' Disk - Track ',trkno,
  857.                                         ' Sector ',secno)
  858.                                         (col(1),a,a,f(3),a,f(3));
  859.                                put skip(2) edit ('Bad Diskette in Drive ',
  860.                                         'B:, try another.')(col(1),a,a);
  861.                                 go to newdisk;
  862.                                 end;
  863.                 end;
  864.  
  865.         /* read back destination loop */
  866.  
  867.                 do secno = 1 to sec_per_trk;
  868.                 call setdma(addr(outbuf(secno)));
  869.                 call setsec(skew(secno));
  870.                                         /* sectors are zero based     */
  871.                                         /* for some CP/M 2.2 BIOS's   */
  872.                                         /* if so replace (secno) with */
  873.                                         /* (secno-1)                  */
  874.  
  875.                 if read() ^= 0 then do;
  876.                                 put edit ('Read Verify Error On Destination',
  877.                                         ' Disk - Track ',trkno,
  878.                                         ' Sector ',secno)
  879.                                         (col(1),a,a,f(3),a,f(3));
  880.                                 put skip(2) edit ('Bad Diskette in Drive ',
  881.                                         'B:, try another.')(col(1),a,a);
  882.                                 go to newdisk;
  883.                                 end;
  884.                 end;
  885.                                 
  886.         /* data compare loop */
  887.  
  888.                 do secno = 1 to sec_per_trk;
  889.  
  890.                 if inbuf(secno) ^= outbuf(secno) then do;
  891.                                 put edit ('Verify Data Compare Error ',
  892.                                         trkno)
  893.                                         (col(1),a,f(3));
  894.                                 put skip(2) edit ('Bad Data on Diskette ',
  895.                                         'in Drive B:, retry same diskette.')
  896.                                         (col(1),a,a);
  897.                                 go to newdisk;
  898.                                 end;
  899.                 if inbuf(secno) = e5string then j=j+1; /* empty sector */
  900.                 end;
  901.  
  902.         if j=sec_per_trk then do;
  903.                         e5trk='1'b;
  904.                         put edit ('Track by track copy complete ',
  905.                                 'at track number ',trkno)
  906.                                 (col(1),a,a,f(3));
  907.                         end;
  908.         end trklp;
  909.  
  910.         free inbuf;
  911.         free outbuf;
  912.         free e5var;
  913.  
  914.         put skip edit('Do you wish to Copy This Another Diskette (Y/N) ')
  915.                (col(1),a);
  916.         resp=conin();
  917.         if (resp = 'Y') ! (resp = 'y') then go to newdisk;
  918.                                        else stop;
  919.         end dskcpy;
  920.  
  921.  
  922.  
  923.  
  924.      To   compile  the  PL/I-80  program  given  above   the 
  925. following  operational sequence would be used to invoke  the 
  926. Digital  Research  PL/I-80 compiler to process  source  file 
  927. "DSKCPY.PLI".  Lower  case characters after the "A>"  prompt 
  928. are  those  typed by you the operator.  Other  text  is  the 
  929. output of the compiler to the console screen.
  930.  
  931. A>pli dskcpy<cr>               <== Extent of .PLI is assumed
  932.  
  933.  
  934. PL/I-80   V1.3   COMPILATION OF:  DSKCPY
  935.  
  936.  
  937. %include 'bioscall.dcl';     <== Complier brings in prev-
  938.                                  iously edited DCL file for
  939.                                  all external entry points
  940.                                  in the "PLIBIOS.REL"module.
  941.  
  942.    NO  ERROR(S) IN PASS 1
  943.  
  944.    NO  ERROR(S) IN PASS 2    <== Compiler informs us of
  945.                                  pass completions.
  946.  
  947. CODE SIZE  =  05A9           <== Compile statistics for
  948. DATA AREA  =  044F               "REL" file of "DSKCPY".
  949. FREE SYMS  =  2BBF
  950. END COMPILATION
  951.  
  952. A>
  953.  
  954.  
  955.      The  next step to get a final disk copy program out  of 
  956. all  of  this work is to link the "REL" (relocatable  object 
  957. code)  modules of "DSKCPY",  "PLIBIOS",  and  the  necessary 
  958. subroutines  from  the  Digital Research  PL/I-80  run  time 
  959. library together into a ".COM" file. The LINK 80 linker from 
  960. Digital  Research  does  the job nicely with  the  following 
  961. console  operational "look".  As before,  the text in  lower 
  962. case following the A> prompt was that typed by the  operator 
  963. performing  the link process.  (Please note that the  symbol 
  964. values  given  may not necessarily coincide with  those  for 
  965. your  version  of  the  above  program;  especially  if  you 
  966. slightly  modify the text strings and signon messages in the 
  967. PL/I-80 source code listing).
  968.  
  969. A>
  970. A>link dskcpy,plibios          <== ".REL" extensions assumed
  971.                                     and output to DSKCPY.REL
  972.                                     is the default output.
  973. LINK 1.3
  974.  
  975. PLILIB   RQST   DSKCPY    0100   CONIN    06CC   SELDSK   0705
  976. HOME     06FF   SETTRK    070E   SETDMA   0720   SETSEC   0717
  977. READ     0732   WRITE     0738   CBOOT    06BA   WBOOT    06C0
  978. CSTAT    06C6   CONOUT    06D8   LIST     06E1   PUNCH    06EA
  979. READER   06F3   LSTAT     06F9   SECTRA   0729   /SYSIN/  27E7
  980. /SYSPRI/ 280C
  981.  
  982. ABSOLUTE    0000                  <== Link prints out module
  983. CODE SIZE   266B   (0100-276A)        size and address statistics
  984. DATA SIZE   06E2   (285A-2F3B) 
  985. COMMON SIZE 00EF   (276B-2859)
  986. USE FACTOR    95                  <== Hex percent of symbol table
  987.                                       usage.
  988. A>
  989.  
  990.      The  rest is up to you.  I have provided an example  of 
  991. how  assembly  language routines can be tied to  PL/I-80  to 
  992. implement  special  functions that are beyond the  scope  or 
  993. capability  of the language.  In this case they were  simple 
  994. examples  of interface conversions for  BIOS  access.  Other 
  995. examples  of  assembly language routines that could be  made 
  996. include "IN and OUT" functions for direct language access to 
  997. machine dependant input and output ports, high speed special 
  998. access  drivers for math chips such as the  Intel  8087,  or 
  999. tightly coded interrupt service routines to increase program 
  1000. performance  where  PL/I-80 is used as the host  development 
  1001. language in a real-time application.
  1002.  
  1003.      Operation  of the above example should be self  evident 
  1004. from a detailed study of the source listing. The best way to 
  1005. tackle your own problem is to dive right in.  For those  who 
  1006. feel  that  they need a little more structured  approach  or 
  1007. partly implemented feel for getting started, I am willing to 
  1008. provide  the source code for all of the modules described in 
  1009. this  article  on a single density CP/M  compatible  8  inch 
  1010. diskette free of charge if you just send a diskette, mailer, 
  1011. and  return postage to the address given at the beginning of 
  1012. this article. Feel free to make use of any new ideas you see 
  1013. here however you see fit.
  1014.  
  1015. ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  1016.  
  1017.                CP/M-Net "Tip-of-the-Month"
  1018.                ===========================
  1019.  
  1020.  The Absolute Minimum Typing, to Run a CP/M Submit File...
  1021.  
  1022.              By: Mike Karas and Kelly Smith
  1023.  
  1024.      On  occasion,  you  may need to run consecutive  SUBmit 
  1025. files,  under  'attended' operation,  and MAY have to do  it 
  1026. numerous  times...well  here is a trick to  enter  only  two 
  1027. characters  on  your keyboard (S and  Carriage  Return),  to 
  1028. minimize the amount of typing required.
  1029.  
  1030.      Edit  your SUBmit file as normally do...  end the edit, 
  1031. and then:
  1032.  
  1033. A>REN .SUB=LAZY.SUB<cr>    <-- Rename your .SUB to ONLY .SUB
  1034. A>REN S.COM=SUBMIT.COM<cr> <-- Rename SUBMIT.COM to S.COM
  1035. A>S<cr>                    <-- Run '.SUB'!!!
  1036.  
  1037.      How's  it work?  Easy...SUBMIT "see's" a temporary  FCB 
  1038. entry  of  the file "        .    " (eight  spaces  for  the 
  1039. filename  and  three  spaces for  the  filetype),  and  goes 
  1040. "searching"  for  a filetype of ".SUB"...and  low-and-behold 
  1041. FINDS IT!
  1042.  
  1043.      Now  to really speed things up for your SUBMIT,  we use 
  1044. that old trick of fooling the CCP (Command Console Processor 
  1045. with a 'null file'...you remember,  'SAVE 0 @.COM'...and use 
  1046. it  to   minimize  the  'loading  time'  of  some  redundant 
  1047. operation.  For  example,  when generating a pile of  system 
  1048. disks you might edit a SUBMIT file like this:
  1049.  
  1050. sysgen           <--- Let's SYSGEN...
  1051. pip b:=pip.*[v]  <--- And PIP some goodies we need to B:
  1052. @ b:=xdir.*[v]   <--- And use PIP still in memory
  1053. @ b:=stat.*[v]   <--- And use PIP again...
  1054. @ b:=asm.*[v]    <--- And again...
  1055. @ b:=ed.*[v]     <--- Again..
  1056. @ b:=load.[v]    <--- Well, you get the point by now!
  1057.  
  1058.      Keep  in mind however,  that you may only use the  LAST 
  1059. file that was loaded into memory...for instance,  if you had 
  1060. placed  something  like ABORTSUB.COM in the  middle  of  the 
  1061. PIP's,  and then intended to CONTINUE PIPing, you MUST again 
  1062. invoke  PIP explicitly as a filename...THEN you can continue 
  1063. using '@' as illustrated here.
  1064.