Wiki source code of API контроллеров AZ®
Last modified by Max on 2025/02/25 11:06
Show last authors
author | version | line-number | content |
---|---|---|---|
1 | {{box cssClass="floatinginfobox" title="**Contents**"}} | ||
2 | {{toc/}} | ||
3 | {{/box}} | ||
4 | |||
5 | = Controller concept = | ||
6 | |||
7 | The controller emulates up to 32 AZ® disk devices on a single microSD card. Each emulated disk (pseudo disk) is represented on the card by a file in terms of the FAT32 file system, so there are no questions about placing pseudo disks on a large-capacity storage device - just place the card in the card reader, connect it to the PC, copy files of the required size (and with the required content) to the card, move it to the controller, and that's it! Moreover, after working with the card on the PDP-11/DVK/BK/UKNC, you can remove it from the controller, place it back in the card reader and copy the resulting disk image files to the PC, where you can work with them by any means - for example, connect to the emulator, archive and put them somewhere for storage, send them to a conference, etc. It is also not particularly difficult to copy some material found on the Internet onto a card, move it to the controller and use this material on the PDP-11/DVK/UKNC. | ||
8 | |||
9 | == Controller registers == | ||
10 | |||
11 | The controller has 4 registers on the QBUS | ||
12 | |||
13 | * 177220 - Command and Status Register (CSR) | ||
14 | * 177222 - Data Register (DR) | ||
15 | * 177224 - Primary Boot Register (BOOT1) | ||
16 | * 177226 - Alternate Boot Register (BOOT2) | ||
17 | |||
18 | The CSR register accepts commands in bits D0-D5 and the interrupt enable bit in bit D6, all write only, always zero is read. In bit D7, the readiness bit is read. One in it means that the previous command has been executed and the controller is ready for exchange. Zero means that the controller is busy executing the previous operation, the other registers are disabled, access to any of them will cause Trap to 4. If the execution of the previous command caused an error, bit D15 is set simultaneously with bit D7. | ||
19 | \\Writing to registers is done only in words; byte writing is not allowed. | ||
20 | \\All data exchange is conducted via DR. For commands with a single-word argument, this argument should be sent to DR, and then the command should be sent to CSR. For commands exchanging with the controller buffer, on the contrary, the command should be issued and only after it a data block of a certain length should be received or transmitted. | ||
21 | |||
22 | == Interruptions == | ||
23 | |||
24 | Most commands are executed almost instantly - within the execution time of one or two CPU commands. But the commands for exchanging with the micro-SD card still take time. You can wait for these operations to complete either by polling the D7 CSR bit, or (for example, if you have a multitasking OS that has somewhere to utilize this time) - set the D6 CSR bit (interrupt enable) and do something else. When the operation is completed, an interrupt will occur and you can continue operations on it. | ||
25 | |||
26 | == Namespaces == | ||
27 | |||
28 | As already mentioned, the disk device presented to the PDP-11 system is a physical file on some file system. Starting with the V17 firmware, network functionality has appeared, it allows using network drives provided by MAXIOL Landisk® technology. Accordingly, for complete unification and ensuring complete "transparency", all differences are reduced to the mount point, at the moment these are the following spaces: | ||
29 | |||
30 | * 0:/ - local files on the MicroSD card | ||
31 | * R:/ - network files in the file repository | ||
32 | * N:/ - network files in the archive [[https:~~/~~/mirrors.pdp-11.ru/>>https://mirrors.pdp-11.ru/]] | ||
33 | |||
34 | Planned network spaces (functionality will be implemented in the following firmware): | ||
35 | |||
36 | * P:/ - personal cloud, available only under one account | ||
37 | * S:/ - cloud that allows you to share your files with other members | ||
38 | |||
39 | == Buffers == | ||
40 | |||
41 | The controller has several buffers for different purposes, all buffers have word sizes because they are transmitted through a 16-bit register. | ||
42 | |||
43 | * Main buffer - IOBUF [258] | ||
44 | * Non-volatile memory buffer - cmosmem_buffer[256] | ||
45 | * Clock buffer - output timestamp_out_buffer[14] | ||
46 | * Clock buffer - input timestamp_in_buffer [7] | ||
47 | * IP information buffer - ipdata | ||
48 | * Map size buffer - sizecard | ||
49 | |||
50 | = Main command block = | ||
51 | |||
52 | The command is sent to the CSR, to bits 5-0. It should be sent only as a word, byte writing is not allowed. The command bits are for writing only, always zero is read. The command codes are given in octal. | ||
53 | |||
54 | These commands use the main buffer for their operation (IOBUF [258]). | ||
55 | |||
56 | == 000: Controller reset == | ||
57 | |||
58 | The command code is 000. The command stops all controller operations, if possible. Its completion should be waited for by polling. Example program: | ||
59 | |||
60 | {{code language="assembler"}} | ||
61 | ;............................. | ||
62 | MOV #AZ$CSR,R3 | ||
63 | 1$: CLR @R3; Send the "Reset" command | ||
64 | TSTB @R3; Check the controller's readiness | ||
65 | BPL 1$; If it's not ready, reset it again | ||
66 | ; and check again | ||
67 | TST (R3)+; Check for an error, | ||
68 | ; change the address at the same time | ||
69 | ; (will be useful later) | ||
70 | BMI ERR1 | ||
71 | ;............................... | ||
72 | {{/code}} | ||
73 | |||
74 | Note on the reset command. In fact, it is instantaneous - if the controller is not busy executing an operation that cannot be interrupted, specifically - SD data exchange. While the SD data exchange is in progress, the controller does not accept any commands and the reset command can be skipped. Therefore, if the controller is busy (bit D7 is zero), the reset command is issued again. This happens quite rarely (for example, double-clicking Ctrl/C when rewriting), usually, during normal operations, the wait for writing or reading is performed in a special place and, upon completion of this operation, not a reset is performed, but completely different actions. | ||
75 | |||
76 | |||
77 | == 001: Select device == | ||
78 | |||
79 | The controller supports up to 32 pseudo disks. The "device selection" command selects one of them for operation. Command code 001. To select a device, you should send the number of the drive you are going to work with to the data register (177222) and then send the code 001 to the CSR. The command is executed instantly, i.e. during the time the CPU sends the "device selection" code. When attempting to select an AZ disk that is not assigned an image file, an error is returned in bit D15 of the CSR register. | ||
80 | |||
81 | Пример программы: | ||
82 | |||
83 | {{code language="assembler"}} | ||
84 | ;....................... | ||
85 | |||
86 | SetUni = 001; Symbolic name of the command | ||
87 | ; "Device selection" | ||
88 | |||
89 | ; The address CSR+2=DR remained in R3 from the previous fragment | ||
90 | |||
91 | ; We assume that in R0 in bits 0-3 there is the number of the | ||
92 | ; device, the remaining bits are zeros, the procedure for ;calculating this number is not shown. | ||
93 | |||
94 | MOV R0,@R3; Let's send to DR the number of disk AZ, with | ||
95 | ; which we are going to work with | ||
96 | |||
97 | MOV #SetUni,-(R3); and send the command "Drive selection", with the correction of the address in R3, which | ||
98 | ; now points to CSR again. | ||
99 | |||
100 | TST (R3)+; Let's check for an error and again | ||
101 | ; move the address in R3 to DR | ||
102 | BMI ERR2 | ||
103 | |||
104 | ;........................ | ||
105 | {{/code}} | ||
106 | |||
107 | |||
108 | == 002: Set block number, low bits of block number == | ||
109 | |||
110 | The controller provides the machine with QBUS as disks AZ0 - AZ7 file-images of the DSK type on the micro-SD card. The size of these file-images and, accordingly, pseudo-disks, can be any, up to 4G each. Addressing on these pseudo-disks is direct - the block number received via QBUS, after shifting, is used as an offset from the beginning of the corresponding file-image. In fact, this is something like LBA on a PC. | ||
111 | \\There are PDP-11 operating systems that support such disks - RSX-11, DIAMS, and some others. However, the most common OS - RT-11 - uses a WORD (16 bits) for the block number, and the code 0177777 is used in some places for special purposes and is not suitable as a disk size, so disks with a maximum number of blocks of 0177776, i.e. 65534 blocks (33553408 bytes or 32767 K bytes), can be used for RT-11. Therefore, there are two commands for setting the block number: to set the low-order bits of the block number - code 002 and to set the high-order bits of the block number - code 012. If the block number fits into 16 bits (for RT-11 - always), it is enough to use the command to set the low-order bits of the block number, the high-order bits are cleared. If the number does not fit into 16 digits, then first the lower bits must be output, and then the higher ones. If you try to immediately transmit the higher bits without first transmitting the lower ones, an error is returned. If the transmitted address goes beyond the file-image boundary, an error is also returned, no matter at what stage - either when transmitting the lower 16 bits of the block number, or when transmitting the higher ones. | ||
112 | |||
113 | |||
114 | To perform these actions, you must send the required part of the block number bits to DR and then send the command code to CSR, after which you must check for an error. The commands are instantaneous, i.e. they are executed in one QBUS access cycle. | ||
115 | \\Example of a program - 16-bit version block number: | ||
116 | |||
117 | {{code language="assembler"}} | ||
118 | ;....................................... | ||
119 | |||
120 | SetBlk=002; Symbolic name of the command | ||
121 | ; "Set the lower 16 bits of the block number" | ||
122 | |||
123 | ; The address DR | ||
124 | ; (177222) remained in R3 from the previous fragment | ||
125 | ; We assume that the cell labeled BLCUR contains the 16- | ||
126 | ; bit disk address (the block number to be | ||
127 | ; input or output). The procedure for obtaining this number is not | ||
128 | ; shown | ||
129 | |||
130 | MOV BLCUR,@R3;We place the block number to be exchanged in DR. | ||
131 | |||
132 | |||
133 | MOV #SetBlk,-(R3); We send the command to the CSR | ||
134 | ; do not forget that the address in R3 will decrease by | ||
135 | ; 2 before sending and will remain so | ||
136 | |||
137 | TST @R3; We check for an error | ||
138 | |||
139 | BMI ERR3 | ||
140 | |||
141 | ; In the 32-bit version, the same actions should be | ||
142 | ; repeat for the senior 16 bits (actually, | ||
143 | ; senior 7, the rest should be zeros, because the maximum | ||
144 | ; size of a pseudo disk is 4G) of the disk address. | ||
145 | |||
146 | ; Note that in R3 the CSR address remains, and not | ||
147 | ; DR, as in the two previous fragments. This is done | ||
148 | ; on purpose. | ||
149 | |||
150 | ;....................................... | ||
151 | {{/code}} | ||
152 | |||
153 | |||
154 | == 003: Open HFS Table of Contents == | ||
155 | |||
156 | Sequence of actions: | ||
157 | |||
158 | • reset the controller | ||
159 | • send the command "Accept data block to buffer" to the CSR and transmit the entire line with the full path text (Full Path) to the required table of contents word by word. The line must end with a zero byte (0x00) and be no longer than 384 bytes (192 words). | ||
160 | • send the "Open table of contents" command code to the CSR | ||
161 | • wait for it to finish (the command is long) | ||
162 | • check for errors | ||
163 | |||
164 | [[Example utility - AZDIR>>url:https://forum.maxiol.com/index.php?s=&showtopic=5605&view=findpost&p=59418]] | ||
165 | |||
166 | |||
167 | Example program: | ||
168 | |||
169 | {{code language="assembler"}} | ||
170 | ;............................. | ||
171 | AZ$CSR = 177220 | ||
172 | WrBuf = 016 | ||
173 | OpnDir = 003 | ||
174 | |||
175 | MOV #AZ$CSR,R3 | ||
176 | ; The DirPtr cell contains a pointer to the beginning of the field with Full | ||
177 | ; Path. We assume that the string is terminated by three zero | ||
178 | ; bytes in order to recognize the end of the transfer of the | ||
179 | ; string word by word by zero. Indeed, if the number of | ||
180 | ; characters in the string is even, then the next two bytes of zeros | ||
181 | ; form a zero word; if it is odd, then one zero | ||
182 | ; will go away with the last character of the string, and the zero | ||
183 | ; word is formed by the second and third zero bytes, | ||
184 | ; ending the string. That is, such an end of the string, | ||
185 | ; transferred word by word, is quite reliable. | ||
186 | MOV DirPtr,R2 | ||
187 | |||
188 | MOV #WrBuf,(R3)+; We issue the "Write to | ||
189 | ; buffer" command and transmit the string | ||
190 | 11$: MOV (R2)+,@R3; word by word, | ||
191 | BNE 11$ ;until zero is sent, | ||
192 | |||
193 | MOV #OpnDir,-(R3); We issue the "Open | ||
194 | ; table of contents" command | ||
195 | |||
196 | 12$: TSTB @R3; and wait for the controller to | ||
197 | BPL 12$; execute it, | ||
198 | TST @R3; after which we check for an error. | ||
199 | BMI Err10; Error --> | ||
200 | ;............................. | ||
201 | {{/code}} | ||
202 | |||
203 | == 004: Mount a disk == | ||
204 | |||
205 | Sequence of actions: | ||
206 | • Reset the controller | ||
207 | • Send a line to the controller with a record similar to the lines describing the disks in the AZ.INI file | ||
208 | • Issue the "Mount disk" command | ||
209 | • Wait for it to finish (the command is long) | ||
210 | • Check for errors | ||
211 | The selected AZnn drive MUST NOT have a disk mounted. If it is mounted, the old disk should be unmounted before mounting a new one - command 014 | ||
212 | |||
213 | Example program: | ||
214 | |||
215 | {{code language="assembler"}} | ||
216 | ;.................. | ||
217 | AZMNT = 004 | ||
218 | MDLEN = MDEND-MDTXT+2 | ||
219 | |||
220 | MOV #AZ$CSR,R3 | ||
221 | |||
222 | 20$: CLR @R3; | ||
223 | TSTB @R3; Reset the controller | ||
224 | BPL 20$; | ||
225 | |||
226 | MOV #WrBuf,(R3)+; Send it a string | ||
227 | MOV #MDTXT,R2; with the assignment command | ||
228 | MOV #MDLEN/2,R1; | ||
229 | 21$: MOV (R2)+,@R3; | ||
230 | SOB R1,21$; | ||
231 | |||
232 | MOV #AZMNT,-(R3); and pass it to | ||
233 | 22$: TSTB @R3 ; execution | ||
234 | BPL 22$ ; | ||
235 | |||
236 | TST @R3; then check for an error | ||
237 | BMI Err11; | ||
238 | ;......................... | ||
239 | |||
240 | MDTXT: .ASCII "D04=0:/DISKS/SYSTEM/51SYS_DS.DSK" | ||
241 | MDEND: .BYTE 0,0 | ||
242 | ;.................. | ||
243 | {{/code}} | ||
244 | |||
245 | In this fragment, the 51SYS_DS.DSK image file is mounted on the AZ4 disk, located in the SYSTEM folder, which is located in the DISKS folder, which is located in the root directory of the micro-SD card. | ||
246 | |||
247 | |||
248 | == 005: Reading block into buffer == | ||
249 | |||
250 | The controller has a built-in buffer for 256 words (512 bytes). In fact, this is part of the STM32 microcontroller's RAM, allocated in its program for this buffer. All exchange in the main command block goes through this buffer. | ||
251 | \\The AZ disk memory is represented as a set of blocks, each 512 bytes in size. Such a block is the only available unit for exchanging data with AZ disks. The blocks are numbered from zero to 65533 for the 16-bit version or up to 8388607 for the 32-bit version - this is when using AZ drives of the maximum permissible capacity. No one prevents you from using drives of a smaller capacity - the actual size of the drive is equal to the size of the file-image mounted on this drive. An error will be registered if you try to access beyond the file-image. | ||
252 | \\Command 005 - reading a block from MicroSD to the buffer. From the pseudo-disk AZn, previously selected by the command "Select device", the block whose number is transmitted by the command (commands) "Set block number" is started for reading. Long-term command. | ||
253 | \\In fact, a block from a MicroSD card is read in about 500-800 µs. During this time, the controller goes into a state that during the discussion of the project was called "I think, please do not interfere." Namely, for the entire time of its execution, all device registers are disabled, except for CSR, in which zero is read as long as the controller is busy executing this command. After the block is read, the remaining controller registers are connected to the QBUS, bit D7 (ready) is set in CSR and, if bit D6 (interrupt enable) was set in CSR, an interrupt with vector 0174 is generated. | ||
254 | \\An example without interruptions is trivial: | ||
255 | |||
256 | {{code language="assembler"}} | ||
257 | ;................................... | ||
258 | |||
259 | CmdRea=005; symbolic name of the "Read block" command | ||
260 | |||
261 | ; In R3 we have the CSR address left from the previous fragment. | ||
262 | ; We send the read command code there | ||
263 | MOV #CmdRea,@R3 | ||
264 | 2$: TSTB @R3 Let's check the ready bit | ||
265 | BPL 2$; Not ready -> we go to check again | ||
266 | ; once | ||
267 | TST @R3; Let's check for an error | ||
268 | BMI ERR4 | ||
269 | ; Here again, unlike the fragments of pp. 3.1 and | ||
270 | ; 3.2, we have the CSR address left in R3, not DR. | ||
271 | |||
272 | ;................................... | ||
273 | {{/code}} | ||
274 | |||
275 | |||
276 | == 006: Write block from buffer to disk == | ||
277 | |||
278 | Command code 006. The buffer contents are written to the selected pseudo-disk at the specified disk address (block number). Before writing, checks are performed (1) "was there a write to the buffer?", if not, an error is returned and (2) "is the buffer completely filled?", if not (for the last shortened block of the file), the rest of the buffer is cleared with zeros. Then the block is written to the media. The operation is lengthy, after it is started the controller, as with reading, goes into the "I think, please do not interfere" state. And just as with reading, you must wait for the end of this operation, by the same means as with reading. | ||
279 | |||
280 | |||
281 | Example of a program without interrupts: | ||
282 | |||
283 | {{code language="assembler"}} | ||
284 | ;............................................................... | ||
285 | |||
286 | CmdWri=006; symbolic name of the "Write | ||
287 | ; block" command | ||
288 | |||
289 | ; In R3 we have the DR address left over from the previous fragment. | ||
290 | ; We correct it to the CSR and send the | ||
291 | ; write command code there | ||
292 | MOV #CmdWri,-(R3) | ||
293 | 5$: TSTB @R3 ; Let's check the ready bit | ||
294 | BPL 5$; Not ready -> we go and check again once | ||
295 | TST @R3; Let's check for an error | ||
296 | BMI ERR5 | ||
297 | |||
298 | ;.................................................................... | ||
299 | {{/code}} | ||
300 | |||
301 | |||
302 | == 007: Get disk size == | ||
303 | |||
304 | There are two commands for obtaining the size of a pseudo-disk, i.e. the AZn file-image mounted on the selected pseudo-drive. | ||
305 | \\If the OS being used (or a program working with disks without an OS) can work with large (more than 32M) disks, you should use the command with the code 017. The sequence of actions: reset the controller (command 000), select the drive (p. 3.2) and send the code 017 to the CSR, and then, without any waiting, read from DR first the lower word, and then the higher word of the size of the selected drive (image file). | ||
306 | \\If the OS you are using cannot work with disks larger than 32M (RT-11), you should use the 007 command - get the pseudo-disk size with a limit of up to 32M. The steps are similar: reset the controller, select the disk, send the 007 code to the CSR and read one word of the pseudo-disk size from DR. If the size of the image file mounted on the selected pseudo-drive is larger than 65534 blocks, the controller returns the number 65534 instead of this "large" size. We remind you that the number 65535 is used in some places for special purposes and cannot be the disk size. | ||
307 | \\We also remind you that if the image file is not mounted on this drive, the sequence of actions will not work (command 001 select device) and the program execution will simply not reach this point. Therefore, these commands do not provide for errors. | ||
308 | |||
309 | Example of a program with "small" disks | ||
310 | |||
311 | {{code language="assembler"}} | ||
312 | ;...................................... | ||
313 | |||
314 | GetSiz=007; Get the "small" disk size | ||
315 | |||
316 | ; From fragment 3.2 (disk selection) we have in R3 | ||
317 | ; DR address (177222) | ||
318 | |||
319 | MOV #GetSiz,-(R3); send the command | ||
320 | TST (R3)+; return the address in R3 back to DR | ||
321 | MOV @R3,DskSiz | ||
322 | ;...................................... | ||
323 | {{/code}} | ||
324 | |||
325 | == 010: Allow network operation == | ||
326 | |||
327 | Command code 010. Having completed the sequence of actions for transferring the next portion of data, and expecting that the next request will not follow immediately, you can "utilize" the processor time of the STM32 microcontroller, which is the basis of AZ - occupy it with servicing the network. In the same RT-11, this can be done before exiting the AZ driver, before the .DRFIN macro command, which completes the execution of the input-output request. | ||
328 | \\Indeed, the I/O operation is completed, the CPU program in the system will prepare a new portion of data for output, or figure out (based on the previously read data) where it should read something else, or generally think about something of its own. smile.gif In other words, after the I/O request is completed, there is a fairly high probability that there will be a pause in working with the AZ disks. So, the time of this pause can be given to servicing the network. To do this, before executing the .DRFIN macro in RT-11 or its analogue in other OS, you should send code 110 (enable network plus enable interrupts) to the CSR. | ||
329 | |||
330 | |||
331 | In this case, the interrupt will not occur, it is activated only upon completion of "long" operations that transfer the controller to the "Thinking, please do not interfere" state, and the interrupt enable trigger set to "1" also enables network operation if it is activated. When the next input-output operation is started, the actions in paragraph 3.1 (controller reset) will also reset this trigger, after which the network service program, having detected the reset of this trigger, will stop (suspend) its work and return control to the main disk service program AZ. The maximum that can be noticed from the CPU side is a small (10-20 µs) delay in executing the reset command, but this is a very reasonable price for network capabilities. | ||
332 | |||
333 | == 011: Get AZn drive assignment table == | ||
334 | |||
335 | Command code 011. Upon receiving this command, the controller switches from the block buffer to its internal assignment table (32 lines of 140 bytes each)*. Before issuing this command, the controller should be reset. After issuing this command, command 015 (read buffer) should be issued, but in this case it will not be the buffer that is read, but the same table, sequentially, word by word. | ||
336 | \\* starting with v17, the file name length is no longer 130 bytes, but 386 bytes (the last word is zero, to end the line) | ||
337 | |||
338 | [[AZSMNT utility example>>url:https://forum.maxiol.com/index.php?s=&showtopic=5605&view=findpost&p=59420]] | ||
339 | |||
340 | |||
341 | Example program: | ||
342 | |||
343 | {{code language="assembler"}} | ||
344 | ;................................... | ||
345 | AZ$CSR = 177220; Controller CSR | ||
346 | RdBuf = 012; Command "Read from controller memory" | ||
347 | RdTbl = 011; Command "Read assignment table" | ||
348 | TblSiz = 1120.; Table length in bytes (decimal) | ||
349 | |||
350 | ; We assume that R2 contains the address of the first word of the | ||
351 | ; memory area for the assignment table. We do not show the procedure for obtaining this address. | ||
352 | |||
353 | MOV #AZ$CSR,R3; Preparing the controller CSR | ||
354 | 10$: CLR @R3; | ||
355 | TSTB @R3; Reset the controller | ||
356 | BPL 10$; | ||
357 | |||
358 | MOV #RdTbl,@R3;Command "Transfer | ||
359 | ; table" | ||
360 | |||
361 | MOV #RdBuf,(R3+); Command "Read from | ||
362 | ; controller memory. At the same time, move the address in R3 | ||
363 | ; to the DR of the controller (177222). | ||
364 | |||
365 | MOV #TblSiz/2,R1; Prepare the word counter | ||
366 | |||
367 | 11$: MOV @R3,(R2)+; Send the current word | ||
368 | SOB R1,11$; and repeat 560 times | ||
369 | ;................................... | ||
370 | {{/code}} | ||
371 | |||
372 | == 012: Setting the block number, block number high bits == | ||
373 | |||
374 | The controller provides the machine with MPI as disks AZ0 - AZ7 file-images of the DSK type on the micro-SD card. The size of these file-images and, accordingly, pseudo-disks, can be any, up to 4G each. Addressing on these pseudo-disks is direct - the block number obtained by QBUS, after shifting, is used as an offset from the beginning of the corresponding file-image. In fact, this is something like LBA on a PC. | ||
375 | |||
376 | |||
377 | There are PDP-11 operating systems that support such disks - RSX-11, DIAMS, and some others. However, the most common OS - RT-11 - uses a WORD (16 bits) for the block number, and the code 0177777 is used in some places for special purposes and is not suitable as a disk size, so disks with a maximum number of blocks of 0177776, i.e. 65534 blocks (33553408 bytes or 32767 K bytes), can be used for RT-11. Therefore, there are two commands for setting the block number: to set the low-order bits of the block number - code 002 and to set the high-order bits of the block number - code 012. If the block number fits into 16 bits (for RT-11 - always), it is enough to use the command to set the low-order bits of the block number, the high-order bits are cleared. If the number does not fit into 16 digits, then first the lower bits must be output, and then the higher ones. If you try to immediately transmit the higher bits without first transmitting the lower ones, an error is returned. If the transmitted address goes beyond the file-image boundary, an error is also returned, no matter at what stage - either when transmitting the lower 16 bits of the block number, or when transmitting the higher ones. | ||
378 | \\To perform these actions, you should send the required part of the block number bits to DR and then send the command code to CSR, after which you should check for an error. The commands are instantaneous, i.e. they are executed in one cycle of access via MPI. | ||
379 | |||
380 | == 013: Read HFS TOC entry == | ||
381 | |||
382 | Command code 013, the command reads the TOC record into the internal memory area and switches the pointer to it for data transfer via DR. The TOC must be open before this. | ||
383 | |||
384 | The procedure is as follows: | ||
385 | • Reset the controller. | ||
386 | • Issue the command "Read the table of contents entry" to the CSR and wait for it to finish. | ||
387 | • Issue the command "Read from controller memory" to the CSR | ||
388 | • Read 11 words of the table of contents entry from DR | ||
389 | \\The table of contents entry has the format: | ||
390 | |||
391 | (% style="width:686px" %) | ||
392 | |=(% style="width: 136px;" %)Offset (octal)|=(% style="width: 305px;" %)Name|=(% style="width: 242px;" %)Value | ||
393 | |(% style="width:136px" %)0|(% style="width:305px" %)fSize|(% style="width:242px" %)File size in bytes, low word | ||
394 | |(% style="width:136px" %)2|(% style="width:305px" %)fSize|(% style="width:242px" %)File size in bytes, high word | ||
395 | |(% style="width:136px" %)4|(% style="width:305px" %)fDate|(% style="width:242px" %)Date in MS-DOS format | ||
396 | |(% style="width:136px" %)6|(% style="width:305px" %)fTime|(% style="width:242px" %)Time in MS-DOS format | ||
397 | |(% style="width:136px" %)10|(% style="width:305px" %)fAttr|(% style="width:242px" %)Attributes 1 byte | ||
398 | |(% style="width:136px" %)10|(% style="width:305px" %)fName|(% style="width:242px" %)NAME.FILE TYPE, 8+1+3+1 = 13 bytes | ||
399 | |||
400 | |||
401 | The offsets are specified in octal. The formula in the fName line means that there must first be a name, maximum of eight characters, then a period, then a type, up to three characters, and a terminating zero byte 0x00. If the type is not specified, the period is also not needed. | ||
402 | \\File attributes in fAttr byte (octal): | ||
403 | \\001 - Read Only | ||
404 | 002 - Hidden | ||
405 | 004 - System | ||
406 | 020 - Directory | ||
407 | 040 - Archive | ||
408 | \\Example program | ||
409 | |||
410 | {{code language="assembler"}} | ||
411 | ;...................................... | ||
412 | RdDir = 013; command code "Read table of contents entry" | ||
413 | RdBuf = 015 | ||
414 | |||
415 | MOV #AZ$CSR,R3 | ||
416 | |||
417 | 15$: CLR @R3; | ||
418 | TSTB @R3; Reset the controller | ||
419 | BPL 15$; | ||
420 | |||
421 | MOV #RdDir,@R3; Ask the controller | ||
422 | 16$: TSTB @R3; to read into its memory | ||
423 | BPL 16$; table of contents entry | ||
424 | |||
425 | MOV @RdBuf,(R3)+; | ||
426 | MOV DIRREC,R2; And transfer it to itself in | ||
427 | MOV #11.,R1; memory area, pointer | ||
428 | 17$: MOV @R3,(R2)+; to which lies in cell | ||
429 | SOB R1,17$; DIRREC. | ||
430 | |||
431 | ;...................................... | ||
432 | {{/code}} | ||
433 | |||
434 | |||
435 | == 014: Unmount disk == | ||
436 | |||
437 | Command code 014, to unmount the disk, you should reset the controller, send the AZ drive number to the controller DR, which should be unmounted, and send the 014 code to the controller CSR, then wait for the operation to complete (it takes a long time) and check for an error. An error is issued if the drive has not been mounted. | ||
438 | [[AZUMNT utility example>>url:https://forum.maxiol.com/index.php?showtopic=5605&st=0&p=59418&#entry59418]] | ||
439 | |||
440 | |||
441 | == 015: Start transferring the read block == | ||
442 | |||
443 | The command code is 015. Having received this command, the controller is configured to output word by word the contents of the same built-in buffer for 256 words, which will be output sequentially through the DR register. No waiting is required, we simply send a word from DR to sequential memory cells 256 times, and that's it. If less than 256 words are needed (the last shortened block of the file), then the remainder can simply be discarded without reading, resetting the controller at the beginning of the next operation will also reset this remainder. | ||
444 | \\Example program: | ||
445 | |||
446 | {{code language="assembler"}} | ||
447 | ;.................................. | ||
448 | RdBuf=015; symbolic name of the command | ||
449 | |||
450 | ; In R3 from the previous fragment there is the address of the CSR | ||
451 | ; (177220) | ||
452 | |||
453 | ; We assume that in R2 we have the address of the first word | ||
454 | ; of memory, where the read block should be placed. | ||
455 | ; The program for obtaining this address is not given. | ||
456 | |||
457 | MOV #400,R1; Prepare the word counter | ||
458 | ; 0400 oct = 256 dec | ||
459 | |||
460 | MOV #RdBuf,(R3)+; and send the command | ||
461 | ; RdBuf to the CSR. The address in R3 will point to DR (177222). | ||
462 | |||
463 | 3$: MOV @R3,(R2)+we will send the next word to | ||
464 | ; memory | ||
465 | SOB R1,3$; and repeat this 256 (0400) | ||
466 | ; times | ||
467 | ;.................................. | ||
468 | {{/code}} | ||
469 | |||
470 | That's it, reading is complete. | ||
471 | |||
472 | To write the opposite way, you first need to transfer the entire data block from the CPU memory to the controller and then issue the command "Write the contents of the buffer to disk" | ||
473 | |||
474 | |||
475 | == 016: Receive data block into buffer == | ||
476 | |||
477 | Command code 016. The command sets the controller to receive a block of data and place it in the buffer. The next 256 write cycles to DR will place the data transferred via the QBUS in the buffer. | ||
478 | \\Example program: | ||
479 | |||
480 | {{code language="assembler"}} | ||
481 | ;.................................. | ||
482 | |||
483 | WrBuf=016; Symbolic name of the command | ||
484 | |||
485 | ; Before writing, you need to perform the same actions as in | ||
486 | ; pp. 3.1.-3.3. Usually, this is the same program, | ||
487 | ; just after point 3.3. a check is performed "What | ||
488 | ; is required: reading or writing?" and a branch is made to the | ||
489 | ; reading or writing program. | ||
490 | |||
491 | ; After the fragment in point 3.3., the CSR address | ||
492 | ; (177220) remains in R3. We will assume that R2 contains the address in the CPU | ||
493 | ; memory where the block to be written is located. | ||
494 | ; The program for obtaining this address is not shown. | ||
495 | |||
496 | MOV #400,R1; Preparing the counter | ||
497 | |||
498 | MOV #WrBuf,(R3)+; Let's forward the command to the CSR and | ||
499 | ; switch the address in R3 to | ||
500 | ; DR | ||
501 | |||
502 | 4$: MOV (R2)+,@R3; Let's forward the next word | ||
503 | ; data | ||
504 | SOB R1,4$; and repeat this 256 times | ||
505 | ;.................................. | ||
506 | {{/code}} | ||
507 | |||
508 | == 017: Get ramdisk size, large == | ||
509 | |||
510 | There are two commands to get the size of a pseudo-disk, i.e. the AZn file-image mounted on the selected pseudo-drive. | ||
511 | \\If the OS being used (or a program working with disks without an OS) can work with large (more than 32M) disks, you should use the command with the code 017. The sequence of actions: reset the controller (p. 3.1), select the drive (p. 3.2) and send the code 017 to the CSR, and then, without any waiting, read from DR first the lower word, and then the higher word of the size of the selected drive (image file). | ||
512 | \\If the OS you are using cannot work with disks larger than 32M (RT-11), you should use the 007 command - get the pseudo-disk size with a limit of up to 32M. The steps are similar: reset the controller, select the disk, send the 007 code to the CSR and read one word of the pseudo-disk size from DR. If the size of the image file mounted on the selected pseudo-drive is larger than 65534 blocks, the controller returns the number 65534 instead of this "large" size. We remind you that the number 65535 is used in some places for special purposes and cannot be the disk size. | ||
513 | \\We also remind you that if the image file is not mounted on this drive, the sequence of actions will not work (command 001 select device) and the program execution will simply not reach this point. Therefore, these commands do not provide for errors. | ||
514 | \\Example of a program with large disks | ||
515 | |||
516 | {{code language="assembler"}} | ||
517 | ;...................................... | ||
518 | |||
519 | GetBig=017; Get the "big" disk size | ||
520 | |||
521 | ; From fragment 3.2 (disk selection) we have in R3 | ||
522 | ; DR address (177222) | ||
523 | |||
524 | MOV #GetBig,-(R3); send the command | ||
525 | TST (R3)+; return the address in R3 back to DR | ||
526 | MOV @R3,BigSiz | ||
527 | MOV @R3,BigSiz+2 | ||
528 | ;...................................... | ||
529 | {{/code}} | ||
530 | |||
531 | |||
532 | == 020: Get extended diagnostic code == | ||
533 | |||
534 | Command code 020, after resetting the controller, you should issue this command in the CSR and then read two words of extended diagnostics from DR. The command is instant, no waiting is required. | ||
535 | |||
536 | |||
537 | == 027: Get firmware version AZ STM32 == | ||
538 | |||
539 | Command code 027, returns 2 words | ||
540 | \\first word - 06404 = high byte 13. this is the firmware version, low byte 4. this is the hardware version - i.e. AZБК in this case second word - 037 = this is the maximum mountable disk - 31. | ||
541 | |||
542 | |||
543 | {{code language="assembler"}} | ||
544 | ;------------------------------------------------------------- | ||
545 | ; getting STM32 firmware version - result in R1 R1=0 error | ||
546 | GTSTMV: MOV #AZ$CSR,R1 | ||
547 | 1$: CLR (R1) ; Send "Reset" command | ||
548 | TSTB (R1) ;Check controller readinessконтроллера | ||
549 | BPL 1$ ; If not ready, reset again | ||
550 | mov #27,(R1) | ||
551 | TST (R1)+ ; Check for error | ||
552 | BMI 2$ | ||
553 | mov (R1),R1 | ||
554 | return | ||
555 | 2$: CLR R1 | ||
556 | return | ||
557 | ;------------------------------------------------------------- | ||
558 | {{/code}} | ||
559 | |||
560 | |||
561 | == 030: No operation == | ||
562 | |||
563 | The main purpose of this command is to set the interrupt enable bit from the controller. The command transfers the interrupt enable bit, which is in the same word with it, but is not part of it (remember, the command is located in bits D0 - D5, and the interrupt enable bit is D6), to the corresponding trigger of the controller and does not affect the processes in the controller in any other way. Control of this trigger works even in the "Thinking, please do not interfere" state, and this is the main feature of the "no operation" command. | ||
564 | \\The command has the code 0030. Sending the code 0130 to the CSR will enable interrupts from the controller, sending the code 0030 will disable them. An example is not given due to its triviality. | ||
565 | |||
566 | |||
567 | = Command block for working with non-volatile memory = | ||
568 | |||
569 | The interface provides any AZ controller with access to 255 words of non-volatile memory, all commands set the ready bit upon completion. This allows you to save user settings in non-volatile memory, for example, this is used in AZBK ??- there are saved settings for more comfortable operation of the controller. | ||
570 | |||
571 | All commands in this block use a non-volatile memory buffer for their operation. | ||
572 | |||
573 | == 021: Read non-volatile memory block into buffer == | ||
574 | |||
575 | (% class="wikigeneratedid" %) | ||
576 | Command code 021, this command causes a block of non-volatile memory to be read into the non-volatile memory buffer. | ||
577 | |||
578 | |||
579 | == 022: Transfer the read block of non-volatile memory from the buffer to the bus == | ||
580 | |||
581 | (% class="wikigeneratedid" %) | ||
582 | Command code 022, this command ensures that the non-volatile memory buffer is transferred to the DR register for reading. | ||
583 | |||
584 | (% class="wikigeneratedid" %) | ||
585 | Example program | ||
586 | |||
587 | {{code language="assembler"}} | ||
588 | AZ$CSR = 177220; command and status register (CSR) | ||
589 | AZ$DR = 177222; data register (DR) | ||
590 | |||
591 | |||
592 | ; trap 50 - reset AZ | ||
593 | ; результат в R1 =0 ok | ||
594 | AZreset: MOV #AZ$CSR,R1 | ||
595 | 1$: CLR (R1); Send the "Reset" command | ||
596 | TSTB (R1); Check the controller readiness | ||
597 | BPL 1$; If not ready, reset again | ||
598 | ; once and check again | ||
599 | TST (R1); Check for an error, | ||
600 | BMI 0ERR$ | ||
601 | CLR R1 | ||
602 | return | ||
603 | 0ERR$: CLR R1 | ||
604 | COM R1 | ||
605 | return | ||
606 | |||
607 | |||
608 | ; trap 54 - reading non-volatile memory of block 1 EEPROM to the buffer from the address ADREEPROMMEM | ||
609 | ; result R3 - address, if R3=0 error | ||
610 | ; read status in R1 0 - ok | ||
611 | ; 1 - size does not match saved | ||
612 | ; 2 - version error | ||
613 | ; 3 - checksum error | ||
614 | ReadEEPROM: push R2 | ||
615 | call AZreset; reset | ||
616 | tst R1 | ||
617 | bne 0ERR$ | ||
618 | ; теперь читаем | ||
619 | MOV #AZ$CSR,R1 | ||
620 | mov #21,(R1); read block 1 of non-volatile memory into buffer | ||
621 | 0$: TSTB (R1); check execution result | ||
622 | BPL 0$; wait | ||
623 | mov #22,(R1); send read block of non-volatile memory from buffer to bus | ||
624 | 1$: TSTB (R1); check execution result | ||
625 | BPL 1$; wait | ||
626 | TST (R1)+; increment | ||
627 | mov #ADREEPROMMEM,R3 | ||
628 | mov #256.,R2; read 256. words; first word is reading result | ||
629 | 2$: mov (R1),(R3)+; read block of words into memory | ||
630 | sob R2,2$ | ||
631 | mov #ADREEPROMMEM,R3; successful | ||
632 | mov (R3),R1 | ||
633 | br 0END$ | ||
634 | 0ERR$: CLR R3 | ||
635 | 0END$: pop R2 | ||
636 | return | ||
637 | {{/code}} | ||
638 | |||
639 | obviously, after reading the memory, it is necessary to check the result code in the first word - see the decoding of error codes | ||
640 | \\Examples of returned data for commands | ||
641 | \\sequentially issuing the command 021 and then 022 will allow reading 256 words from non-volatile memory | ||
642 | Attention! The first word will be the reading success status | ||
643 | |||
644 | * 0 - ok | ||
645 | * 1 - size does not match saved | ||
646 | * 2 - version error | ||
647 | * 3 - checksum error | ||
648 | |||
649 | == 023: Receive data from the bus into the buffer for subsequent writing into the buffer == | ||
650 | |||
651 | Command code 023, this command allows you to fill the non-volatile memory buffer | ||
652 | |||
653 | == 024: Write from buffer to non-volatile memory block == | ||
654 | |||
655 | Command code 024, this command causes a non-volatile memory block to be written from the non-volatile memory buffer. | ||
656 | |||
657 | Example program | ||
658 | |||
659 | {{code language="assembler"}} | ||
660 | AZ$CSR = 177220; Command and Status Register (CSR) | ||
661 | AZ$DR = 177222; Data Register (DR) | ||
662 | |||
663 | |||
664 | ; trap 50 - reset AZ | ||
665 | ; результат в R1 =0 ok | ||
666 | AZreset: MOV #AZ$CSR,R1 | ||
667 | 1$: CLR (R1); Send the "Reset" command | ||
668 | TSTB (R1); Check the controller readiness | ||
669 | BPL 1$; If not ready, reset again | ||
670 | ; once and check again | ||
671 | TST (R1); Check for an error, | ||
672 | BMI 0ERR$ | ||
673 | CLR R1 | ||
674 | return | ||
675 | 0ERR$: CLR R1 | ||
676 | COM R1 | ||
677 | return | ||
678 | |||
679 | ; trap 55 - write non-volatile memory from the buffer at address ADREEPROMMEM в блок 1 EEPROM | ||
680 | WriteEEPROM: push R1 | ||
681 | push R2 | ||
682 | push R3 | ||
683 | call AZreset; reset | ||
684 | tst R1 | ||
685 | bne 0ERR$ | ||
686 | |||
687 | MOV #AZ$CSR,R1 | ||
688 | mov #23,(R1);command that we will write data to the buffer | ||
689 | 0$: TSTB (R1); check the result of executio | ||
690 | BPL 0$; wait | ||
691 | TST (R1)+; increment | ||
692 | mov #ADREEPROMMEM+2,R3 | ||
693 | mov #255.,R2; write 255. words; skip the first word - the result of reading | ||
694 | 1$: mov (R3)+,(R1); send to the controller | ||
695 | sob R2,1$ | ||
696 | tst -(R1); decrement | ||
697 | mov #24,(R1); write from the buffer to block 1 of non-volatile memory | ||
698 | 2$: TSTB (R1); check the result of execution | ||
699 | BPL 2$; we are waiting | ||
700 | br 0END$ | ||
701 | 0ERR$: CLR R3 | ||
702 | 0END$: pop R3 | ||
703 | pop R2 | ||
704 | pop R1 | ||
705 | return | ||
706 | {{/code}} | ||
707 | |||
708 | **Please note** that when recording, the buffer immediately comes with the data, i.e. there is no first word with the statu | ||
709 | |||
710 | |||
711 | |||
712 | = Block of commands for working with RTC and NTP = | ||
713 | |||
714 | The AZ® controller has 2 sources of date-time, the first is the RTC built into the STM32, the second is the clock in the TCP/IP stack. The RTC clock works autonomously with a 2032 battery installed. The clock in the TCP/IP stack is set based on data from the NTP server. | ||
715 | |||
716 | |||
717 | == Buffer format timestamp (readable) == | ||
718 | |||
719 | The controller API immediately prepares time in several formats, so that it can be conveniently used on the PDP-11 side | ||
720 | |||
721 | {{info}} | ||
722 | datetime buffer format octal offset - those words datetime buffer format | ||
723 | \\[0]=rtc_rt11date(); | ||
724 | [2]=rt11 time 50Hz big word; | ||
725 | [4]=rt11 time 50Hz little word; | ||
726 | [6]=rt11 time 60Hz big word; | ||
727 | [10]=rt11 time 60Hz little word; | ||
728 | [12]=rtc_fat_date(); | ||
729 | [14]=rtc_fat_time(); | ||
730 | [16]=year+2000; | ||
731 | [20]=month; | ||
732 | [22]=day; | ||
733 | [24]=wday; | ||
734 | [26]=hour; | ||
735 | [30]=min; | ||
736 | [32]=sec; | ||
737 | {{/info}} | ||
738 | |||
739 | |||
740 | == SimpleIN buffer format (when writing) == | ||
741 | |||
742 | the format is simplified as much as possible, for work with PDP-11 | ||
743 | |||
744 | {{info}} | ||
745 | offset in octal - those words | ||
746 | |||
747 | [0]=year, the lower two digits are 22 and not 2022(!) | ||
748 | [2]=month; month | ||
749 | [4]=day; day | ||
750 | [6]=wday; day of the week =0 not set, 1 - Monday 2 - Tuesday etc. | ||
751 | [10]=hour; hour | ||
752 | [12]=min; minute | ||
753 | [14]=sec; second | ||
754 | {{/info}} | ||
755 | |||
756 | |||
757 | == 031: Get time from RTC to timestamp buffer == | ||
758 | |||
759 | Command code 031, this command uses RTC clock as a source of filling the timestamp buffer | ||
760 | |||
761 | Example program: | ||
762 | |||
763 | {{code language="assembler"}} | ||
764 | ; trap 61 - reading clock data from autonomous RTC clock | ||
765 | ; R3 - buffer address where to read | ||
766 | ; result in R3 address if successful. R3=0 if error | ||
767 | GetDateFromRTC: push R0 | ||
768 | push R1 | ||
769 | push R2 | ||
770 | call AZreset; reset | ||
771 | tst R1 | ||
772 | bne G60ERR | ||
773 | MOV #AZ$CSR,R1 | ||
774 | mov #31,(R1) | ||
775 | br G60; let's go there because further code is the same | ||
776 | {{/code}} | ||
777 | |||
778 | == 032: Get time from timestamp buffer == | ||
779 | |||
780 | Command code 032, this command sends the contents of the timestamp buffer to the bus | ||
781 | |||
782 | {{code language="assembler"}} | ||
783 | ; working with clock | ||
784 | ; trap 60 - reading clock data from TCP/IP stack | ||
785 | ; R3 - buffer address where to read | ||
786 | ; result in R3 address if successful. R3=0 if error | ||
787 | GetDateFromLAN: push R0 | ||
788 | push R1 | ||
789 | push R2 | ||
790 | call AZreset; reset | ||
791 | tst R1 | ||
792 | bne G60ERR | ||
793 | MOV #AZ$CSR,R1 | ||
794 | mov #42,(R1) | ||
795 | G60: TSTB (R1); check execution result | ||
796 | BPL G60; wait | ||
797 | mov #32,(R1) | ||
798 | 1$: TSTB (R1); check execution result | ||
799 | BPL 1$; ждем | ||
800 | TST (R1)+; increment | ||
801 | mov R3,R0; remember R3 address | ||
802 | mov #10.,R2; read 10 words | ||
803 | 2$: mov (R1),(R3)+; read block of words into memory | ||
804 | sob R2,2$ | ||
805 | mov R0,R3; successful, return address to R3 | ||
806 | br 0END$ | ||
807 | G60ERR: CLR R3 | ||
808 | 0END$: pop R2 | ||
809 | pop R1 | ||
810 | pop R0 | ||
811 | return | ||
812 | {{/code}} | ||
813 | |||
814 | It is worth checking the correctness of the received time: | ||
815 | |||
816 | {{code language="assembler"}} | ||
817 | ; trap 63 - check time correctness | ||
818 | ; R3 - buffer address, result in R3, if buffer address then OK, =0 error | ||
819 | CheckDateTime: Cmp 6(r3),#2021. | ||
820 | Blos 1err | ||
821 | Cmp 6(r3),#2100. | ||
822 | Bhi 1err | ||
823 | 0ok$: return | ||
824 | 1err$: clr R3 | ||
825 | return | ||
826 | {{/code}} | ||
827 | |||
828 | == 033: Write time-date to SimpleIN buffer == | ||
829 | |||
830 | Command code 033, this command receives data from the bus into the SimpleIN buffer | ||
831 | |||
832 | The operation of this command is similar to the operation of commands 023 and 016. | ||
833 | |||
834 | == 034: Set RTC based on buffer data == | ||
835 | |||
836 | Command code 034, this command sets the RTC based on the data in the SimpleIN buffer | ||
837 | |||
838 | This command executes quickly, but to avoid problems, a wait loop is recommended. | ||
839 | |||
840 | == 035: Stimulate time request from NTP server, set based on response == | ||
841 | |||
842 | Command code 035, this command sends a request to the NTP server (set in the AZ.INI file or received from DHCP) and sets the clock in the TCP/IP stack. | ||
843 | |||
844 | Example program: sending a request to set the time from an NTP server | ||
845 | |||
846 | {{code language="assembler"}} | ||
847 | ; trap 62 - sending a request to set the time from the NTP server | ||
848 | GetDateNTPtoNET:push R1 | ||
849 | call AZreset; reset | ||
850 | tst R1 | ||
851 | bne 0ERR$ | ||
852 | MOV #AZ$CSR,R1 | ||
853 | mov #35,(R1) | ||
854 | 0$: TSTB (R1); check the result of execution | ||
855 | BPL 0$; wait | ||
856 | 0ERR$: pop R1 | ||
857 | return | ||
858 | {{/code}} | ||
859 | |||
860 | The command execution takes 1-2 seconds on average. This command requires the TCP/IP stack to work, so waiting cycles are needed when the stack is enabled. | ||
861 | |||
862 | An example of a polling cycle to get time from the network | ||
863 | |||
864 | {{code language="assembler"}} | ||
865 | ; date-time | ||
866 | mov #S_DateTime_0,R3; "Lan Date:" | ||
867 | trap 10 | ||
868 | mov #20,R4; number of wait cycles | ||
869 | $datry: trap 62; sent a request to the NTP server | ||
870 | mov #110,@#AZ$CSR; enable the network | ||
871 | trap 47; waiting | ||
872 | trap 47; waiting | ||
873 | mov #ADRTMPSTR,R3 | ||
874 | trap 60; read the date-time into the buffer | ||
875 | trap 63; checked the date-time | ||
876 | tst R3 | ||
877 | bne $ok | ||
878 | $sob: sob R4,$datry | ||
879 | mov #S_DateTime_2,R3; print error | ||
880 | trap 7 | ||
881 | br $go | ||
882 | |||
883 | $ok: mov #ADRTMPSTR,R3 | ||
884 | trap 24; print date | ||
885 | trap 25; time | ||
886 | $go: mov #110,@#AZ$CSR; let's turn on the network | ||
887 | {{/code}} | ||
888 | |||
889 | Here we explicitly send a request to the NTP server, then turn on the network and wait for the result, periodically polling and checking the correctness of the result. | ||
890 | |||
891 | == 036: Setting RTC based on TCP/IP stack clock == | ||
892 | |||
893 | Command code 036, this command sets the RTC based on the clock in the TCP/IP stack. You must first set the clock in TCP/IP - command 036. | ||
894 | |||
895 | Example program: | ||
896 | |||
897 | {{code language="assembler"}} | ||
898 | ; trap 64 - set RTC time based on stack time | ||
899 | ; R1 - result R1=0 - OK | ||
900 | SetDateNETtoRTC:call AZreset; reset | ||
901 | tst R1 | ||
902 | bne 0ERR$ | ||
903 | MOV #AZ$CSR,R1 | ||
904 | mov #36,(R1) | ||
905 | 0$: TSTB (R1); check execution result | ||
906 | BPL 0$; wait | ||
907 | clr R1 | ||
908 | 0ERR$: return | ||
909 | {{/code}} | ||
910 | |||
911 | == 042: Get time from TCP/IP stack clock into timestamp buffer == | ||
912 | |||
913 | Command code 042, this command uses the TCP/IP stack clock as a source for filling the timestamp buffer. | ||
914 | |||
915 | Example program: | ||
916 | |||
917 | {{code language="assembler"}} | ||
918 | ; working with clock | ||
919 | ; trap 60 - reading clock data from TCP/IP stack | ||
920 | ; R3 - buffer address where to read | ||
921 | ; result in R3 address if successful. R3=0 if error | ||
922 | GetDateFromLAN: push R0 | ||
923 | push R1 | ||
924 | push R2 | ||
925 | call AZreset; reset | ||
926 | tst R1 | ||
927 | bne G60ERR | ||
928 | MOV #AZ$CSR,R1 | ||
929 | mov #42,(R1) | ||
930 | G60: TSTB (R1); check execution result | ||
931 | BPL G60; wait | ||
932 | mov #32,(R1) | ||
933 | 1$: TSTB (R1); check execution result | ||
934 | BPL 1$; wait | ||
935 | TST (R1)+; increment | ||
936 | mov R3,R0; remember R3 address | ||
937 | mov #10.,R2; читаем 10 слов | ||
938 | 2$: mov (R1),(R3)+; read block of words into memory | ||
939 | sob R2,2$ | ||
940 | mov R0,R3; successful, return address to R3 | ||
941 | br 0END$ | ||
942 | G60ERR: CLR R3 | ||
943 | 0END$: pop R2 | ||
944 | pop R1 | ||
945 | pop R0 | ||
946 | return | ||
947 | {{/code}} | ||
948 | |||
949 | All commands set the ready bit upon completion. | ||
950 | |||
951 | |||
952 | = **[[AZБК®>>doc:Контроллеры AZБК® для компьютеров БК-0010 БК-0010\.01 БК-0011М.WebHome]] **specific commands = | ||
953 | |||
954 | These commands are intended for operation of the AZБК® controller, developed for the BK series of computers - BK-0010/BK-0010.01/BK-0011M. | ||
955 | |||
956 | Other AZ® controllers ignore these commands. | ||
957 | |||
958 | == 037: Restart of the** [[AZБК®>>doc:Контроллеры AZБК® для компьютеров БК-0010 БК-0010\.01 БК-0011М.WebHome]] **controller and the entire computer == | ||
959 | |||
960 | Command code 037, this command restarts the AZ® microcontroller, which also causes a restart of the BK-0010/BK-0010.01/BK-0011M itself | ||
961 | |||
962 | Example program | ||
963 | |||
964 | {{code language="assembler"}} | ||
965 | AZ$CSR = 177220; command and status register (CSR) | ||
966 | AZ$DR = 177222; data register (DR) | ||
967 | |||
968 | |||
969 | ; trap 57 - full restart | ||
970 | AZcouldReboot: call AZreset; reset AZ so it is ready to receive | ||
971 | mov #037,@#AZ$CSR | ||
972 | return | ||
973 | {{/code}} | ||
974 | |||
975 | == 044: Saving a screenshot to a file == | ||
976 | |||
977 | Command code 044, this command is designed to take a memory image of the specified size (or determined automatically based on saved parameters) technically, the command can serve as a debugging tool because it is capable of taking a memory image the general limitation on taking a memory image is 2MB per image | ||
978 | \\service memory page 76(8) is used as parameters | ||
979 | |||
980 | Structure of filling information about a screenshot | ||
981 | |||
982 | {{code language="c"}} | ||
983 | // screenshot header structure | ||
984 | typedef __packed struct screen_header | ||
985 | { | ||
986 | unsigned short int tag; // must be equal to 0240 | ||
987 | |||
988 | unsigned int begin_adress; // start address in words - forward task in physical addresses | ||
989 | unsigned int length; // length in words - forward task in physical addresses | ||
990 | |||
991 | unsigned short int begin_page; // start page - number - forward task in page numbers | ||
992 | unsigned short int len_pages; // number of pages - forward task in page numbers | ||
993 | |||
994 | unsigned short int R177300; // | ||
995 | unsigned short int R177302; // | ||
996 | unsigned short int R177304; // | ||
997 | unsigned short int R177306; // | ||
998 | unsigned short int R177310; // | ||
999 | unsigned short int R177312; // | ||
1000 | unsigned short int R177314; // | ||
1001 | unsigned short int R177316; // | ||
1002 | unsigned short int R177320; // | ||
1003 | unsigned short int R177322; // | ||
1004 | unsigned short int R177324; // | ||
1005 | unsigned short int R177326; // | ||
1006 | unsigned short int R177330; // | ||
1007 | unsigned short int R177332; // | ||
1008 | unsigned short int R177334; // | ||
1009 | unsigned short int R177336; // | ||
1010 | unsigned short int R177340; // - Window activation control register - window masks | ||
1011 | unsigned short int R177342; // - Control register r/o per window | ||
1012 | unsigned short int R177344; // - Shadow window control register - window masks | ||
1013 | unsigned short int R177346; // - Mapper control register | ||
1014 | unsigned short int R177350; // - copy by record register 177130 in memory management write mode in SMK | ||
1015 | unsigned short int R177352; // - copy by record register 177716 in memory management write mode in BK11M | ||
1016 | |||
1017 | unsigned short int R177230; // - control register | ||
1018 | unsigned short int R177232; // - display start page number register - upper page (layer 0) | ||
1019 | unsigned short int R177240; // - display start page number register - upper page (layer 1) | ||
1020 | unsigned short int R177242; // - display start page number register - upper page (layer 2) | ||
1021 | unsigned short int R177244; // - vertical scroll register layer 2 | ||
1022 | unsigned short int R177246; // - vertical scroll register layer 1 | ||
1023 | unsigned short int R177250; // - vertical scroll register layer 0 | ||
1024 | unsigned short int R177252; // - horizontal scroll register layer 0 | ||
1025 | unsigned short int R177254; // - horizontal scroll register layer 1 | ||
1026 | unsigned short int R177256; // - horizontal scroll register layer 2 | ||
1027 | |||
1028 | unsigned short int paldata[338]; // | ||
1029 | } screen_header_t; | ||
1030 | {{/code}} | ||
1031 | |||
1032 | Example code for filling a memory page | ||
1033 | |||
1034 | {{code language="assembler"}} | ||
1035 | ;-------------------------------------------------- | ||
1036 | ; 76th page map - we prepare data for the screenshot command there | ||
1037 | SCR_PAGE = 130000 ; we temporarily attach the 76th page to the 77th - that is, into the 130000 window | ||
1038 | SCR_TAG = SCR_PAGE+0 ; here is the input - 240 - 1 word | ||
1039 | SCR_ADDR_CONF = SCR_TAG+2 ; here is the command with addresses - address+length 24 bits - 4 words | ||
1040 | SCR_PAGE_CONF = SCR_ADDR_CONF+8. ; here is the command with pages - the starting page and the number of pages - 2 words | ||
1041 | SCR_MEM_CONF = SCR_PAGE_CONF+4. ; here is the memory configuration from the registers - 22 words | ||
1042 | SCR_VGA_CONF = SCR_MEM_CONF+44. ; here video controller configuration - 10 words | ||
1043 | SCR_PAL = SCR_VGA_CONF+20. ; here 338. values (words) of palette 338 words | ||
1044 | ;-------------------------------------------------- | ||
1045 | ; trap 41 - preparation of default information for screenshot functionality | ||
1046 | PrepSRC: jsr R5, PUSHA ; batch saving of registers | ||
1047 | mov @#177326,-(SP) ; save page 130k which was before the call | ||
1048 | mov #76,@#177326 ; hook 76th page into window | ||
1049 | |||
1050 | mov #100377,R3 ; constant-filler | ||
1051 | mov #SCR_PAGE,R4 | ||
1052 | mov #2047.,R2 | ||
1053 | 4$: mov R3,(R4)+ | ||
1054 | sob R2,4$ | ||
1055 | |||
1056 | mov #240,@#SCR_TAG ; put the tag | ||
1057 | |||
1058 | ; clean the address section - default is automatic address detection | ||
1059 | clr R3 | ||
1060 | mov #SCR_ADDR_CONF,R4 | ||
1061 | mov #10,R2 | ||
1062 | 2$: mov R3,(R4)+ | ||
1063 | sob R2,2$ | ||
1064 | |||
1065 | ; memory configuration - default | ||
1066 | mov #SCR_MEM_CONF,R4 | ||
1067 | mov #30,(R4)+ ;177300 | ||
1068 | mov #31,(R4)+ ;177302 | ||
1069 | mov #32,(R4)+ ;177304 | ||
1070 | mov #33,(R4)+ ;177306 | ||
1071 | mov #04,(R4)+ ;177310 | ||
1072 | mov #05,(R4)+ ;177312 | ||
1073 | mov #06,(R4)+ ;177314 | ||
1074 | mov #07,(R4)+ ;177316 | ||
1075 | mov #20,(R4)+ ;177320 | ||
1076 | mov #21,(R4)+ ;177322 | ||
1077 | mov #22,(R4)+ ;177324 | ||
1078 | mov #23,(R4)+ ;177326 | ||
1079 | mov #120,(R4)+ ;177330 | ||
1080 | mov #121,(R4)+ ;177332 | ||
1081 | mov #110,(R4)+ ;177334 | ||
1082 | mov #100,(R4)+ ;177336 | ||
1083 | |||
1084 | mov #170000,(R4)+ ;177340 | ||
1085 | mov R3,(R4)+ ;177342 | ||
1086 | mov #7777,(R4)+ ;177344 | ||
1087 | mov #40404,(R4)+ ;177346 | ||
1088 | mov R3,(R4)+ ;177350 | ||
1089 | mov #16000,(R4)+ ;177352 | ||
1090 | |||
1091 | ;Video controller configuration - default | ||
1092 | ; 177230-177256 | ||
1093 | mov #SCR_VGA_CONF,R4 | ||
1094 | |||
1095 | mov #12201,(R4)+ ;177230 | ||
1096 | mov #4,(R4)+ ;177232 | ||
1097 | mov R3,(R4)+ ;177240 | ||
1098 | mov R3,(R4)+ ;177242 | ||
1099 | mov R3,(R4)+ ;177244 | ||
1100 | mov R3,(R4)+ ;177246 | ||
1101 | mov R3,(R4)+ ;177250 | ||
1102 | mov R3,(R4)+ ;177252 | ||
1103 | mov R3,(R4)+ ;177254 | ||
1104 | mov R3,(R4)+ ;177256 | ||
1105 | |||
1106 | ; download the palette - take the default one from this ROM | ||
1107 | mov #SCR_PAL,R4 | ||
1108 | mov #PalData,R2 | ||
1109 | mov #338.,R3 | ||
1110 | 1$: mov (R2)+,(R4)+ | ||
1111 | sob R3,1$ | ||
1112 | |||
1113 | mov (SP)+,@#177326; return the page from which the call was made | ||
1114 | return | ||
1115 | {{/code}} | ||
1116 | |||
1117 | There are three options for specifying memory areas. | ||
1118 | |||
1119 | 1. specify the address and length 24-bit - see format, if they are not there - the system looks further | ||
1120 | 1. specify the page number and page quantity, if they are not there | ||
1121 | 1. the system looks further - that is, it makes a screenshot based on the data about registers 177230, etc. | ||
1122 | |||
1123 | The screenshot is saved in the format | ||
1124 | - page 76 - its first kilobyte | ||
1125 | - the memory image itself (if the mode is layered - then all three layers) | ||
1126 | |||
1127 | |||
1128 | Before calling the command, you can load the file name for saving the screenshot [in the cmosmem buffer], but if it is missing (there will be no name in the buffer - a line ending with 0), the system will generate its own name based on the following rule: default path for saving screenshots | ||
1129 | 0:/SCREENS/ | ||
1130 | name format - DDHHMISS.SCR | ||
1131 | where DD is two digits of the day of the month, HH is the hour, MI is the minute, SS is the second | ||
1132 | \\If an error occurs during the command execution, the name will be "ERROR *" | ||
1133 | for example | ||
1134 | "ERROR f_open 6" | ||
1135 | |||
1136 | |||
1137 | Example program | ||
1138 | |||
1139 | {{code language="assembler"}} | ||
1140 | ; update the information in the screenshot header | ||
1141 | ;-------------------------------------------------- | ||
1142 | ; 76th page map - we prepare data for the screenshot command there | ||
1143 | SCR_PAGE = 130000 ; we temporarily attach the 76th page to the 77th - that is, into the 130000 window | ||
1144 | SCR_TAG = SCR_PAGE+0 ; here is the input - 240 - 1 word | ||
1145 | SCR_ADDR_CONF = SCR_TAG+2 ; here is the command with addresses - address+length 24 bits - 4 words | ||
1146 | SCR_PAGE_CONF = SCR_ADDR_CONF+8. ; here is the command with pages - the starting page and the number of pages - 2 words | ||
1147 | SCR_MEM_CONF = SCR_PAGE_CONF+4. ; here is the memory configuration from the registers - 22 words | ||
1148 | SCR_VGA_CONF = SCR_MEM_CONF+44. ; here video controller configuration - 10 words | ||
1149 | SCR_PAL = SCR_VGA_CONF+20. ; here 338. values (words) of palette 338 words | ||
1150 | ;-------------------------------------------------- | ||
1151 | mov @#177326,R5 ; save page 130k which was before the call | ||
1152 | mov #76,@#177336 ; hook the 76th page into the window | ||
1153 | |||
1154 | ;video controller configuration | ||
1155 | ; 177230-177256 | ||
1156 | mov #SVGAC,R4 | ||
1157 | |||
1158 | mov @#177230,(R4)+;177230 - control register | ||
1159 | mov @#177232,(R4)+;177232 - register - top page (layer 0) | ||
1160 | mov @#177240,(R4)+;177240 - register - top page (layer 1) | ||
1161 | mov @#177242,(R4)+;177242 - register - top page (layer 2) | ||
1162 | mov @#177244,(R4)+;177244 - vertical scroll register layer 2 | ||
1163 | mov @#177246,(R4)+;177246 - vertical scroll register layer 1 | ||
1164 | mov @#177250,(R4)+;177250 - vertical scroll register layer 0 | ||
1165 | mov @#177252,(R4)+;177252 - horizontal scroll register layer 0 | ||
1166 | mov @#177254,(R4)+;177254 - horizontal scroll register layer 1 | ||
1167 | mov @#177256,(R4)+;177256 - horizontal scroll register layer 2 | ||
1168 | |||
1169 | mov R5,@#177336; return the page from which the call was made | ||
1170 | |||
1171 | |||
1172 | ;------------------------- | ||
1173 | MOV #AZ$CSR,R3 ; Preparing controller CSR | ||
1174 | MOV #AZ$DR,R4 ; Preparing controller DR | ||
1175 | 20$: CLR (R3) ; Reset the controller | ||
1176 | TSTB (R3) | ||
1177 | BPL 20$ | ||
1178 | |||
1179 | ; clear the memory block for the name - so that the system makes a default file name | ||
1180 | mov #23,(R3) ; command that we will write data to the buffer | ||
1181 | 128$: TSTB (R3) ; check the result of execution | ||
1182 | BPL 128$ ; wait | ||
1183 | clr R1 | ||
1184 | |||
1185 | mov #256.,R2 ; | ||
1186 | 129$: mov R1,(R4) ; give to the controller | ||
1187 | sob R2,129$ | ||
1188 | |||
1189 | |||
1190 | MOV #044,(R3) ; screenshot command | ||
1191 | 22$: TSTB (R3) ; | ||
1192 | BPL 22$ ; | ||
1193 | |||
1194 | ; get the screenshot name | ||
1195 | mov #22,(R3) ; give the read memory block from the buffer to the bus | ||
1196 | 121$: tstb (R3) ; check the result of execution | ||
1197 | bpl 121$ ; wait | ||
1198 | |||
1199 | mov #BUF,R1 | ||
1200 | mov #256.,R2 ; read 256. words; the first word is the result of reading | ||
1201 | 122$: mov (R4),(R1)+ ; read a block of words into memory | ||
1202 | sob R2,122$ | ||
1203 | |||
1204 | .PRINT #RESOK | ||
1205 | .Print #BUF | ||
1206 | |||
1207 | mov #110,@#AZ$CSR; enable the network by default, the network should be constantly enabled | ||
1208 | .Exit | ||
1209 | {{/code}} | ||
1210 | |||
1211 | To unpack a screenshot, you can use this utility - [[https:~~/~~/master.pdp-11.ru/screen_unpack/>>https://master.pdp-11.ru/screen_unpack/]] | ||
1212 | |||
1213 | |||
1214 | |||
1215 | = Commands for working with the TCP/IP stack = | ||
1216 | |||
1217 | The following commands operate on the TCP/IP stack information buffer. | ||
1218 | |||
1219 | == 040: Get IP address and other TCP/IP stack settings to buffer == | ||
1220 | |||
1221 | Command code 040, this command fills the buffer with information from the TCP/IP stack with current (actual) information. | ||
1222 | |||
1223 | == 041: Reading ip address buffer == | ||
1224 | |||
1225 | Command code 041, this command transfers the buffer to the bus | ||
1226 | |||
1227 | This pair of commands allows you to get current information from the stack | ||
1228 | |||
1229 | * IP address | ||
1230 | * MASK mask | ||
1231 | * GW gateway | ||
1232 | * NTP address of the NTP server | ||
1233 | * DNS1 primary DNS address | ||
1234 | * DNS2 backup DNS address | ||
1235 | |||
1236 | accordingly it is 12 words | ||
1237 | |||
1238 | Example program: | ||
1239 | |||
1240 | {{code language="assembler"}} | ||
1241 | ; trap 52 - reading a block of IP addresses into the IPADDDBLOCK memory block (8 cells) | ||
1242 | ; result in R3 = 0 if error, otherwise the address where it was read (IPADDDBLOCK) | ||
1243 | GetIPaddrs: push R1 | ||
1244 | push R2 | ||
1245 | call AZreset; reset | ||
1246 | tst R1 | ||
1247 | bne 0ERR$ | ||
1248 | MOV #AZ$CSR,R1 | ||
1249 | mov #40,(R1) | ||
1250 | 0$: TSTB (R1); read addresses into its memory | ||
1251 | BPL 0$; wait | ||
1252 | mov #41,(R1) | ||
1253 | 1$: TSTB (R1); prepare buffer | ||
1254 | BPL 1$; wait | ||
1255 | TST (R1)+; increment | ||
1256 | mov #IPADDDBLOCK,R3 | ||
1257 | mov #12.,R2 | ||
1258 | 2$: mov (R1),(R3)+; read block of words into memory | ||
1259 | sob R2,2$ | ||
1260 | mov #IPADDDBLOCK,R3; success | ||
1261 | br 0END$ | ||
1262 | 0ERR$: CLR R3 | ||
1263 | 0END$: pop R2 | ||
1264 | pop R1 | ||
1265 | return | ||
1266 | |||
1267 | {{/code}} | ||
1268 | |||
1269 | Example return data: | ||
1270 | |||
1271 | {{info}} | ||
1272 | Data examples - words returned in octal format | ||
1273 | 124300 116400 - IP address 192.168.0.157 | ||
1274 | 177777 000377 - MASK mask 255.255.255.0 | ||
1275 | 124300 000400 - GW gateway 192.168.0.1 | ||
1276 | 124300 000400 - NTP address of NTP server 192.168.0.1 | ||
1277 | 124300 050000 - DNS1 address of primary DNS 192.168.0.90 | ||
1278 | 124300 055000 - DNS2 address of backup DNS 192.168.0.80 | ||
1279 | {{/info}} | ||
1280 | |||
1281 | == 043: Read MAC address into ip buffer == | ||
1282 | |||
1283 | Command code 043, this command reads the current actual MAC address into the IP address buffer i.e. first 043 and then 041 commands | ||
1284 | |||
1285 | Example program: | ||
1286 | |||
1287 | {{code language="assembler"}} | ||
1288 | ; trap 72 - reading the MAC address into the IPADDDBLOCK memory block (12 cells) | ||
1289 | ; result in R3 = 0 if error, otherwise the address where it was read (IPADDDBLOCK) | ||
1290 | GetMACaddrs: push R1 | ||
1291 | push R2 | ||
1292 | call AZreset ; reset | ||
1293 | tst R1 | ||
1294 | bne 0ERR$ | ||
1295 | MOV #AZ$CSR,R1 | ||
1296 | mov #43,(R1) | ||
1297 | 0$: TSTB (R1) ; read addresses into its memory | ||
1298 | BPL 0$ ; wait | ||
1299 | mov #41,(R1) | ||
1300 | 1$: TSTB (R1) ; prepare buffer | ||
1301 | BPL 1$ ; wait | ||
1302 | TST (R1)+ ; increment | ||
1303 | mov #IPADDDBLOCK,R3 | ||
1304 | mov #12.,R2 | ||
1305 | 2$: mov (R1),(R3)+ ; read block of words into memory | ||
1306 | sob R2,2$ | ||
1307 | mov #IPADDDBLOCK,R3; success | ||
1308 | br 0END$ | ||
1309 | 0ERR$: CLR R3 | ||
1310 | 0END$: pop R2 | ||
1311 | pop R1 | ||
1312 | retur | ||
1313 | |||
1314 | {{/code}} | ||
1315 | |||
1316 | |||
1317 | |||
1318 | = Commands for working with a MicroSD card at the file level = | ||
1319 | |||
1320 | |||
1321 | These commands are designed to work with a MicroSD card at the file system level and allow you to read/write files without mounting files as disk images. | ||
1322 | |||
1323 | These commands use a 256-word buffer that is used in the interface for working with non-volatile memory (see commands 022 023 above) | ||
1324 | |||
1325 | Limitations - the length of the full path to the file is 256 bytes | ||
1326 | |||
1327 | == 050: Set the name of the file we will read == | ||
1328 | |||
1329 | Command code 050, this command sets the name of the file that we will read, while opening the file for reading, and also obtaining its properties. | ||
1330 | |||
1331 | == 051: Get file size for reading (or its status) on BUS == | ||
1332 | |||
1333 | Command code 051, this command transmits the file size or reading error to the MPI. The file size is 31 bits, the most significant bit is an error indicator. Accordingly, the maximum file size that can be worked with via this interface is limited to 2 ^ 31 bytes (2GB). | ||
1334 | |||
1335 | The error generation looks like this: | ||
1336 | sizeanyfile=1<<31 + FFres; ~/~/ if the most significant bit of a 32-bit word is set, then the error code is in the lower part | ||
1337 | |||
1338 | FFres = FatFS error | ||
1339 | |||
1340 | {{code language="c"}} | ||
1341 | typedef enum { | ||
1342 | FR_OK = 0, /* (0) Succeeded */ | ||
1343 | FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ | ||
1344 | FR_INT_ERR, /* (2) Assertion failed */ | ||
1345 | FR_NOT_READY, /* (3) The physical drive cannot work */ | ||
1346 | FR_NO_FILE, /* (4) Could not find the file */ | ||
1347 | FR_NO_PATH, /* (5) Could not find the path */ | ||
1348 | FR_INVALID_NAME, /* (6) The path name format is invalid */ | ||
1349 | FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ | ||
1350 | FR_EXIST, /* (8) Access denied due to prohibited access */ | ||
1351 | FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ | ||
1352 | FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ | ||
1353 | FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ | ||
1354 | FR_NOT_ENABLED, /* (12) The volume has no work area */ | ||
1355 | FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ | ||
1356 | FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ | ||
1357 | FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ | ||
1358 | FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ | ||
1359 | FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ | ||
1360 | FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_LOCK */ | ||
1361 | FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ | ||
1362 | } FRESULT; | ||
1363 | {{/code}} | ||
1364 | |||
1365 | [[http:~~/~~/elm-chan.org/fsw/ff/doc/open.html>>url:http://elm-chan.org/fsw/ff/doc/open.html]] | ||
1366 | |||
1367 | == 052: Read a block of a set file into a buffer == | ||
1368 | |||
1369 | Command code 052, this command reads a file into a non-volatile memory buffer. | ||
1370 | |||
1371 | As a result, the file reading scheme looks like this | ||
1372 | **023** - fill the file name into the buffer | ||
1373 | **050 **- set the file for reading | ||
1374 | **051** - read the file length or file open error | ||
1375 | if there is an error - repeat 023 050 051 from the beginning | ||
1376 | if everything is ok - start reading the file | ||
1377 | **052** - read the file block into the buffer | ||
1378 | **022** - take data from the buffer | ||
1379 | repeat the **052 022** pairs the required number of times in order to read the entire file | ||
1380 | once the file is read - the last 052 command will close it automatically. | ||
1381 | |||
1382 | Example program: | ||
1383 | |||
1384 | {{code language="assembler"}} | ||
1385 | call AZRST; reset | ||
1386 | |||
1387 | ; load the file name into the buffer | ||
1388 | 7$: mov #23,(R1); command to write data to the buffer | ||
1389 | 5$: TSTB (R1); check execution result | ||
1390 | BPL 5$ ; wait | ||
1391 | TST (R1)+; increment | ||
1392 | mov #FILNM,R3 | ||
1393 | 1$: mov (R3),(R1); send to controller | ||
1394 | tst (R3)+ | ||
1395 | bne 1$ | ||
1396 | tst -(R1); decrement | ||
1397 | |||
1398 | ; set the file for reading | ||
1399 | mov #50,(R1); set file for reading | ||
1400 | 2$: TSTB (R1); check execution result | ||
1401 | BPL 2$ ; wait | ||
1402 | |||
1403 | ; read the file length | ||
1404 | mov #51,(R1); set file for reading | ||
1405 | 3$: TSTB (R1); check execution result | ||
1406 | BPL 3$ ; wait | ||
1407 | TST (R1)+; increment | ||
1408 | mov #FILSZ,R3 | ||
1409 | mov (R1),(R3)+; read from controller | ||
1410 | mov (R1),(R3); read from controller | ||
1411 | |||
1412 | ; display the file length on the screen | ||
1413 | clr R0 | ||
1414 | mov #FILSZ+2,R3 | ||
1415 | mov (R3),R1 | ||
1416 | call DNOZ | ||
1417 | mov -(R3),R1 | ||
1418 | call DNOZ | ||
1419 | .print #STMS2 | ||
1420 | |||
1421 | ; read the file | ||
1422 | mov R1,R4; R1 holds the file length | ||
1423 | MOV #AZ$CSR,R1 | ||
1424 | mov #BUFFL,R5 | ||
1425 | |||
1426 | bit #1,R4; if an odd number of bytes | ||
1427 | beq 47$ | ||
1428 | inc R4; add 1 more byte as we read words | ||
1429 | |||
1430 | 47$: tst R4 | ||
1431 | beq 45$ ; nothing left to read - exit | ||
1432 | |||
1433 | mov #52,(R1); read block into buffer | ||
1434 | 4$: TSTB (R1); check execution result | ||
1435 | BPL 4$ ; wait | ||
1436 | mov #22,(R1); start reading buffer | ||
1437 | 51$: TSTB (R1); check execution result | ||
1438 | BPL 51$ ; wait | ||
1439 | |||
1440 | cmp R4,#512.; compare with buffer size in bytes | ||
1441 | Blos 44$ ; less than buffer size left | ||
1442 | |||
1443 | .print #STMS1 | ||
1444 | mov #256.,R2 | ||
1445 | TST (R1)+; move to data register | ||
1446 | 46$: mov (R1),(R5)+; read into buffer | ||
1447 | sob R2,46$ | ||
1448 | sub #512.,R4; subtract | ||
1449 | TST -(R1); move to command register | ||
1450 | br 47$ | ||
1451 | |||
1452 | 44$: .print #STMS3 | ||
1453 | mov R4,R2 | ||
1454 | asr R2 ; /2 since reading words | ||
1455 | TST (R1)+; move to data register | ||
1456 | 43$: mov (R1),(R5)+; read into buffer | ||
1457 | sob R2,43$ | ||
1458 | |||
1459 | 45$: clr (R5); set end of file marker | ||
1460 | |||
1461 | ; file read - display on screen | ||
1462 | .print #STMS4 | ||
1463 | .print #BUFFL | ||
1464 | .print #STMS5 | ||
1465 | |||
1466 | mov #110,@#AZ$CSR; enable network | ||
1467 | .Exit | ||
1468 | |||
1469 | {{/code}} | ||
1470 | |||
1471 | |||
1472 | |||
1473 | == 053: set the name of the file that we will write == | ||
1474 | |||
1475 | Command code 053, this command opens a file for writing, receives opening parameters (or errors). | ||
1476 | |||
1477 | == 054: Set file length == | ||
1478 | |||
1479 | Command code 054, this command sets the expected file length, this is necessary for the correct formation of the file at the file level of the MicroSD card, as well as for organizing data transfer. | ||
1480 | |||
1481 | == 055: write data from buffer to file == | ||
1482 | |||
1483 | Command code 055, this command writes data from the non-volatile memory buffer to an open file for writing. The last command 055 will automatically close the file when the declared file length is reached. | ||
1484 | |||
1485 | The command flow chart for writing is as follows | ||
1486 | **023** - fill the file name into the buffer | ||
1487 | **053** - set the file for reading | ||
1488 | **051** - file opening/creation status | ||
1489 | if an error - repeat from the beginning 023 053 051 | ||
1490 | if everything is ok - move on | ||
1491 | **054** - set the file length, i.e. we must immediately declare what the file length will be | ||
1492 | **023** - fill the data block into the buffer | ||
1493 | **055** - write from the buffer to the file | ||
1494 | repeat the 023 055 pairs the required number of times in order to write the entire file | ||
1495 | once the file is written - the last 055 command will close it automatically | ||
1496 | |||
1497 | |||
1498 | Example program: | ||
1499 | |||
1500 | {{code language="assembler"}} | ||
1501 | ; load the file name into the buffer | ||
1502 | MOV #AZ$CSR,R1 | ||
1503 | 17$: mov #23,(R1); command to write data to the buffer | ||
1504 | 15$: TSTB (R1) ; check execution result | ||
1505 | BPL 15$ ; wait | ||
1506 | TST (R1)+ ; move to data register | ||
1507 | mov #FILNM2,R3 | ||
1508 | 11$: mov (R3),(R1); send to controller | ||
1509 | tst (R3)+ | ||
1510 | bne 11$ | ||
1511 | tst -(R1) ; move to command register | ||
1512 | |||
1513 | ; set the file for writing | ||
1514 | mov #53,(R1); set file for writing | ||
1515 | 12$: TSTB (R1) ; check execution result | ||
1516 | BPL 12$ ; wait | ||
1517 | |||
1518 | ; read file creation status | ||
1519 | mov #51,(R1) | ||
1520 | 13$: TSTB (R1) ; check execution result | ||
1521 | BPL 13$ ; wait | ||
1522 | TST (R1)+ ; move to data register | ||
1523 | mov #STATS,R3 | ||
1524 | mov (R1),(R3)+; read from controller | ||
1525 | mov (R1),(R3); read from controller | ||
1526 | |||
1527 | ; check here - if the file is created, both words should be zero | ||
1528 | mov #STATS,R3 | ||
1529 | TST (R3)+ | ||
1530 | BNE 66$ | ||
1531 | TST (R3) | ||
1532 | BEQ 60$ | ||
1533 | 66$: .print #ERRMS1 ; print error | ||
1534 | .exit | ||
1535 | |||
1536 | 60$: MOV #AZ$CSR,R1 | ||
1537 | mov #54,(R1); set file length to be written | ||
1538 | 23$: TSTB (R1) ; check execution result | ||
1539 | BPL 23$ ; wait | ||
1540 | TST (R1)+ ; move to data register | ||
1541 | mov #FILSZ,R3 | ||
1542 | mov (R3)+,(R1); write to controller | ||
1543 | mov (R3),(R1); write to controller | ||
1544 | |||
1545 | tst -(R1) ; move to command register | ||
1546 | |||
1547 | ; write file | ||
1548 | mov @#FILSZ,R4; file length | ||
1549 | MOV #AZ$CSR,R1 | ||
1550 | mov #BUFFL,R5; file buffer | ||
1551 | |||
1552 | bit #1,R4 ; if an odd number of bytes | ||
1553 | beq 147$ | ||
1554 | inc R4 ; add 1 more byte since reading words | ||
1555 | |||
1556 | 147$: tst R4 ; check length | ||
1557 | beq 145$ ; nothing left to write - exit | ||
1558 | |||
1559 | mov #23,(R1); write to buffer | ||
1560 | 151$: TSTB (R1) ; check execution result | ||
1561 | BPL 151$ ; wait | ||
1562 | |||
1563 | cmp R4,#512.; compare with buffer size in bytes | ||
1564 | Blos 144$ ; less than buffer size left | ||
1565 | |||
1566 | .print #STMS6 ; writing full block | ||
1567 | mov #256.,R2 | ||
1568 | TST (R1)+ ; move to data register | ||
1569 | 146$: mov (R5)+,(R1); write to controller buffer | ||
1570 | sob R2,146$ | ||
1571 | sub #512.,R4; subtract | ||
1572 | TST -(R1) ; move to command register | ||
1573 | |||
1574 | mov #55,(R1); write buffer to file | ||
1575 | 104$: TSTB (R1) ; check execution result | ||
1576 | BPL 104$ ; wait | ||
1577 | br 147$ ; loop back | ||
1578 | |||
1579 | 144$: .print #STMS7 ; writing last block | ||
1580 | TST (R1)+ ; move to data register | ||
1581 | mov R4,R2 | ||
1582 | asr R2 ; /2 since writing words | ||
1583 | 143$: mov (R5)+,(R1); write to controller buffer | ||
1584 | sob R2,143$ | ||
1585 | |||
1586 | TST -(R1) ; move to command register | ||
1587 | mov #55,(R1); write last buffer to file | ||
1588 | 105$: TSTB (R1) ; check execution result | ||
1589 | BPL 105$ ; wait | ||
1590 | |||
1591 | 145$: .print #STMSE ; end | ||
1592 | mov #110,@#AZ$CSR; enable network | ||
1593 | .Exit ; exit | ||
1594 | |||
1595 | {{/code}} | ||
1596 | |||
1597 | **[[the example is completely in the form of the RT11 utility and is posted here>>url:https://forum.maxiol.com/index.php?s=&showtopic=5605&view=findpost&p=57055]]** | ||
1598 | |||
1599 | |||
1600 | == 056: Get data on the size of the map into the sizecard buffer == | ||
1601 | |||
1602 | Command code 056, this command reads the parameters of the MicroSD card into the sizecard buffer | ||
1603 | |||
1604 | == 057: Reading sizecard buffer == | ||
1605 | |||
1606 | Command code 057, this command returns the sizecard buffer (2 words) | ||
1607 | |||
1608 | sizecard buffer contains 2 words 16bit | ||
1609 | first word - total card size available for FAT in MB | ||
1610 | second word - free card size in MB | ||
1611 | |||
1612 | Example program: | ||
1613 | |||
1614 | {{code language="assembler"}} | ||
1615 | ; trap 51 - get the total/free size of the SD card in megabytes | ||
1616 | ; result in R1 - total; R2 - free | ||
1617 | GetSizeSD: call AZreset ; reset | ||
1618 | tst R1 | ||
1619 | bne 0ERR$ | ||
1620 | MOV #AZ$CSR,R1 | ||
1621 | mov #56,(R1) | ||
1622 | 1$: TSTB (R1) ; prepare buffer | ||
1623 | BPL 1$ ; wait | ||
1624 | mov #57,(R1) | ||
1625 | 2$: TSTB (R1) ; prepare buffer | ||
1626 | BPL 2$ ; wait | ||
1627 | mov @#AZ$DR ,R1 ; total megabytes | ||
1628 | mov @#AZ$DR ,R2 ; free megabytes | ||
1629 | return | ||
1630 | clr R1 | ||
1631 | clr R2 | ||
1632 | return | ||
1633 | |||
1634 | {{/code}} | ||
1635 | |||
1636 | Example data | ||
1637 | 035521 - total megabytes on the card - 15185. | ||
1638 | 035417 - free megabytes - 15119. | ||
1639 | |||
1640 | = Hall of Fame API Command Block = | ||
1641 | |||
1642 | This block of commands is intended for interaction with the server [[Hall of Fame>>https://forum.maxiol.com/index.php?showtopic=5642]] | ||
1643 | |||
1644 | == **025: Инициализация Hall of Fame (HOF)** == | ||
1645 | |||
1646 | Command code 025, this command establishes a connection to the Hall of Fame server, initializes the encrypted tunnel and prepares the API for work. | ||
1647 | |||
1648 | Example program: | ||
1649 | |||
1650 | {{code language="assembler"}} | ||
1651 | AZ$CSR = 177220 ; command and status register (CSR) | ||
1652 | AZ$DR = 177222 ; data register (DR) | ||
1653 | |||
1654 | ; buffers | ||
1655 | SNDBUF: .BLKW 256. ; send buffer | ||
1656 | RCVBUF: .BLKW 256. ; receive buffer | ||
1657 | SIDMEM: .BLKB 34. ; SID | ||
1658 | SIDCST: .ASCII \{"SID":"\ ; SID header | ||
1659 | .even | ||
1660 | |||
1661 | HOFINI: ; HOF initialization | ||
1662 | ; The result is a JSON response: | ||
1663 | ; {"SID":"session hash","RESULT":"OK"} | ||
1664 | ; or an error: | ||
1665 | ; {"RESULT":"ERROR","DESCRIPTION":"SERVER_ERROR"} | ||
1666 | ; {"RESULT":"ERROR","DESCRIPTION":"CONNECTION_ERROR"} | ||
1667 | ; The result is placed in SNDBUF | ||
1668 | ; R5 indicates success: 1 = SID exists, 0 = no SID | ||
1669 | |||
1670 | mov R5, -(SP) | ||
1671 | mov R4, -(SP) | ||
1672 | mov R3, -(SP) | ||
1673 | mov R2, -(SP) | ||
1674 | mov R1, -(SP) | ||
1675 | mov R0, -(SP) | ||
1676 | |||
1677 | mov #3,R5 ; number of attempts | ||
1678 | |||
1679 | 220$: mov #AZ$CSR,R1 | ||
1680 | clr (R1) ; Send "Reset" command | ||
1681 | 221$: tstb (R1) ; Check controller readiness | ||
1682 | bpl 221$ ; If not ready, wait | ||
1683 | |||
1684 | mov #25,(R1) ; initialization - command 025 | ||
1685 | 20$: tstb (R1) ; check execution result | ||
1686 | bpl 20$ ; wait | ||
1687 | |||
1688 | ; Retrieve the result | ||
1689 | mov #22,(R1) ; output the read memory block from buffer to the bus | ||
1690 | 21$: tstb (R1) ; check execution result | ||
1691 | bpl 21$ ; wait | ||
1692 | tst (R1)+ ; increment | ||
1693 | mov #SNDBUF,R3 | ||
1694 | mov #256.,R2 ; read 256 words; first word is the read result | ||
1695 | 22$: mov (R1),(R3)+ ; read block of words into memory | ||
1696 | sob R2,22$ | ||
1697 | |||
1698 | |||
1699 | ; Determine if SID exists | ||
1700 | mov #4,R0 | ||
1701 | mov #SNDBUF,R1 | ||
1702 | mov #SIDCST,R2 | ||
1703 | 23$: cmp (R1)+,(R2)+ | ||
1704 | bne 24$ | ||
1705 | sob R0,23$ | ||
1706 | clr R5 | ||
1707 | inc R5 | ||
1708 | br 26$ ; success | ||
1709 | |||
1710 | 24$: ; SID not found! | ||
1711 | sob R5,220$ | ||
1712 | clr R5 ; error - no SID | ||
1713 | |||
1714 | 26$: mov (SP)+, R0 | ||
1715 | mov (SP)+, R1 | ||
1716 | mov (SP)+, R2 | ||
1717 | mov (SP)+, R3 | ||
1718 | mov (SP)+, R4 | ||
1719 | mov (SP)+, R5 | ||
1720 | return | ||
1721 | |||
1722 | {{/code}} | ||
1723 | |||
1724 | == 026: Exchange with Hall of Fame (HOF) == | ||
1725 | |||
1726 | Command code 026, this command makes a direct exchange with Hall of Fame | ||
1727 | |||
1728 | Example program: | ||
1729 | |||
1730 | {{code language="assembler"}} | ||
1731 | ;4. user authentication | ||
1732 | ;technically, this is sending a JSON | ||
1733 | ;{"SID":"session hash","CMD":"AUTH_USER","NIKNAME":"user nickname","PASSWORD":"user password"} | ||
1734 | ;the response is also JSON | ||
1735 | ;{"SID":"session hash","RESULT":"OK","UID":"user hash"} | ||
1736 | ;or | ||
1737 | ;{"SID":"session hash","RESULT":"ERROR","DESCRIPTION":"USER_NOT_FOUND_OR_WRONG_PASSWORD"} | ||
1738 | ;{"SID":"session hash","RESULT":"ERROR","DESCRIPTION":"SERVER_ERROR"} | ||
1739 | ;{"SID":"session hash","RESULT":"ERROR","DESCRIPTION":"SESSION_NOT_EXISTS_OR_EXPIRED"} | ||
1740 | |||
1741 | ; load command CMD04 | ||
1742 | mov #CMD04,R1 | ||
1743 | mov #ADRMEM,R2 | ||
1744 | add #42.,R2 ; shift the pointer to the SID block length | ||
1745 | 33$: movb (R1)+,(R2)+ | ||
1746 | bne 33$ | ||
1747 | |||
1748 | .Print #ADRMEM | ||
1749 | |||
1750 | .Print #HOF05 | ||
1751 | ; send the command and wait for a response | ||
1752 | ; load into buffer | ||
1753 | MOV #AZ$CSR,R1 | ||
1754 | 331$: TSTB (R1) ; Check controller readiness | ||
1755 | BPL 331$ ; If not ready, wait | ||
1756 | mov #23,(R1) ; command to write data into buffer | ||
1757 | 34$: TSTB (R1) ; check execution result | ||
1758 | BPL 34$ ; wait | ||
1759 | TST (R1)+ ; increment | ||
1760 | mov #ADRMEM,R3 | ||
1761 | mov #256.,R2 ; | ||
1762 | 35$: mov (R3)+,(R1) ; send to controller | ||
1763 | sob R2,35$ | ||
1764 | tst -(R1) ; decrement | ||
1765 | |||
1766 | ; exchange - command 026 | ||
1767 | MOV #AZ$CSR,R1 | ||
1768 | 361$: TSTB (R1) ; Check controller readiness | ||
1769 | BPL 361$ ; If not ready, wait | ||
1770 | mov #26,(R1) | ||
1771 | 36$: TSTB (R1) ; check execution result | ||
1772 | BPL 36$ ; wait | ||
1773 | |||
1774 | ; receive result | ||
1775 | 371$: TSTB (R1) ; Check controller readiness | ||
1776 | BPL 371$ ; If not ready, wait | ||
1777 | mov #22,(R1) ; send buffer to the bus | ||
1778 | 37$: TSTB (R1) ; check execution result | ||
1779 | BPL 37$ ; wait | ||
1780 | TST (R1)+ ; increment | ||
1781 | mov #ADRMEM,R3 | ||
1782 | mov #256.,R2 ; read 256 words; first word is the read result | ||
1783 | 38$: mov (R1),(R3)+ ; read block of words into memory | ||
1784 | sob R2,38$ | ||
1785 | |||
1786 | {{/code}} | ||
1787 | |||
1788 |