I've coded a SIC assembler and everything seems to be working fine except for the I/O aspect of it.
I've loaded the object code into memory (converted char format into machine representation), but when I call SICRun(); to execute the code, I get an error stating "devf1 cannot be found".
I know this is related to the input/output device instructions in the source code.
The c file states that it depends on external files, most notably, Dev[6]. Am I supposed to create this myself? My instructor did not give us any other files to work with. Any insight?
Example: TD OUTPUT ;TEST OUTPUT DEVICE
This directory contains the source code (source.asm), header file (sic.h) and the SIC simulator (sicengine.c)
From the sicengine.c source file it looks as though the devf1 (also dev2/dev3) file is expected to exist so this 'input device' can be read from (fopen is passed "r" as a parameter):
if (opcode == 216) { /* RD */
/* ... */
if ((Dev[Devcode] = fopen(SICFile[Devcode],"r")) == NULL) {
printf("cannot open file %s\n", SICFile[Devcode]);
exit(1);
}
The comment in the code about depending on file Dev[6] is ambiguous. It really means the names of the files in the Dev array, which are devf1, devf2 and devf3 (input devices) and devf04, devf05 and devf05 (output devices).
I would suggest creating files devf1, devf1 and devf3.
Related
I'm working on an ESP32 platform with IDF 4.1, using code like this:
struct dirent * dirent;
while((dirent = readdir(dir)) != nullptr) {
ESP_LOGI("ConfigServer", "Found %s, id %d, type %d", dirent->d_name, dirent->d_ino, dirent->d_type);
if(dirent->d_name[0] == '\377') {
++invalid_ctr;
} else {
// do something with the file info
}
}
closedir(dir);
I had to add the bit where invalid_ctr is incremented, because I started getting loads of iterations where dirent->d_name was "\377\377\377\377\377\377\377\377.\377\377\377" (rendered as inverse-video "?" characters in my terminal). The code not shown involves feeding that name to stat(), which would return the same values as the last valid file encountered. The log entry would look like this:
I (608261) ConfigServer: Found ��������.���, id 0, type 2
Type 2 represents a directory. This is happening on a partition on the onboard flash, formatted by the IDF library's "format if mount failed" option at mount. So perhaps my assumption of FAT32 is invalid. I do know that IDF uses FatFs internally.
Is this indicative of an error on the filesystem? Is it expected to need to filter out such trash on a typical iteration with readdir()?
The FAT component in ESP IDF has support for long file names disabled by default. Run idf.py menuconfig, then "Component config → FAT Filesystem support → Long filename support" to enable it.
I'm making some extensions to the kernel module nandsim, and I'm having trouble finding the correct way to test if a file exists before opening it. I've read this question, which covers how the basic open/read/write operations go, but I'm having trouble figuring out if and how the normal open(2) flags apply here.
I'm well aware that file reading and writing in kernel modules is bad practice; this code already exists in the kernel and is already reading and writing files. I am simply trying to make a few adjustments to what is already in place. At present, when the module is loaded and instructed to use a cache file (specified as a string path when invoking modprobe), it uses filp_open() to open the file or create it if it does not exist:
/* in nandsim.c */
...
module_param(cache_file, charp, 0400);
...
MODULE_PARM_DESC(cache_file, "File to use to cache nand pages instead of memory");
...
struct file *cfile;
cfile = filp_open(cache_file, O_CREAT | O_RDWR | O_LARGEFILE, 0600);
You might ask, "what do you really want to do here?" I want to include a header for the cache file, such that it can be reused if the system needs to be reset. By including information about the nand page geometry and page count at the beginning of this file, I can more readily simulate a number of error conditions that otherwise would be impossible within the nandsim framework. If I can bring down the nandsim module during file operations, or modify the backing file to model a real-world fault mode, I can recreate the net effect of these error conditions.
This would allow me to bring the simulated device back online using nandsim, and assess how well a fault-tolerant file system is doing its job.
My thought process was to modify it as follows, such that it would fail trying to force creation of a file which already exists:
struct file *cfile;
cfile = filp_open(cache_file, O_CREAT | O_EXCL | O_RDWR | O_LARGEFILE, 0600);
if(IS_ERR(cfile)){
printk(KERN_INFO "File didn't exist: %ld", PTR_ERR(cfile));
/* Do header setup for first-time run of NAND simulation */
}
else{
/* Read header and validate against system parameters. Recover operations */
}
What I'm seeing is an error, but it is not the one I would have expected. It is reporting errno 14, EFAULT (bad address) instead of errno 17 EEXIST (File exists). I don't want to run with this because I would like this to be as idiomatic and correct as possible.
Is there some other way I should be doing this?
Do I need to somehow specify that the file path is in user address space? If so, why is that not the case in the code as it was?
EDIT: I was able to get a reliable error by trying to open with only O_RDWR and O_LARGEFILE, which resulted in ENOENT. It is still not clear why my original approach was incorrect, nor what the best way to accomplish my goal is. That said, if someone more experienced could comment on this, I can add it as a solution.
Indeed, filp_open expects a file path which is in kernel address space. Proof is the use of getname_kernel. You can mimic this for your use case with something like this:
struct filename *name = getname(cache_file);
struct file *cfile = ERR_CAST(name);
if (!IS_ERR(name)) {
cfile = file_open_name(name, O_CREAT | O_EXCL | O_RDWR | O_LARGEFILE, 0600);
if (IS_ERR(cfile))
return PTR_ERR(cfile);
putname(name);
}
Note that getname expects a user-space address and is the equivalent of getname_kernel.
My application/device is running on an ARM Cortex M3 (STM32), without OS but with a FatFs) and needs to access many resources files (audio, image, etc..)
The code runs from internal flash (ROM, 256Kb).
The resources files are stored on external flash (SD card, 4Gb).
There is not much RAM (32Kb), so malloc a complete file from package is not an option.
As the user has access to the resources folder for atomic update, I would like to package all theses resources files in a single (.dat, .rom, .whatever)
So the user doesn't mishandle theses data.
Can someone point me to a nice solution to do so?
I don't mind remapping fopen, fread, fseek and fclose in my application, but I would not like starting from scratch (coding the serializer, table of content, parser, etc...). My system is quite limited (no malloc, no framework, just stdlib and FatFs)
Thanks for any input you can give me.
note: I'm not looking for a solution where the resources are embedded IN the code (ROM) as obviously they are way too big for that.
It should be possible to use fatfs recursively.
Drive 0 would be your real device, and drive 1 would be a file on drive 0. You can implement the disk_* functions like this
#define BLOCKSIZE 512
FIL imagefile;
DSTATUS disk_initialize(BYTE drv) {
UINT r;
if(drv == 0)
return SD_initialize();
else if(drv == 1) {
r = f_open(&image, "0:/RESOURCE.DAT", FA_READ);
if(r == FR_OK)
return 0;
}
return STA_NOINIT;
}
DRESULT disk_read(BYTE drv, BYTE *buff, DWORD sector, DWORD count) {
UINT br, r;
if(drv == 0)
return SD_read_blocks(buff, sector, count);
else if(drv == 1) {
r = f_seek(&imagefile, sector*BLOCKSIZE);
if(r != FR_OK)
return RES_ERROR;
r = f_read(&imagefile, buff, count*BLOCKSIZE, &br);
if((r == FR_OK) && (br == count*BLOCKSIZE))
return RES_OK;
}
return RES_ERROR;
}
To create the filesystem image on Linux or other similar systems you'd need mkfs.msdos and the mtools package. See this SO post on how to do it. Might work on Windows with Cygwin, too.
To expand on what Joachim said above:
Popular choices of uncompressed (sometimes) archive formats are cpio, tar, and zip. Any of the 3 would work just fine.
Here are a few more in-depth comments on using TAR or CPIO.
TAR
I've used tar before for the exact purpose, on an stm32 with FatFS, so can tell you it works. I chose it over cpio or zip because of its familiarity (most developers have seen it), ease of use, and rich command line tools.
GNU Tar gives you fine-grained control over order in which the files are placed in the archive and regexes to manipulate file names (--xform) or --exclude paths. You can pretty much guarantee you can get exactly the archive you're after with nothing more than GNU Tar and a makefile. I'm not sure the same can be said for cpio or zip.
This means it worked well for my build environment, but your requirements may vary.
CPIO
The cpio has a much worse/harder to use set of command line tools than tar in my opinion. Which is why I steer clear of it when I can. However, its file format is a little lighter-weight and might be even simpler to parse (not that tar is hard).
The Linux kernel project uses cpio for initramfs images, so that's probably the best / most mature example on the internet that you'll find on using it for this sort of purpose.
If you grab any kernel source tree, the tool usr/gen_init_cpio.c can used to generate a cpio from a cpio listing file format described in that source file.
The extraction code is in init/initramfs.c.
ZIP
I've never used the zip format for this sort of purpose. So no real comment there.
Berendi found a very clever solution: use the existing fat library to access it recursively!
The implementation is quite simple, and after extensive testing, I'd like to post the code to use FatFs recursively and the commands used for single file fat generation.
First, lets generate a 100Mb FAT32 file:
dd if=/dev/zero of=fat.fs bs=1024 count=102400
mkfs.vfat -F 32 -r 112 -S 512 -v fatfile.fs
Create/push content into it:
echo HelloWorld on Virtual FAT >> helloworld.txt
mcopy -i fatfile.fs helloworld.txt ::/
Change the diskio.c file, to add Berendi's code but also:
DSTATUS disk_status ()
{
DSTATUS status = STA_NOINIT;
switch (pdrv)
{
case FATFS_DRIVE_VIRTUAL:
printf("disk_status: FATFS_DRIVE_VIRTUAL\r\n" );
case FATFS_DRIVE_ATA: /* SD CARD */
status = FATFS_SD_SDIO_disk_status();
}
}
Dont forget to add the enum for the drive name, and the number of volumes:
#define _VOLUMES 2
Then mount the virtual FAT, and access it:
f_mount(&VirtualFAT, (TCHAR const*)"1:/", 1);
f_open(&file, "1:/test.txt", FA_READ);
Thanks a lot for your help.
So the C program i am building has to be placed within the PA_mobile file on the desktop of my MAC computer (OS X 10.9.4) so it can access the scripts and text file caches that are associated with it.
now when my program launches, it validates the current working directory, comparing it to a string variable which contains the correct working directory, and requests a working directory change if the program is not located in the desktop.
here is the code associated with the directory path validation:
char* cwd_2;
char buff[PATH_MAX + 1];
cwd_2 = getcwd( buff, PATH_MAX + 1 );
char desired_working_directory[50] = "/Users/haydn/Desktop/PA_mobile";
if( cwd_2 != NULL )
{
printf("\n-----------------------------------");
printf("\nCurrent working directory: [%s]\n", cwd_2 );
usleep(10000);
printf("Desired working directory: [%s]\n",desired_working_directory);
usleep(50000);
if(strcmp(desired_working_directory, cwd_2) == 0)
{
fflush(stdout);
printf("Appropriate Working Directory achieved!\n");
printf("-----------------------------------");
}
else
{
printf("Critical Error: inapropriate working directory!\n");
printf("please relocate the 'PA_mobile' file to [/users/user/Desktop] and re-launch\n");
getchar();
exit();
}
}
The program compares the current working directory path as determined by the cwd_2 variable and compares it to the desired working directory path as described in the desired_working_directory variable.
Now here is what the program displays when run:
-----------------------------------
Current working directory: [/users/haydn/DEsktop/PA_mobile]
Desired working directory: [/Users/haydn/Desktop/PA_mobile]
Critical Error: inapropriate working directory!
please relocate file to [/users/haydn/Desktop] and re-launch
Notice that the current working directory displayed (which is determined by the getcwd( buff, PATH_MAX + 1 ); function describes the desktop file as DEsktop instead of Desktop as it is meant to be. i have checked the actual desktop folder, it is spelt without a capital E, and the capital E appears to be causing the problem, and exists solely within this code, can anyone help me?
If you run diskutil list / (or diskutil info / on latter variants of Mac OSX) you'll see whether the file system is case sensitive or not. You may need to do it on a file system lower in the hierarchy than / if those directories are in a separate file system.
You could also try the following sequence of commands:
touch /Users/haydn/Desktop/xyzzy
touch /Users/haydn/Desktop/xyzzY
ls /Users/haydn/Desktop/xyzz*
and see how many files are there. One means case-insensitive, two means case sensitive.
It's the file system personality that dictates this and, unless it mentions case sensitivity, you can assume it's not.
In which case, there is no distinction between these:
/users/haydn/DEsktop/PA_mobile
/Users/haydn/Desktop/PA_mobile
^ ^
despite the differing letters.
If you've established that this is the problem, your code should really be adjusted to handle both cases. This question (and its answers) offer ways to do this from within your code.
I am using VC++ 2008 express edition for C. When I try to run this:
/* Demonstrates printer output. */
#include <stdio.h>
main()
{
float f = 2.0134;
fprintf(stdprn, "This message is printed.\n\n");
fprintf(stdprn, "And now some numbers:\n\n");
fprintf(stdprn, "The square of %f is %f.", f, f*f);
/* Send a form feed */
fprintf(stdprn, "\f");
}
I get four of these errors: error C2065: 'stdprn' : undeclared identifier.
On this forum, they wrote that it works to define the printer as follows:
FILE *printer;
printer = fopen("PRN", "w");
EDIT
It builds with a warning that fopen is unsafe. When it runs the error appears:
Debug Assertion fails.
File: f:\dd\vctools\crt_bld\self_x86\crt\src\fprintf.c
Line: 55
Expression: (str != NULL)
The stdprn stream was an extension provided by Borland compilers - as far as I know, MS have never supported it. Regarding the use of fopen to open the printer device, I don't think this will work with any recent versions of Windows, but a couple of things to try:
use PRN: as the name instead of PRN (note the colon)
try opening the specific device using (for example) LPT1: (once again, note the colon). This will of course not work if you don't have a printer attached.
don't depend on a printer dialog coming up - you are not really using the WIndows printing system when you take this approach (and so it probably won't solve your problem, but is worth a try).
I do not have a printer attached, but I do have the Microsoft XPS document writer installed, s it shoulod at least bring up the standard Windows Print dialog from which one can choose the printer.
No. It wouldn't bring up a dialogue. This is because you are flushing data out to a file. And not going through the circuitous Win32 API.
The print doesn't work because the data is not proper PDL -- something that the printer could understand. For the print to work fine, you need to push in a PDL file, with language specific constructs. This varies from printer to printer, a PS printer will need you to push in a PostScript snippet, a PCL -- a PCL command-set and in case of MXDW you will have to write up XML based page description markup and create a zip file (with all resources embedded in it) i.e. an XPS file to get proper printout.
The PDL constructs are important because otherwise the printer doesn't know where to put the data, which color to print it on, what orientation to use, how many copies to print and so on and so forth.
Edit: I am curious why you are doing this. I understand portability is probably something you are trying to address. But apart from that, I'd like to know, there may be better alternatives available. Win32 Print Subsytem APIs are something that you ought to lookup if you are trying to print programmatically on Windows with any degree of fidelity.
Edit#2:
EDIT It builds with a warning that fopen is unsafe.
This is because MS suggests you use the safer versions nowadays fopen_s . See Security Enhancements in the CRT.
When it runs the error appears:
Debug Assertion fails. File: f:\dd\vctools\crt_bld\self_x86\crt\src\fprintf.c Line: 55
Expression: (str != NULL)
This is because fopen (whose return value you do not check) returns a NULL pointer. The file open failed. Also, if it did succeed a matching fclose call is called for.
There's no such thing as stdprn in ANSI C, it was a nonstandard extension provided by some compilers many years ago.
Today to print you have to use the specific APIs provided on your platform; to print on Windows you have to use the printing APIs to manage the printing of the document and obtain a DC to the printer and the GDI APIs to perform the actual drawing on the DC.
On UNIX-like OSes, instead, usually CUPS is used.
You can substitute the printer using this command with net use, see here on the MSDN kb
NET USE LPT1 \\server_name\printer_name
There is an excellent chapter on printing in DOS using the BIOS, ok, its a bit antiquated but interesting to read purely for nostalgic sake.
Onto your problem, you may need to use CreateFile to open the LPT1 port, see here for an example, I have it duplicated it here, for your benefit.
HANDLE hFile;
hFile = CreateFile("LPT1", GENERIC_WRITE, 0,NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
// handle error
}
OVERLAPPED ov = {};
ov.hEvent = CreateEvent(0, false, false, 0);
char szData[] = "1234567890";
DWORD p;
if (!WriteFile(hFile,szData, 10, &p, &ov))
{
if (GetLastError() != ERROR_IO_PENDING)
{
// handle error
}
}
// Wait for write op to complete (maximum 3 second)
DWORD dwWait = WaitForSingleObject(ov.hEvent, 3000);
if (dwWait == WAIT_TIMEOUT)
{
// it took more than 3 seconds
} else if (dwWait == WAIT_OBJECT_0)
{
// the write op completed,
// call GetOverlappedResult(...)
}
CloseHandle(ov.hEvent);
CloseHandle(hFile);
But if you insist on opening the LPT1 port directly, error checking is omitted...
FILE *prn = fopen("lpt1", "w");
fprintf(prn, "Hello World\n\f");
fclose(prn);
Hope this helps,
Best regards,
Tom.