ZX Review #11-12
26 ноября 1997 |
|
reader-reader - Driver input in serial mode and direct access from the files of TR-DOS.
The Reader - Reader Music by ZET and MITCHELL Boris Kuritsyn, 257010 Cherkassy, PO Box 1529 DRIVER INPUT In sequencing mode And direct access FROM THE FILES OF TR-DOS The author wrote this driver (Or rather its first part) as basic element of a high-level language compiler, development which is engaged in the initiative group from Cherkassy. But it is completely standalone program that can directly used by those who wrote to assembly, and a dilator designed BASIC - and those who BASIC programmers. In addition, analysis of her acquaint the reader with the programming of the driver of the channels and streams, the principles of writing BASIC extenders, the basics parsing with the methods of extending the set of errors BASIC. What is the purpose of writing this driver? There is a class of programs, who must simultaneously work with many files (and commissioning, and decommissioning) themselves at the same time hold enough memory, so that about simultaneous loading of all the required files can not be speech. Opening the System Stream for each file, they can work through this driver, even with 10 files in length, say, 60 KB at a time, at the rate of memory about 290 bytes per file. By driver functions similar to the operators TR DOS file handling direct / sequential access, but works with CODE-file and similar files with non-standard types. In addition, as shown by the "father All programmers "Niklaus Wirth (Author of Pascal, Modula2), then the program is simple, correct and reliable when PROGRAMME STRUCTURE MEETS THE STRUCTURE OF THE DATA. To implement this principle, we need appropriate tools. One of them is this driver because it allows the program to work only with "abstract" aspects of data ie in terms of "character", "read the position." First, the technical aspects. The author prefers an assembler ZX Turbo Assembler v.2.4, and listing is given in its format. Hopefully, users of other assemblers do not have problems transfer program texts. The author when writing programs uses two auxiliary modules: access to TR DOS and the interception module system errors. TR DOS module is very simple. This is analogous to a system call # 3D13, which mistakes TR DOS converts exceptions (call RST 8) codes 100-112, which eliminates the need each time to check the error code system. In addition, the call itself done through a vector (dosVector), which allows you to "hang" the call TR DOS additional function at run time program by replacing the values dosVector to address its additional handler. 140. ; **************************************** * * * System call TR-DOS v5.0x * * * 3; * (c) B. Kuritsyn, June 1996 * * * * File: dosapi.a * * File Format: ZX TASM * * * ; **************************************** The exception "error TR-DOS" excDosError equ 99 ; Mistakes TR-DOS dNoFile equ 1 dFileExists equ 2 dNoSpace equ 3 dDirFull equ 4 dRecOF equ 5 dNoDisk equ 6 dStrmOpened equ 10 dNotDskFile equ 11 dVfyError equ 13 dos db # c3; it jp (dosVector) dosVector dw dosIntr ; Call DOS shell functions dosIntr push af ld a, c ld (_fncNo), a xor a ld (23823), a ld (23824), a pop af call # 3d13 push af ld a, (_fncNo) cp # a; if the function was performed 10 (file search), the code jr z, dosOk; return - this is not an error code ld a, (23823) or a jr z, dosOk; if there was a mistake to initiate an exception ; Initiation exception DOS dosError add a, excDosError ld (_errNo), a rst 8 _errNo db 0 _fncNo db 0 dosOk pop af ret 2 Interested exception handling may apply to the article [1]. Here I will say briefly: an exception - it is extremely convenient programming technology, which allows implicit conversions of handling errors or exceptional situations without the need regular checks flags and return codes, etc. At the same time ensures the conservation status of some elements of the system and programs (at least the stack). In the form below supports multi-level exception handling with the ability to dynamically change set of conserved elements and the exception handler. Anyone interested can simply analyze the text of the program. Briefly same can be said like this: if at the beginning of a program, call try (see listing) with the address of your error-handling routine, then in the case of a call RST 8 anywhere within the program management be passed to your handler. The handler must, first of all, cause endTry to restore the stack and remove for themselves. That the same call should be made before command RET program. 140. ; **************************************** ; * Exceptions handle * * Exception handling * 3; * (c) 1994-96 B. Kuritsyn * 3 * Version: 2.0 06/18/1996 * * File: except.a * ; **************************************** err_SP equ 23613 ; ---------- External Treatment --------- try db # C3; a JP (tryAd) tryAd dw mark endTry db # C3 eTryAd dw unmark ; Continue the current exception , (Just call the exception handler team JP) ; New exception code in the A putErr ld (iy), a ; The same exception contErr ld sp, (err_SP) ret ; Change marking stack ; Entry: @ HL-new handler Try, @ DE-new handler EndTry , Output: @ HL-old handler, Try, @ DE-old handler EndTry chMarking ld bc, (tryAd) ld (tryAd), hl ld h, b ld l, c ld bc, (eTryAd) ld (eTryAd), de ld d, b ld e, c ret ; ------------ SALES -----------; Mark stack entry: @ DE-exception handler ; Destroyed: HL, IX mark POP IX LD HL, (err_SP) PUSH HL PUSH DE LD (err_SP), SP JP (IX) ; Demarcate the stack are destroyed: HL, IX unmark POP IX LD SP, (err_SP) POP HL POP HL LD (err_SP), HL JP (IX) 2 Necessary to stipulate that those who do not know what a "channel" and "flow", before continuing reading this article should read the chapter on canals and streams in the famous three volumes Inforkoma Programming native for the ZX Spectrum or an application to the instruction Users of text editor BK Write, is spread author. And now for the driver itself. Which set of functions it should perform? Firstly, it is opening the file: creating a channel that is associated with the file, and connect any flow to the channel. Secondly, the driver must allows you to read the file bytes not only consistent but also permuted index of reading directly to any position in the file. Third, it is shutting down File: the destruction of the channel, closing the stream. And how, indeed, entering information, you ask? It's very simple - the system procedure WaitKey (address # 15D4). Next, we consider the annotated text driver (Note: the driver is moments associated with the withdrawal of file - this part of the driver is not over, in addition, include her story is not possible due to the large volume). Immediately turn to the basic idea of the driver: it is necessary to give byte file, offset by a file defines the current position. Position can be arbitrarily changed at any time. When reading byte pointer position moves to the next byte. Trying to read anything beyond the end of the file generated an exception. To implement this in a leader tape channel provides space for one sector of the file. At any point in that buffer is loaded sector corresponding to the current position in the file. With any change in the position number of the sector is monitored and, if necessary, the sector is overloaded. 140. ; **************************************** * * ; * DISK STREAMABLE I / O DRIVER v1.05 * * * * Driver streaming I / O * * To / from files TR-DOS. * * * 3; * (c) B. Kuritsyn, June 1996 * * * * File: stream.a * * File Format: ZX Turbo Assembler * ; **************************************** include "dosapi.a" The length of the sector sectLen equ 256 ; System variables chans equ 23631; pointer to channel curChl equ 23633; pointer to the current channel prog equ 23635; pointer to the program (for the end of channels) ; System variables TR-DOS dfDrive equ # 5d19; drive by default searchCh equ 23814; number of characters for a name search ; Protsedury system, details of procedures see [2] makeRoom equ # 1655 reclaim2 equ # 19E8 strData equ # 171E +3 chOpen equ # 1601 callJp equ # 162C ; Channel input and output is named "F", so they can be differences chat, ; Established leader tape of the signature: readSign equ # 5678 writeSign equ # 6789 ; Throwing exceptions invStream rst 8 db 23, "is used incorrectly Stream invFileName rst 8 db 14, "invalid name" invDevice rst 8 db 18, "invalid device" endOfFile rst 8 db 7, 'end of file " ; Shift in the leader tape, relatively unique structure of the leader tape see [3] oPrint equ 0; address inference procedures oInput equ 2; address of a procedure entry oName equ 4; channel name oSign equ 5; the signature of the channel oClose equ 7; address of the procedure file is closed oLength equ 9; length of leader tape oIntern equ 11; More ; Image of the leader tape channel for reading readRecord DW invStream DW inputIn DB "F" DW readSign, noOp DW 1930 + sectLen; length of leader tape DB 0; device (displacement oIntern +0) DB "C" DS 7; directory entry (...+ 1) DW # FFFF; file position (...+ 17) ; Here: DS sectLen - buffer sector file (...+ 19) rdRecLen equ $-readRecord + sectLen ; Procedure for entering characters from a file, the next byte of the file returns , In A. Byte is always returned, so the state INKEY $ # 5 = "" ; Does not happen. inputIn ld ix, (curChl); address of the leader tape ld l, (ix + oIntern +17); current position in file ld h, (ix + oIntern +18) ld c, l ld b, 0 add ix, bc ld a, (ix + oIntern +19); fetching bytes from the current sector scf; Flag: Byte received from the device push af inc hl call _seekRead; the transition to the next position pop af noOp ret ; Positioning in the current file for reading ; Input: HL-position ; --- External version, verify that the current channel - F and it open ; To read - if not the wrong thread) seekRead push hl call testChl ld l, (ix + oSign) ld h, (ix + oSign +1) ld bc, readSign and a sbc hl, bc jp nz, invStream pop hl ; --- Internal version _seekRead ld ix, (curChl) , Check to see whether an end of file ld c, l ld b, h ld e, (ix + oIntern +12); compared with the length of the file ld d, (ix + oIntern +13) and a sbc hl, de ; Position just after the last byte of the file, the buffer is not overloaded zhaetsya jr z, atEOF jp nc, endOfFile ; Establish a new position ld a, b cp (ix + oIntern +18); in the same sector? atEOF ld (ix + oIntern +17), c ld (ix + oIntern +18), b ret z; yes - complete ; Not - the new position of the corresponding byte from another sector file ; Download a new sector in the buffer ld l, (ix + oIntern +15) ld h, (ix + oIntern +16); position of beginning of the file on disk call makeLog; transformation into a logical sector ld e, b ld d, 0 add hl, de; a logical sector number that you want download call makePhis; inverse ex de, hl push ix pop hl ld bc, oIntern +19 add hl, bc push hl push de ld a, (ix + oIntern) ld c, a call dos; drive select pop de pop hl ld bc, # 105; boot sector jp dos ; Convert Options "Sector L - track H" in the "logical Sector HL " ; NOTE: The conversion to a "logical sector" can easily ; Calculate the position of the sector on the disk as logical Sector And you can add and subtract like normal numbers. ; Transformation formula: log. sector = track * 16 + sector makeLog xor a 140. srl h rr a srl h rr a srl h rr a srl h rr a or l ld l, a ret ; Convert Options "logical sector HL" in the sector L - lane H " makePhis ld a, l sla a rl h sla a rl h sla a rl h sla a rl h ld a, # F and l ld l, a ret ; Type of file to open, changing this value before calling openRead, Or you can open a non-standard files fileType db "C" ; Opening file for reading; ; Entry: ; A-number flow, @ DE/BC- name string (as usual: the address of DE, length of BC) ; Specified thread will be opened on the file. openRead exx ld (stream), a; verifies that the stream is closed call strData ld a, b or c ld a, dStrmOpened; if not - the error DOS "thread already open " jp nz, dosError call newRdRecord; create a channel leader tape ld (record), hl push hl exx pop hl call parseName; parse name ld hl, (record) inc hl ld bc, (chans); find the offset of the leader tape (Chans) ... and a sbc hl, bc push hl ld a, (stream) call strData pop bc ld (hl), c; ... and open the requested stream to the channel inc hl ld (hl), b ld ix, (record) ld a, (ix + oIntern) ld c, a call dos; drive select ld c, # 18 call dos; setting a floppy disk ld hl, (record) ld bc, oIntern +1 add hl, bc push hl ld c, # 13 call dos ld hl, searchCh ld (hl), 9 ld c, # a call dos; search for the file to open bit 7, c jr nz, noFile; if the file does not exist - everything back to its original state ld a, c ld c, 8 call dos; if the file is, get its directory entry pop hl ld c, # 14 call dos; and copy it to the leader tape channel ld a, (stream) call chOpen ld hl, 0 jp _seekRead; establish a position on top of file , In the absence of a file all returns to its initial state noFile ld ix, (record) call freeRecord; remove the leader tape channel ld a, (stream) call strData ld (hl), 0; close of thread inc hl ld (hl), 0 ld a, dNoFile; an error DOS "no such file" jp dosError stream db 0 record dw 0 ; Parsing the file name , As "[drive:] name". If the drive is not specified, is taken drive on ; Default. Name and drive into the realm of oIntern leader tape. ; Input: HL-mail leader tape to open the channel, @ DE/BC- Line Name parseName push hl pop ix ld a, b; empty name string - Error or c jp z, invFileName ld a, (dfDrive); until the drive by default ld (ix + oIntern), a ld a, (fileType); set the file type ld (ix + oIntern +9), a ld hl, -3; name is longer than three characters? and a adc hl, bc jp m, nameOnly; not - hence the drive to name just Unknown inc de; check with respect to the drive ... ld a, (de) dec de cp ":"; if the second character - a colon ... jr nz, nameOnly ld a, (de); ... then the first - the drive res 5, a; leads to the Uppercase sub "A"; separate the correct names: A, B, C, D. jp c, invDevice cp 4 jp nc, invDevice ld (ix + oIntern), a; drive is found and installed in leader tape inc de; shifted to the name ... (2 characters) inc de dec bc dec bc nameOnly ld a, b or a jp nz, invFileName ld a, c; if the name is 8 characters long - error cp 9 jp nc, invFileName ld b, c; if everything is ok - copy the name of leader tape copyName ld a, (de) inc de ld (ix + oIntern +1), a inc ix djnz copyName ret ; Creating leader tape read channel ; Output: HL-mail created by the leader tape newRdRecord ld hl, (prog); at the end of Chans ... dec hl ld bc, rdRecLen; ... allocate memory for the leader tape ... call makeRoom inc hl push hl ex de, hl ld hl, readRecord; ... and copy back the image of the leader tape ld bc, rdRecLen-sectLen ldir pop hl ret ; Removing leader tape read / write channel ; Entry: IX-mail leader tape freeRecord ld c, (ix + oLength); of the leader tape get its length ... ld b, (ix + oLength +1) ld (recLen), bc push ix pop hl jp reclaim2; ... and release the area occupied by them recLen dw 0 , Check that the current channel - a channel of "F" , Output: IX = (curChl) testChl ld ix, (curChl); if the name of the current channel ... ld a, "F" cp (ix + oName); ... not "F" ... ld a, dNotDskFile jp nz, dosError; ... mistake DOS "is not a disk file" ret , Close the file read / write stream A close push af call chOpen; makes the channel current call testChl; verify that this flow is associated with our channel , Perform the procedure closing the channel ld l, (ix + oClose) ld h, (ix + oClose +1) call callJp; transition to execution ; Free memory ld ix, (curChl) call freeRecord; remove the current channel of memory , Close the stream pop af call strData ld (hl), 0, 0 - stream closed inc hl ld (hl), 0 ; Adjust variables for streams Streams 0-15. ; The fact that after the opening of this channel could be opened and ; Others. Their leader tape located in the memory after our leader tape. ; With the removal of our leader tape displacement of Streams in the these ; Channels become null and void and must be corrected for , The length of the remote leader tape. ld d, b; offset the already remote leader tape ld e, c ld b, 16; scan the 16 streams strmLoop push bc ld a, b dec a call strData; choose a value for the flow push hl pop ix ld h, d ld l, e and a sbc hl, bc; if it does not shift more of our ... jr nc, noCorrt; ... no adjustment is required ld h, b ld l, c ld bc, (recLen); otherwise reduce the bias on the length of leader tape and a sbc hl, bc ld (ix), l; and save ld (ix +1), h noCorrt pop bc djnz strmLoop; cycle for all streams ret 2 Thus, the driver is written. And how I use it? Elementary. First, any free flow (say, # 5) open at file: 140. org 60000 jp start include "stream.a"; enable drivers to ... include "except.a"; ... and exception handling name db "textfile"; to name the file ... nameLen equ $-name; ... and its length start ld de, name ld bc, nameLen ld a, 5 call openRead 2 File is opened. Now, for example, print it on the screen. We will read a file character by character and transmit bytes channel "S". Because the process is completed except for "end of file", catch the exception: 140. ld de, endOfPrint; address of the handler call try 2 Now you can display the file in "Infinite" loop. 140. waitKey equ # 15D4 process ld a, 5; file stream call chOpen; chOpen = # 1601 call waitKey; introduce the symbol push af ld a, 2; flow screen call chOpen pop af rst 16; derive symbol jr process 2 At the end of the file or another exception management will be sent here: 140. endOfPrint call endTry; remove handler ld a, 7 cp (iy); current fault - it's "end of file"? jp nz, contErr; if not - keep it spread the word ; Yes - everything is in order, the withdrawal is completed ld a, 5 call close; close disk file ret 2 What about BASIC? Like would be able to open files directly from the operators BASIC. Nothing could be easier. The following interface to the BASIC driver realizes this and more something: it converts the new DOSovskie exceptions to the new BASIC errors, which are displayed in the same way as conventional. Except He displays the status of the streams system on the screen or in any another thread. New operators BASIC is as follows: LET d = 64000 REM opening <flow> on <filename> RANDOMIZE USR d: OPEN # stream, filename $ REM closing disk file associated with the <flow> RANDOMIZE USR d: CLOSE # stream REM transition toin the file associated with <thread> PRINT # thread;: RANDOMIZE USR d: GO TO position REM display information about the state of flux at <thread> REM to display flow = 2 RANDOMIZE USR d: LIST # stream A small remark about the operator GO TO: operator PRINT, you see ahead, only makes the flow of current as well as working with GO TO current flow. Note the ";" at the end of the operator PRINT. Operator LIST lists all open streams with an indication of the channels that they are open. Direct input from a file in the usual way: the operator INPUT # thread; simv_peremennaya $ to enter a character string or operator LET simv_peremennaya $ = INKEY $ # input stream for a single character The second way is preferable, as in the ZX Spectrum has this feature: assumed that the operator INPUT data is entered only from flows associated with the keyboard and the sound of the keyboard is handled in a statement INPUT. Therefore, when you enter the file you hear the squeaks of the sequence "keystroke" in the enter each character. In addition, input lines will be completed only upon receipt of a character code Keys <Enter> (# 0D) and this very character in the string will not be included. Throughout the program, well It can be seen as a dilator BASIC not only performs the action, but and analyzes the syntax of the program. 140 .;***************************************** * * ; * BASIC INTERFACE TO * ; * DISK STREAMABLE I / O DRIVER, v1.05 * * * * The interface to the BASIC * * Download streaming I / O * * To / from files TR-DOS. * * * 3; * (c) B. Kuritsyn, June 1996 * * * * File: basint.a * * File Format: ZX Turbo Assembler * ;***************************************** ; Codes tokens t_open equ # d3 t_close equ # d4 t_goto equ # ec t_list equ # f0 ; System variables (or bias) oFlags equ 1 oTVFlag equ 2 oFlags2 equ 48 oFlagX equ 55 oXPtrHi equ 37 defAdd equ 23563 strms_6 equ 23574 ; Used semiconductor ROM. For details, see [2]. class06 equ # 1C82; syntax. Analyzer - should be numeric expression class0A equ # 1C8C; syntax. Analyzer - should be a line Character separator equ # 1B6F; parser - should be lexeme stkToA equ # 1E94; stack calculator - the A stkToBC equ # 1E99; stack calculator - in BC stkFetch equ # 2BF1; stack calculator - in the A / BC / DE clsLower equ # 0D6E setMin equ # 16B0 copyBuff equ # 0ECD stackA equ # 2D28; A - a stack calculator printFP equ # 2DE3; print number from the stack calculator po_mess equ # 0C0A; display a message from Table A (DE) org 64000 ; Entry point startUp ld de, newErrors; new handler issklyucheny call try ld c, ":"; a separator after the USR 64000 ; Separator verify that the current symbol interpretiruenmy namely , Is as follows. If not - error "nonsense in BASIC" call separator ld c, a rst # 20; take the name of the operator ... ld hl, statms; ... and look for it in the table Statms jr compare nextSearch inc hl inc hl compare ld a, (hl) inc hl or a jr nz, cont_comp; if the table was over - a mistake rst 8 db # b; Nonsense in Basic cont_comp cp c jr nz, nextSearch ld a, (hl); found, select the address of the handler ... inc hl ld h, (hl) ld l, a call callJp; ... and run it call endTry; remove the exception handler ret ; Table description operators. ; Format: token, the address of the handler, token, address, handler, ... 0. statms db t_open dw _open db t_close dw _close db t_goto dw _goto db t_list dw _list db 0 listStrm db 0 The text for the operator LIST listMess db # 80, "Streams status:", 13 + # 80 db ": opened to", 34 + # 80 140. db 34,13 + # 80 3; operator LIST # stream _list ld c, "#"; check symbol stream call separator call class06; should be followed by a numeric expression call stkToA; it - in the battery call chOpen; open channel for output ld de, listMess sub a ld (listStrm), a call po_mess; output header listNext ld a, (listStrm); for the flow ... call strData; ... selected data from the Streams ld a, b or c jr z, listCont; when closed - the next thread ld a, "#"; print symbol rate ... push bc rst 16 ld a, (listStrm) call stackA call printFP; ... and its facilities pop bc ld hl, (chans) add hl, bc inc hl inc hl inc hl; definition addresses oName bias in the channel push hl ld a, 1 ld de, listMess call po_mess; message "open with" ... pop hl ld a, (hl) rst 16; ... such and such a channel ld a, 2 ld de, listMess call po_mess listCont ld hl, listStrm inc (hl); next thread ld a, 16 cp (hl) jr nz, listNext; move to the next thread ret 3; Operator OPEN # stream, name $ - no comment _open exx push hl call openParams call openRead pop hl exx ret ; Parse OPEN statement openParams call class06; must be thread number - a stack Calculator ld c, "," call separator; then a comma call class0A; then the character expression - the filename rst # 28; exchange name and the flow in some places on the stack db 1, # 38; exchange call stkToA; stream - in the battery push af call stkFetch; name - in the DE / BC pop af ret 3; operator CLOSE # stream _close call class06; must be thread number - a stack Calculator call stkToA; stream - in the battery jp close ; Operator GO TO pos _goto call class06; number - position in the file call stkToBC; with stack calculator in BC ld h, b ld l, c jp seekRead ; Error handler: visualizes new errors newErrors call endTry; remove handler halt ld a, (iy); error code cp excDosError + dNoFile jp m, contErr; usual errors SOLD further ; This part of the program virtually repeats the action standard ; Error handler from the ROM to output error messages. See , [3] from the address # 1303 res 5, (iy + oFlags) bit 1, (iy + oFlags2) call nz, copyBuff ld hl, 0 ld (iy + oFlagX), h ld (iy + oXPtrHi), h ld (defAdd), hl ld hl, 1 ld (strms_6), hl call setMin res 5, (iy + oFlagX) call clsLower set 5, (iy + oTVFlag) ld a, (iy) sub excDosError + dNoFile ld b, a add a, "a"; new codes Posts rst 16 ld a, ""; gap rst 16 ld a, b ld de, messages; new posts in this table ld (iy), # FF jp # 1346; further processing of the standard - in ROM ; Table text messages about new bugs (in the format of the procedure PO_MESS) messages db # 1980, "No fil", "e" + # 80 db "File exist", "s" + # 80 db "Disk ful", "l" + # 80 db "Dir ful", "l" + # 80 db "RecNo overflo", "w" + # 80 db "No dis", "k" + # 80 db "(7) DOS erro", "r" + # 80 db "(8) DOS erro", "r" + # 80 db "(9) DOS erro", "r" + # 80 db "Stream opene", "d" + # 80 db "Not disk fil", "e" + # 80 3 db "(1912) DOS erro", "r" + # 80 db "Verify erro", "r" + # 80 ; Enable drivers and exception handling include "stream.a" include "except.a" ; *** End of basint.a *** 2 Well that's all. I think to check the interface you will be able to write as many programs. I hope that my brief (Due to the large amount of assembly code) comments given at least a general idea of work program and the program will be useful to you. If you have any problems or specific questions you can write to the author at the address above. REFERENCES 1. B. Kuritsyn. Interception system programming errors in the Assembler for PCs "ZX Spectrum ".: Radio amateur, 10 ' 1994; p. 12 - Minsk, 1994. 2. Logan, O'Hare. Full description Rom ZX Spectrum.: "ProgrammAss" - Kharkov, 1992. 3. K. Kurylovich, D. Madej, K. Marasek. Guide to the ZX Spectrum.: "Program-Ass" - Kharkov 1992. *
Other articles:
Similar articles:
В этот день... 21 November