AZ® Controller API
- Controller concept
- Main command block
- 000: Controller reset
- 001: Select device
- 002: Set block number, low bits of block number
- 003: Open HFS Table of Contents
- 004: Mount a disk
- 005: Reading block into buffer
- 006: Write block from buffer to disk
- 007: Get disk size
- 010: Allow network operation
- 011: Get AZn drive assignment table
- 012: Setting the block number, block number high bits
- 013: Read HFS TOC entry
- 014: Unmount disk
- 015: Start transferring the read block
- 016: Receive data block into buffer
- 017: Get ramdisk size, large
- 020: Get extended diagnostic code
- 027: Get firmware version AZ STM32
- 030: No operation
- Command block for working with non-volatile memory
- Block of commands for working with RTC and NTP
- Buffer format timestamp (readable)
- SimpleIN buffer format (when writing)
- 031: Get time from RTC to timestamp buffer
- 032: Get time from timestamp buffer
- 033: Write time-date to SimpleIN buffer
- 034: Set RTC based on buffer data
- 035: Stimulate time request from NTP server, set based on response
- 036: Setting RTC based on TCP/IP stack clock
- 042: Get time from TCP/IP stack clock into timestamp buffer
- AZБК® specific commands
- Commands for working with the TCP/IP stack
- Commands for working with a MicroSD card at the file level
- 050: Set the name of the file we will read
- 051: Get file size for reading (or its status) on BUS
- 052: Read a block of a set file into a buffer
- 053: set the name of the file that we will write
- 054: Set file length
- 055: write data from buffer to file
- 056: Get data on the size of the map into the sizecard buffer
- 057: Reading sizecard buffer
- Hall of Fame API Command Block
Controller concept
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.
Controller registers
The controller has 4 registers on the QBUS
- 177220 - Command and Status Register (CSR)
- 177222 - Data Register (DR)
- 177224 - Primary Boot Register (BOOT1)
- 177226 - Alternate Boot Register (BOOT2)
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.
Writing to registers is done only in words; byte writing is not allowed.
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.
Interruptions
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.
Namespaces
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:
- 0:/ - local files on the MicroSD card
- R:/ - network files in the file repository
- N:/ - network files in the archive https://mirrors.pdp-11.ru/
Planned network spaces (functionality will be implemented in the following firmware):
- P:/ - personal cloud, available only under one account
- S:/ - cloud that allows you to share your files with other members
Buffers
The controller has several buffers for different purposes, all buffers have word sizes because they are transmitted through a 16-bit register.
- Main buffer - IOBUF [258]
- Non-volatile memory buffer - cmosmem_buffer[256]
- Clock buffer - output timestamp_out_buffer[14]
- Clock buffer - input timestamp_in_buffer [7]
- IP information buffer - ipdata
- Map size buffer - sizecard
Main command block
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.
These commands use the main buffer for their operation (IOBUF [258]).
000: Controller reset
The command code is 000. The command stops all controller operations, if possible. Its completion should be waited for by polling. Example program:
MOV #AZ$CSR,R3
1$: CLR @R3; Send the "Reset" command
TSTB @R3; Check the controller's readiness
BPL 1$; If it's not ready, reset it again
; and check again
TST (R3)+; Check for an error,
; change the address at the same time
; (will be useful later)
BMI ERR1
;...............................
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.
001: Select device
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.
Пример программы:
SetUni = 001; Symbolic name of the command
; "Device selection"
; The address CSR+2=DR remained in R3 from the previous fragment
; We assume that in R0 in bits 0-3 there is the number of the
; device, the remaining bits are zeros, the procedure for ;calculating this number is not shown.
MOV R0,@R3; Let's send to DR the number of disk AZ, with
; which we are going to work with
MOV #SetUni,-(R3); and send the command "Drive selection", with the correction of the address in R3, which
; now points to CSR again.
TST (R3)+; Let's check for an error and again
; move the address in R3 to DR
BMI ERR2
;........................
002: Set block number, low bits of block number
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.
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.
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.
Example of a program - 16-bit version block number:
SetBlk=002; Symbolic name of the command
; "Set the lower 16 bits of the block number"
; The address DR
; (177222) remained in R3 from the previous fragment
; We assume that the cell labeled BLCUR contains the 16-
; bit disk address (the block number to be
; input or output). The procedure for obtaining this number is not
; shown
MOV BLCUR,@R3;We place the block number to be exchanged in DR.
MOV #SetBlk,-(R3); We send the command to the CSR
; do not forget that the address in R3 will decrease by
; 2 before sending and will remain so
TST @R3; We check for an error
BMI ERR3
; In the 32-bit version, the same actions should be
; repeat for the senior 16 bits (actually,
; senior 7, the rest should be zeros, because the maximum
; size of a pseudo disk is 4G) of the disk address.
; Note that in R3 the CSR address remains, and not
; DR, as in the two previous fragments. This is done
; on purpose.
;.......................................
003: Open HFS Table of Contents
Sequence of actions:
• reset the controller
• 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).
• send the "Open table of contents" command code to the CSR
• wait for it to finish (the command is long)
• check for errors
Example program:
AZ$CSR = 177220
WrBuf = 016
OpnDir = 003
MOV #AZ$CSR,R3
; The DirPtr cell contains a pointer to the beginning of the field with Full
; Path. We assume that the string is terminated by three zero
; bytes in order to recognize the end of the transfer of the
; string word by word by zero. Indeed, if the number of
; characters in the string is even, then the next two bytes of zeros
; form a zero word; if it is odd, then one zero
; will go away with the last character of the string, and the zero
; word is formed by the second and third zero bytes,
; ending the string. That is, such an end of the string,
; transferred word by word, is quite reliable.
MOV DirPtr,R2
MOV #WrBuf,(R3)+; We issue the "Write to
; buffer" command and transmit the string
11$: MOV (R2)+,@R3; word by word,
BNE 11$ ;until zero is sent,
MOV #OpnDir,-(R3); We issue the "Open
; table of contents" command
12$: TSTB @R3; and wait for the controller to
BPL 12$; execute it,
TST @R3; after which we check for an error.
BMI Err10; Error -->
;.............................
004: Mount a disk
Sequence of actions:
• Reset the controller
• Send a line to the controller with a record similar to the lines describing the disks in the AZ.INI file
• Issue the "Mount disk" command
• Wait for it to finish (the command is long)
• Check for errors
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
Example program:
AZMNT = 004
MDLEN = MDEND-MDTXT+2
MOV #AZ$CSR,R3
20$: CLR @R3;
TSTB @R3; Reset the controller
BPL 20$;
MOV #WrBuf,(R3)+; Send it a string
MOV #MDTXT,R2; with the assignment command
MOV #MDLEN/2,R1;
21$: MOV (R2)+,@R3;
SOB R1,21$;
MOV #AZMNT,-(R3); and pass it to
22$: TSTB @R3 ; execution
BPL 22$ ;
TST @R3; then check for an error
BMI Err11;
;.........................
MDTXT: .ASCII "D04=0:/DISKS/SYSTEM/51SYS_DS.DSK"
MDEND: .BYTE 0,0
;..................
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.
005: Reading block into buffer
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.
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.
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.
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.
An example without interruptions is trivial:
CmdRea=005; symbolic name of the "Read block" command
; In R3 we have the CSR address left from the previous fragment.
; We send the read command code there
MOV #CmdRea,@R3
2$: TSTB @R3 Let's check the ready bit
BPL 2$; Not ready -> we go to check again
; once
TST @R3; Let's check for an error
BMI ERR4
; Here again, unlike the fragments of pp. 3.1 and
; 3.2, we have the CSR address left in R3, not DR.
;...................................
006: Write block from buffer to disk
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.
Example of a program without interrupts:
CmdWri=006; symbolic name of the "Write
; block" command
; In R3 we have the DR address left over from the previous fragment.
; We correct it to the CSR and send the
; write command code there
MOV #CmdWri,-(R3)
5$: TSTB @R3 ; Let's check the ready bit
BPL 5$; Not ready -> we go and check again once
TST @R3; Let's check for an error
BMI ERR5
;....................................................................
007: Get disk size
There are two commands for obtaining the size of a pseudo-disk, i.e. the AZn file-image mounted on the selected pseudo-drive.
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).
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.
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.
Example of a program with "small" disks
GetSiz=007; Get the "small" disk size
; From fragment 3.2 (disk selection) we have in R3
; DR address (177222)
MOV #GetSiz,-(R3); send the command
TST (R3)+; return the address in R3 back to DR
MOV @R3,DskSiz
;......................................
010: Allow network operation
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.
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.
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.
011: Get AZn drive assignment table
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.
* starting with v17, the file name length is no longer 130 bytes, but 386 bytes (the last word is zero, to end the line)
Example program:
AZ$CSR = 177220; Controller CSR
RdBuf = 012; Command "Read from controller memory"
RdTbl = 011; Command "Read assignment table"
TblSiz = 1120.; Table length in bytes (decimal)
; We assume that R2 contains the address of the first word of the
; memory area for the assignment table. We do not show the procedure for obtaining this address.
MOV #AZ$CSR,R3; Preparing the controller CSR
10$: CLR @R3;
TSTB @R3; Reset the controller
BPL 10$;
MOV #RdTbl,@R3;Command "Transfer
; table"
MOV #RdBuf,(R3+); Command "Read from
; controller memory. At the same time, move the address in R3
; to the DR of the controller (177222).
MOV #TblSiz/2,R1; Prepare the word counter
11$: MOV @R3,(R2)+; Send the current word
SOB R1,11$; and repeat 560 times
;...................................
012: Setting the block number, block number high bits
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.
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.
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.
013: Read HFS TOC entry
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.
The procedure is as follows:
• Reset the controller.
• Issue the command "Read the table of contents entry" to the CSR and wait for it to finish.
• Issue the command "Read from controller memory" to the CSR
• Read 11 words of the table of contents entry from DR
The table of contents entry has the format:
Offset (octal) | Name | Value |
---|---|---|
0 | fSize | File size in bytes, low word |
2 | fSize | File size in bytes, high word |
4 | fDate | Date in MS-DOS format |
6 | fTime | Time in MS-DOS format |
10 | fAttr | Attributes 1 byte |
10 | fName | NAME.FILE TYPE, 8+1+3+1 = 13 bytes |
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.
File attributes in fAttr byte (octal):
001 - Read Only
002 - Hidden
004 - System
020 - Directory
040 - Archive
Example program
RdDir = 013; command code "Read table of contents entry"
RdBuf = 015
MOV #AZ$CSR,R3
15$: CLR @R3;
TSTB @R3; Reset the controller
BPL 15$;
MOV #RdDir,@R3; Ask the controller
16$: TSTB @R3; to read into its memory
BPL 16$; table of contents entry
MOV @RdBuf,(R3)+;
MOV DIRREC,R2; And transfer it to itself in
MOV #11.,R1; memory area, pointer
17$: MOV @R3,(R2)+; to which lies in cell
SOB R1,17$; DIRREC.
;......................................
014: Unmount disk
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.
AZUMNT utility example
015: Start transferring the read block
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.
Example program:
RdBuf=015; symbolic name of the command
; In R3 from the previous fragment there is the address of the CSR
; (177220)
; We assume that in R2 we have the address of the first word
; of memory, where the read block should be placed.
; The program for obtaining this address is not given.
MOV #400,R1; Prepare the word counter
; 0400 oct = 256 dec
MOV #RdBuf,(R3)+; and send the command
; RdBuf to the CSR. The address in R3 will point to DR (177222).
3$: MOV @R3,(R2)+we will send the next word to
; memory
SOB R1,3$; and repeat this 256 (0400)
; times
;..................................
That's it, reading is complete.
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"
016: Receive data block into buffer
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.
Example program:
WrBuf=016; Symbolic name of the command
; Before writing, you need to perform the same actions as in
; pp. 3.1.-3.3. Usually, this is the same program,
; just after point 3.3. a check is performed "What
; is required: reading or writing?" and a branch is made to the
; reading or writing program.
; After the fragment in point 3.3., the CSR address
; (177220) remains in R3. We will assume that R2 contains the address in the CPU
; memory where the block to be written is located.
; The program for obtaining this address is not shown.
MOV #400,R1; Preparing the counter
MOV #WrBuf,(R3)+; Let's forward the command to the CSR and
; switch the address in R3 to
; DR
4$: MOV (R2)+,@R3; Let's forward the next word
; data
SOB R1,4$; and repeat this 256 times
;..................................
017: Get ramdisk size, large
There are two commands to get the size of a pseudo-disk, i.e. the AZn file-image mounted on the selected pseudo-drive.
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).
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.
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.
Example of a program with large disks
GetBig=017; Get the "big" disk size
; From fragment 3.2 (disk selection) we have in R3
; DR address (177222)
MOV #GetBig,-(R3); send the command
TST (R3)+; return the address in R3 back to DR
MOV @R3,BigSiz
MOV @R3,BigSiz+2
;......................................
020: Get extended diagnostic code
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.
027: Get firmware version AZ STM32
Command code 027, returns 2 words
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.
; getting STM32 firmware version - result in R1 R1=0 error
GTSTMV: MOV #AZ$CSR,R1
1$: CLR (R1) ; Send "Reset" command
TSTB (R1) ;Check controller readinessконтроллера
BPL 1$ ; If not ready, reset again
mov #27,(R1)
TST (R1)+ ; Check for error
BMI 2$
mov (R1),R1
return
2$: CLR R1
return
;-------------------------------------------------------------
030: No operation
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.
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.
Command block for working with non-volatile memory
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.
All commands in this block use a non-volatile memory buffer for their operation.
021: Read non-volatile memory block into buffer
Command code 021, this command causes a block of non-volatile memory to be read into the non-volatile memory buffer.
022: Transfer the read block of non-volatile memory from the buffer to the bus
Command code 022, this command ensures that the non-volatile memory buffer is transferred to the DR register for reading.
Example program
AZ$DR = 177222; data register (DR)
; trap 50 - reset AZ
; результат в R1 =0 ok
AZreset: MOV #AZ$CSR,R1
1$: CLR (R1); Send the "Reset" command
TSTB (R1); Check the controller readiness
BPL 1$; If not ready, reset again
; once and check again
TST (R1); Check for an error,
BMI 0ERR$
CLR R1
return
0ERR$: CLR R1
COM R1
return
; trap 54 - reading non-volatile memory of block 1 EEPROM to the buffer from the address ADREEPROMMEM
; result R3 - address, if R3=0 error
; read status in R1 0 - ok
; 1 - size does not match saved
; 2 - version error
; 3 - checksum error
ReadEEPROM: push R2
call AZreset; reset
tst R1
bne 0ERR$
; теперь читаем
MOV #AZ$CSR,R1
mov #21,(R1); read block 1 of non-volatile memory into buffer
0$: TSTB (R1); check execution result
BPL 0$; wait
mov #22,(R1); send read block of non-volatile memory from buffer to bus
1$: TSTB (R1); check execution result
BPL 1$; wait
TST (R1)+; increment
mov #ADREEPROMMEM,R3
mov #256.,R2; read 256. words; first word is reading result
2$: mov (R1),(R3)+; read block of words into memory
sob R2,2$
mov #ADREEPROMMEM,R3; successful
mov (R3),R1
br 0END$
0ERR$: CLR R3
0END$: pop R2
return
obviously, after reading the memory, it is necessary to check the result code in the first word - see the decoding of error codes
Examples of returned data for commands
sequentially issuing the command 021 and then 022 will allow reading 256 words from non-volatile memory
Attention! The first word will be the reading success status
- 0 - ok
- 1 - size does not match saved
- 2 - version error
- 3 - checksum error
023: Receive data from the bus into the buffer for subsequent writing into the buffer
Command code 023, this command allows you to fill the non-volatile memory buffer
024: Write from buffer to non-volatile memory block
Command code 024, this command causes a non-volatile memory block to be written from the non-volatile memory buffer.
Example program
AZ$DR = 177222; Data Register (DR)
; trap 50 - reset AZ
; результат в R1 =0 ok
AZreset: MOV #AZ$CSR,R1
1$: CLR (R1); Send the "Reset" command
TSTB (R1); Check the controller readiness
BPL 1$; If not ready, reset again
; once and check again
TST (R1); Check for an error,
BMI 0ERR$
CLR R1
return
0ERR$: CLR R1
COM R1
return
; trap 55 - write non-volatile memory from the buffer at address ADREEPROMMEM в блок 1 EEPROM
WriteEEPROM: push R1
push R2
push R3
call AZreset; reset
tst R1
bne 0ERR$
MOV #AZ$CSR,R1
mov #23,(R1);command that we will write data to the buffer
0$: TSTB (R1); check the result of executio
BPL 0$; wait
TST (R1)+; increment
mov #ADREEPROMMEM+2,R3
mov #255.,R2; write 255. words; skip the first word - the result of reading
1$: mov (R3)+,(R1); send to the controller
sob R2,1$
tst -(R1); decrement
mov #24,(R1); write from the buffer to block 1 of non-volatile memory
2$: TSTB (R1); check the result of execution
BPL 2$; we are waiting
br 0END$
0ERR$: CLR R3
0END$: pop R3
pop R2
pop R1
return
Please note that when recording, the buffer immediately comes with the data, i.e. there is no first word with the statu
Block of commands for working with RTC and NTP
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.
Buffer format timestamp (readable)
The controller API immediately prepares time in several formats, so that it can be conveniently used on the PDP-11 side
SimpleIN buffer format (when writing)
the format is simplified as much as possible, for work with PDP-11
031: Get time from RTC to timestamp buffer
Command code 031, this command uses RTC clock as a source of filling the timestamp buffer
Example program:
; R3 - buffer address where to read
; result in R3 address if successful. R3=0 if error
GetDateFromRTC: push R0
push R1
push R2
call AZreset; reset
tst R1
bne G60ERR
MOV #AZ$CSR,R1
mov #31,(R1)
br G60; let's go there because further code is the same
032: Get time from timestamp buffer
Command code 032, this command sends the contents of the timestamp buffer to the bus
; trap 60 - reading clock data from TCP/IP stack
; R3 - buffer address where to read
; result in R3 address if successful. R3=0 if error
GetDateFromLAN: push R0
push R1
push R2
call AZreset; reset
tst R1
bne G60ERR
MOV #AZ$CSR,R1
mov #42,(R1)
G60: TSTB (R1); check execution result
BPL G60; wait
mov #32,(R1)
1$: TSTB (R1); check execution result
BPL 1$; ждем
TST (R1)+; increment
mov R3,R0; remember R3 address
mov #10.,R2; read 10 words
2$: mov (R1),(R3)+; read block of words into memory
sob R2,2$
mov R0,R3; successful, return address to R3
br 0END$
G60ERR: CLR R3
0END$: pop R2
pop R1
pop R0
return
It is worth checking the correctness of the received time:
; R3 - buffer address, result in R3, if buffer address then OK, =0 error
CheckDateTime: Cmp 6(r3),#2021.
Blos 1err
Cmp 6(r3),#2100.
Bhi 1err
0ok$: return
1err$: clr R3
return
033: Write time-date to SimpleIN buffer
Command code 033, this command receives data from the bus into the SimpleIN buffer
The operation of this command is similar to the operation of commands 023 and 016.
034: Set RTC based on buffer data
Command code 034, this command sets the RTC based on the data in the SimpleIN buffer
This command executes quickly, but to avoid problems, a wait loop is recommended.
035: Stimulate time request from NTP server, set based on response
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.
Example program: sending a request to set the time from an NTP server
GetDateNTPtoNET:push R1
call AZreset; reset
tst R1
bne 0ERR$
MOV #AZ$CSR,R1
mov #35,(R1)
0$: TSTB (R1); check the result of execution
BPL 0$; wait
0ERR$: pop R1
return
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.
An example of a polling cycle to get time from the network
mov #S_DateTime_0,R3; "Lan Date:"
trap 10
mov #20,R4; number of wait cycles
$datry: trap 62; sent a request to the NTP server
mov #110,@#AZ$CSR; enable the network
trap 47; waiting
trap 47; waiting
mov #ADRTMPSTR,R3
trap 60; read the date-time into the buffer
trap 63; checked the date-time
tst R3
bne $ok
$sob: sob R4,$datry
mov #S_DateTime_2,R3; print error
trap 7
br $go
$ok: mov #ADRTMPSTR,R3
trap 24; print date
trap 25; time
$go: mov #110,@#AZ$CSR; let's turn on the network
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.
036: Setting RTC based on TCP/IP stack clock
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.
Example program:
; R1 - result R1=0 - OK
SetDateNETtoRTC:call AZreset; reset
tst R1
bne 0ERR$
MOV #AZ$CSR,R1
mov #36,(R1)
0$: TSTB (R1); check execution result
BPL 0$; wait
clr R1
0ERR$: return
042: Get time from TCP/IP stack clock into timestamp buffer
Command code 042, this command uses the TCP/IP stack clock as a source for filling the timestamp buffer.
Example program:
; trap 60 - reading clock data from TCP/IP stack
; R3 - buffer address where to read
; result in R3 address if successful. R3=0 if error
GetDateFromLAN: push R0
push R1
push R2
call AZreset; reset
tst R1
bne G60ERR
MOV #AZ$CSR,R1
mov #42,(R1)
G60: TSTB (R1); check execution result
BPL G60; wait
mov #32,(R1)
1$: TSTB (R1); check execution result
BPL 1$; wait
TST (R1)+; increment
mov R3,R0; remember R3 address
mov #10.,R2; читаем 10 слов
2$: mov (R1),(R3)+; read block of words into memory
sob R2,2$
mov R0,R3; successful, return address to R3
br 0END$
G60ERR: CLR R3
0END$: pop R2
pop R1
pop R0
return
All commands set the ready bit upon completion.
AZБК® specific commands
These commands are intended for operation of the AZБК® controller, developed for the BK series of computers - BK-0010/BK-0010.01/BK-0011M.
Other AZ® controllers ignore these commands.
037: Restart of the AZБК® controller and the entire computer
Command code 037, this command restarts the AZ® microcontroller, which also causes a restart of the BK-0010/BK-0010.01/BK-0011M itself
Example program
AZ$DR = 177222; data register (DR)
; trap 57 - full restart
AZcouldReboot: call AZreset; reset AZ so it is ready to receive
mov #037,@#AZ$CSR
return
044: Saving a screenshot to a file
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
service memory page 76(8) is used as parameters
Structure of filling information about a screenshot
typedef __packed struct screen_header
{
unsigned short int tag; // must be equal to 0240
unsigned int begin_adress; // start address in words - forward task in physical addresses
unsigned int length; // length in words - forward task in physical addresses
unsigned short int begin_page; // start page - number - forward task in page numbers
unsigned short int len_pages; // number of pages - forward task in page numbers
unsigned short int R177300; //
unsigned short int R177302; //
unsigned short int R177304; //
unsigned short int R177306; //
unsigned short int R177310; //
unsigned short int R177312; //
unsigned short int R177314; //
unsigned short int R177316; //
unsigned short int R177320; //
unsigned short int R177322; //
unsigned short int R177324; //
unsigned short int R177326; //
unsigned short int R177330; //
unsigned short int R177332; //
unsigned short int R177334; //
unsigned short int R177336; //
unsigned short int R177340; // - Window activation control register - window masks
unsigned short int R177342; // - Control register r/o per window
unsigned short int R177344; // - Shadow window control register - window masks
unsigned short int R177346; // - Mapper control register
unsigned short int R177350; // - copy by record register 177130 in memory management write mode in SMK
unsigned short int R177352; // - copy by record register 177716 in memory management write mode in BK11M
unsigned short int R177230; // - control register
unsigned short int R177232; // - display start page number register - upper page (layer 0)
unsigned short int R177240; // - display start page number register - upper page (layer 1)
unsigned short int R177242; // - display start page number register - upper page (layer 2)
unsigned short int R177244; // - vertical scroll register layer 2
unsigned short int R177246; // - vertical scroll register layer 1
unsigned short int R177250; // - vertical scroll register layer 0
unsigned short int R177252; // - horizontal scroll register layer 0
unsigned short int R177254; // - horizontal scroll register layer 1
unsigned short int R177256; // - horizontal scroll register layer 2
unsigned short int paldata[338]; //
} screen_header_t;
Example code for filling a memory page
; 76th page map - we prepare data for the screenshot command there
SCR_PAGE = 130000 ; we temporarily attach the 76th page to the 77th - that is, into the 130000 window
SCR_TAG = SCR_PAGE+0 ; here is the input - 240 - 1 word
SCR_ADDR_CONF = SCR_TAG+2 ; here is the command with addresses - address+length 24 bits - 4 words
SCR_PAGE_CONF = SCR_ADDR_CONF+8. ; here is the command with pages - the starting page and the number of pages - 2 words
SCR_MEM_CONF = SCR_PAGE_CONF+4. ; here is the memory configuration from the registers - 22 words
SCR_VGA_CONF = SCR_MEM_CONF+44. ; here video controller configuration - 10 words
SCR_PAL = SCR_VGA_CONF+20. ; here 338. values (words) of palette 338 words
;--------------------------------------------------
; trap 41 - preparation of default information for screenshot functionality
PrepSRC: jsr R5, PUSHA ; batch saving of registers
mov @#177326,-(SP) ; save page 130k which was before the call
mov #76,@#177326 ; hook 76th page into window
mov #100377,R3 ; constant-filler
mov #SCR_PAGE,R4
mov #2047.,R2
4$: mov R3,(R4)+
sob R2,4$
mov #240,@#SCR_TAG ; put the tag
; clean the address section - default is automatic address detection
clr R3
mov #SCR_ADDR_CONF,R4
mov #10,R2
2$: mov R3,(R4)+
sob R2,2$
; memory configuration - default
mov #SCR_MEM_CONF,R4
mov #30,(R4)+ ;177300
mov #31,(R4)+ ;177302
mov #32,(R4)+ ;177304
mov #33,(R4)+ ;177306
mov #04,(R4)+ ;177310
mov #05,(R4)+ ;177312
mov #06,(R4)+ ;177314
mov #07,(R4)+ ;177316
mov #20,(R4)+ ;177320
mov #21,(R4)+ ;177322
mov #22,(R4)+ ;177324
mov #23,(R4)+ ;177326
mov #120,(R4)+ ;177330
mov #121,(R4)+ ;177332
mov #110,(R4)+ ;177334
mov #100,(R4)+ ;177336
mov #170000,(R4)+ ;177340
mov R3,(R4)+ ;177342
mov #7777,(R4)+ ;177344
mov #40404,(R4)+ ;177346
mov R3,(R4)+ ;177350
mov #16000,(R4)+ ;177352
;Video controller configuration - default
; 177230-177256
mov #SCR_VGA_CONF,R4
mov #12201,(R4)+ ;177230
mov #4,(R4)+ ;177232
mov R3,(R4)+ ;177240
mov R3,(R4)+ ;177242
mov R3,(R4)+ ;177244
mov R3,(R4)+ ;177246
mov R3,(R4)+ ;177250
mov R3,(R4)+ ;177252
mov R3,(R4)+ ;177254
mov R3,(R4)+ ;177256
; download the palette - take the default one from this ROM
mov #SCR_PAL,R4
mov #PalData,R2
mov #338.,R3
1$: mov (R2)+,(R4)+
sob R3,1$
mov (SP)+,@#177326; return the page from which the call was made
return
There are three options for specifying memory areas.
- specify the address and length 24-bit - see format, if they are not there - the system looks further
- specify the page number and page quantity, if they are not there
- the system looks further - that is, it makes a screenshot based on the data about registers 177230, etc.
The screenshot is saved in the format
- page 76 - its first kilobyte
- the memory image itself (if the mode is layered - then all three layers)
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
0:/SCREENS/
name format - DDHHMISS.SCR
where DD is two digits of the day of the month, HH is the hour, MI is the minute, SS is the second
If an error occurs during the command execution, the name will be "ERROR *"
for example
"ERROR f_open 6"
Example program
;--------------------------------------------------
; 76th page map - we prepare data for the screenshot command there
SCR_PAGE = 130000 ; we temporarily attach the 76th page to the 77th - that is, into the 130000 window
SCR_TAG = SCR_PAGE+0 ; here is the input - 240 - 1 word
SCR_ADDR_CONF = SCR_TAG+2 ; here is the command with addresses - address+length 24 bits - 4 words
SCR_PAGE_CONF = SCR_ADDR_CONF+8. ; here is the command with pages - the starting page and the number of pages - 2 words
SCR_MEM_CONF = SCR_PAGE_CONF+4. ; here is the memory configuration from the registers - 22 words
SCR_VGA_CONF = SCR_MEM_CONF+44. ; here video controller configuration - 10 words
SCR_PAL = SCR_VGA_CONF+20. ; here 338. values (words) of palette 338 words
;--------------------------------------------------
mov @#177326,R5 ; save page 130k which was before the call
mov #76,@#177336 ; hook the 76th page into the window
;video controller configuration
; 177230-177256
mov #SVGAC,R4
mov @#177230,(R4)+;177230 - control register
mov @#177232,(R4)+;177232 - register - top page (layer 0)
mov @#177240,(R4)+;177240 - register - top page (layer 1)
mov @#177242,(R4)+;177242 - register - top page (layer 2)
mov @#177244,(R4)+;177244 - vertical scroll register layer 2
mov @#177246,(R4)+;177246 - vertical scroll register layer 1
mov @#177250,(R4)+;177250 - vertical scroll register layer 0
mov @#177252,(R4)+;177252 - horizontal scroll register layer 0
mov @#177254,(R4)+;177254 - horizontal scroll register layer 1
mov @#177256,(R4)+;177256 - horizontal scroll register layer 2
mov R5,@#177336; return the page from which the call was made
;-------------------------
MOV #AZ$CSR,R3 ; Preparing controller CSR
MOV #AZ$DR,R4 ; Preparing controller DR
20$: CLR (R3) ; Reset the controller
TSTB (R3)
BPL 20$
; clear the memory block for the name - so that the system makes a default file name
mov #23,(R3) ; command that we will write data to the buffer
128$: TSTB (R3) ; check the result of execution
BPL 128$ ; wait
clr R1
mov #256.,R2 ;
129$: mov R1,(R4) ; give to the controller
sob R2,129$
MOV #044,(R3) ; screenshot command
22$: TSTB (R3) ;
BPL 22$ ;
; get the screenshot name
mov #22,(R3) ; give the read memory block from the buffer to the bus
121$: tstb (R3) ; check the result of execution
bpl 121$ ; wait
mov #BUF,R1
mov #256.,R2 ; read 256. words; the first word is the result of reading
122$: mov (R4),(R1)+ ; read a block of words into memory
sob R2,122$
.PRINT #RESOK
.Print #BUF
mov #110,@#AZ$CSR; enable the network by default, the network should be constantly enabled
.Exit
To unpack a screenshot, you can use this utility - https://master.pdp-11.ru/screen_unpack/
Commands for working with the TCP/IP stack
The following commands operate on the TCP/IP stack information buffer.
040: Get IP address and other TCP/IP stack settings to buffer
Command code 040, this command fills the buffer with information from the TCP/IP stack with current (actual) information.
041: Reading ip address buffer
Command code 041, this command transfers the buffer to the bus
This pair of commands allows you to get current information from the stack
- IP address
- MASK mask
- GW gateway
- NTP address of the NTP server
- DNS1 primary DNS address
- DNS2 backup DNS address
accordingly it is 12 words
Example program:
; result in R3 = 0 if error, otherwise the address where it was read (IPADDDBLOCK)
GetIPaddrs: push R1
push R2
call AZreset; reset
tst R1
bne 0ERR$
MOV #AZ$CSR,R1
mov #40,(R1)
0$: TSTB (R1); read addresses into its memory
BPL 0$; wait
mov #41,(R1)
1$: TSTB (R1); prepare buffer
BPL 1$; wait
TST (R1)+; increment
mov #IPADDDBLOCK,R3
mov #12.,R2
2$: mov (R1),(R3)+; read block of words into memory
sob R2,2$
mov #IPADDDBLOCK,R3; success
br 0END$
0ERR$: CLR R3
0END$: pop R2
pop R1
return
Example return data:
043: Read MAC address into ip buffer
Command code 043, this command reads the current actual MAC address into the IP address buffer i.e. first 043 and then 041 commands
Example program:
; result in R3 = 0 if error, otherwise the address where it was read (IPADDDBLOCK)
GetMACaddrs: push R1
push R2
call AZreset ; reset
tst R1
bne 0ERR$
MOV #AZ$CSR,R1
mov #43,(R1)
0$: TSTB (R1) ; read addresses into its memory
BPL 0$ ; wait
mov #41,(R1)
1$: TSTB (R1) ; prepare buffer
BPL 1$ ; wait
TST (R1)+ ; increment
mov #IPADDDBLOCK,R3
mov #12.,R2
2$: mov (R1),(R3)+ ; read block of words into memory
sob R2,2$
mov #IPADDDBLOCK,R3; success
br 0END$
0ERR$: CLR R3
0END$: pop R2
pop R1
retur
Commands for working with a MicroSD card at the file level
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.
These commands use a 256-word buffer that is used in the interface for working with non-volatile memory (see commands 022 023 above)
Limitations - the length of the full path to the file is 256 bytes
050: Set the name of the file we will read
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.
051: Get file size for reading (or its status) on BUS
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).
The error generation looks like this:
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
FFres = FatFS error
FR_OK = 0, /* (0) Succeeded */
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
FR_INT_ERR, /* (2) Assertion failed */
FR_NOT_READY, /* (3) The physical drive cannot work */
FR_NO_FILE, /* (4) Could not find the file */
FR_NO_PATH, /* (5) Could not find the path */
FR_INVALID_NAME, /* (6) The path name format is invalid */
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
FR_EXIST, /* (8) Access denied due to prohibited access */
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
FR_NOT_ENABLED, /* (12) The volume has no work area */
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_LOCK */
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
} FRESULT;
http://elm-chan.org/fsw/ff/doc/open.html
052: Read a block of a set file into a buffer
Command code 052, this command reads a file into a non-volatile memory buffer.
As a result, the file reading scheme looks like this
023 - fill the file name into the buffer
050 - set the file for reading
051 - read the file length or file open error
if there is an error - repeat 023 050 051 from the beginning
if everything is ok - start reading the file
052 - read the file block into the buffer
022 - take data from the buffer
repeat the 052 022 pairs the required number of times in order to read the entire file
once the file is read - the last 052 command will close it automatically.
Example program:
; load the file name into the buffer
7$: mov #23,(R1); command to write data to the buffer
5$: TSTB (R1); check execution result
BPL 5$ ; wait
TST (R1)+; increment
mov #FILNM,R3
1$: mov (R3),(R1); send to controller
tst (R3)+
bne 1$
tst -(R1); decrement
; set the file for reading
mov #50,(R1); set file for reading
2$: TSTB (R1); check execution result
BPL 2$ ; wait
; read the file length
mov #51,(R1); set file for reading
3$: TSTB (R1); check execution result
BPL 3$ ; wait
TST (R1)+; increment
mov #FILSZ,R3
mov (R1),(R3)+; read from controller
mov (R1),(R3); read from controller
; display the file length on the screen
clr R0
mov #FILSZ+2,R3
mov (R3),R1
call DNOZ
mov -(R3),R1
call DNOZ
.print #STMS2
; read the file
mov R1,R4; R1 holds the file length
MOV #AZ$CSR,R1
mov #BUFFL,R5
bit #1,R4; if an odd number of bytes
beq 47$
inc R4; add 1 more byte as we read words
47$: tst R4
beq 45$ ; nothing left to read - exit
mov #52,(R1); read block into buffer
4$: TSTB (R1); check execution result
BPL 4$ ; wait
mov #22,(R1); start reading buffer
51$: TSTB (R1); check execution result
BPL 51$ ; wait
cmp R4,#512.; compare with buffer size in bytes
Blos 44$ ; less than buffer size left
.print #STMS1
mov #256.,R2
TST (R1)+; move to data register
46$: mov (R1),(R5)+; read into buffer
sob R2,46$
sub #512.,R4; subtract
TST -(R1); move to command register
br 47$
44$: .print #STMS3
mov R4,R2
asr R2 ; /2 since reading words
TST (R1)+; move to data register
43$: mov (R1),(R5)+; read into buffer
sob R2,43$
45$: clr (R5); set end of file marker
; file read - display on screen
.print #STMS4
.print #BUFFL
.print #STMS5
mov #110,@#AZ$CSR; enable network
.Exit
053: set the name of the file that we will write
Command code 053, this command opens a file for writing, receives opening parameters (or errors).
054: Set file length
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.
055: write data from buffer to file
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.
The command flow chart for writing is as follows
023 - fill the file name into the buffer
053 - set the file for reading
051 - file opening/creation status
if an error - repeat from the beginning 023 053 051
if everything is ok - move on
054 - set the file length, i.e. we must immediately declare what the file length will be
023 - fill the data block into the buffer
055 - write from the buffer to the file
repeat the 023 055 pairs the required number of times in order to write the entire file
once the file is written - the last 055 command will close it automatically
Example program:
MOV #AZ$CSR,R1
17$: mov #23,(R1); command to write data to the buffer
15$: TSTB (R1) ; check execution result
BPL 15$ ; wait
TST (R1)+ ; move to data register
mov #FILNM2,R3
11$: mov (R3),(R1); send to controller
tst (R3)+
bne 11$
tst -(R1) ; move to command register
; set the file for writing
mov #53,(R1); set file for writing
12$: TSTB (R1) ; check execution result
BPL 12$ ; wait
; read file creation status
mov #51,(R1)
13$: TSTB (R1) ; check execution result
BPL 13$ ; wait
TST (R1)+ ; move to data register
mov #STATS,R3
mov (R1),(R3)+; read from controller
mov (R1),(R3); read from controller
; check here - if the file is created, both words should be zero
mov #STATS,R3
TST (R3)+
BNE 66$
TST (R3)
BEQ 60$
66$: .print #ERRMS1 ; print error
.exit
60$: MOV #AZ$CSR,R1
mov #54,(R1); set file length to be written
23$: TSTB (R1) ; check execution result
BPL 23$ ; wait
TST (R1)+ ; move to data register
mov #FILSZ,R3
mov (R3)+,(R1); write to controller
mov (R3),(R1); write to controller
tst -(R1) ; move to command register
; write file
mov @#FILSZ,R4; file length
MOV #AZ$CSR,R1
mov #BUFFL,R5; file buffer
bit #1,R4 ; if an odd number of bytes
beq 147$
inc R4 ; add 1 more byte since reading words
147$: tst R4 ; check length
beq 145$ ; nothing left to write - exit
mov #23,(R1); write to buffer
151$: TSTB (R1) ; check execution result
BPL 151$ ; wait
cmp R4,#512.; compare with buffer size in bytes
Blos 144$ ; less than buffer size left
.print #STMS6 ; writing full block
mov #256.,R2
TST (R1)+ ; move to data register
146$: mov (R5)+,(R1); write to controller buffer
sob R2,146$
sub #512.,R4; subtract
TST -(R1) ; move to command register
mov #55,(R1); write buffer to file
104$: TSTB (R1) ; check execution result
BPL 104$ ; wait
br 147$ ; loop back
144$: .print #STMS7 ; writing last block
TST (R1)+ ; move to data register
mov R4,R2
asr R2 ; /2 since writing words
143$: mov (R5)+,(R1); write to controller buffer
sob R2,143$
TST -(R1) ; move to command register
mov #55,(R1); write last buffer to file
105$: TSTB (R1) ; check execution result
BPL 105$ ; wait
145$: .print #STMSE ; end
mov #110,@#AZ$CSR; enable network
.Exit ; exit
the example is completely in the form of the RT11 utility and is posted here
056: Get data on the size of the map into the sizecard buffer
Command code 056, this command reads the parameters of the MicroSD card into the sizecard buffer
057: Reading sizecard buffer
Command code 057, this command returns the sizecard buffer (2 words)
sizecard buffer contains 2 words 16bit
first word - total card size available for FAT in MB
second word - free card size in MB
Example program:
; result in R1 - total; R2 - free
GetSizeSD: call AZreset ; reset
tst R1
bne 0ERR$
MOV #AZ$CSR,R1
mov #56,(R1)
1$: TSTB (R1) ; prepare buffer
BPL 1$ ; wait
mov #57,(R1)
2$: TSTB (R1) ; prepare buffer
BPL 2$ ; wait
mov @#AZ$DR ,R1 ; total megabytes
mov @#AZ$DR ,R2 ; free megabytes
return
clr R1
clr R2
return
Example data
035521 - total megabytes on the card - 15185.
035417 - free megabytes - 15119.
Hall of Fame API Command Block
This block of commands is intended for interaction with the server Hall of Fame
025: Инициализация Hall of Fame (HOF)
Command code 025, this command establishes a connection to the Hall of Fame server, initializes the encrypted tunnel and prepares the API for work.
Example program:
AZ$DR = 177222 ; data register (DR)
; buffers
SNDBUF: .BLKW 256. ; send buffer
RCVBUF: .BLKW 256. ; receive buffer
SIDMEM: .BLKB 34. ; SID
SIDCST: .ASCII \{"SID":"\ ; SID header
.even
HOFINI: ; HOF initialization
; The result is a JSON response:
; {"SID":"session hash","RESULT":"OK"}
; or an error:
; {"RESULT":"ERROR","DESCRIPTION":"SERVER_ERROR"}
; {"RESULT":"ERROR","DESCRIPTION":"CONNECTION_ERROR"}
; The result is placed in SNDBUF
; R5 indicates success: 1 = SID exists, 0 = no SID
mov R5, -(SP)
mov R4, -(SP)
mov R3, -(SP)
mov R2, -(SP)
mov R1, -(SP)
mov R0, -(SP)
mov #3,R5 ; number of attempts
220$: mov #AZ$CSR,R1
clr (R1) ; Send "Reset" command
221$: tstb (R1) ; Check controller readiness
bpl 221$ ; If not ready, wait
mov #25,(R1) ; initialization - command 025
20$: tstb (R1) ; check execution result
bpl 20$ ; wait
; Retrieve the result
mov #22,(R1) ; output the read memory block from buffer to the bus
21$: tstb (R1) ; check execution result
bpl 21$ ; wait
tst (R1)+ ; increment
mov #SNDBUF,R3
mov #256.,R2 ; read 256 words; first word is the read result
22$: mov (R1),(R3)+ ; read block of words into memory
sob R2,22$
; Determine if SID exists
mov #4,R0
mov #SNDBUF,R1
mov #SIDCST,R2
23$: cmp (R1)+,(R2)+
bne 24$
sob R0,23$
clr R5
inc R5
br 26$ ; success
24$: ; SID not found!
sob R5,220$
clr R5 ; error - no SID
26$: mov (SP)+, R0
mov (SP)+, R1
mov (SP)+, R2
mov (SP)+, R3
mov (SP)+, R4
mov (SP)+, R5
return
026: Exchange with Hall of Fame (HOF)
Command code 026, this command makes a direct exchange with Hall of Fame
Example program:
;technically, this is sending a JSON
;{"SID":"session hash","CMD":"AUTH_USER","NIKNAME":"user nickname","PASSWORD":"user password"}
;the response is also JSON
;{"SID":"session hash","RESULT":"OK","UID":"user hash"}
;or
;{"SID":"session hash","RESULT":"ERROR","DESCRIPTION":"USER_NOT_FOUND_OR_WRONG_PASSWORD"}
;{"SID":"session hash","RESULT":"ERROR","DESCRIPTION":"SERVER_ERROR"}
;{"SID":"session hash","RESULT":"ERROR","DESCRIPTION":"SESSION_NOT_EXISTS_OR_EXPIRED"}
; load command CMD04
mov #CMD04,R1
mov #ADRMEM,R2
add #42.,R2 ; shift the pointer to the SID block length
33$: movb (R1)+,(R2)+
bne 33$
.Print #ADRMEM
.Print #HOF05
; send the command and wait for a response
; load into buffer
MOV #AZ$CSR,R1
331$: TSTB (R1) ; Check controller readiness
BPL 331$ ; If not ready, wait
mov #23,(R1) ; command to write data into buffer
34$: TSTB (R1) ; check execution result
BPL 34$ ; wait
TST (R1)+ ; increment
mov #ADRMEM,R3
mov #256.,R2 ;
35$: mov (R3)+,(R1) ; send to controller
sob R2,35$
tst -(R1) ; decrement
; exchange - command 026
MOV #AZ$CSR,R1
361$: TSTB (R1) ; Check controller readiness
BPL 361$ ; If not ready, wait
mov #26,(R1)
36$: TSTB (R1) ; check execution result
BPL 36$ ; wait
; receive result
371$: TSTB (R1) ; Check controller readiness
BPL 371$ ; If not ready, wait
mov #22,(R1) ; send buffer to the bus
37$: TSTB (R1) ; check execution result
BPL 37$ ; wait
TST (R1)+ ; increment
mov #ADRMEM,R3
mov #256.,R2 ; read 256 words; first word is the read result
38$: mov (R1),(R3)+ ; read block of words into memory
sob R2,38$