The page fault handler does not tell you directly. What you need to do is look at the existing permissions on the page, and conclude that the fault is due to the missing permission. If the page has no permissions, you should add the PROT_READ permissions and return. If the page has PROT_READ, then you should add PROT_WRITE and continue. (PROT_EXEC shouldn't happen in this assignment.)
Call page_table_get_entry(page,&frame,&bits). If the given page has non-zero permission bits, then it resides in the indicated frame number. If the permission bits are zero, then it is not in memory, and the frame number is irrelevant.
Yes, you should create a frame table that keeps track of the state of each frame. That will make it easy to find a free frame for replacement.
In a real operating system, the contents of a page should initially be all zeroes. As it turns out, it does not matter for this project, since each program fills in each page with its own data before attempting to read it.
It is possible for an instruction to touch more than one page of memory. For example, MOV a, b could touch three pages of memory if a and b are in different pages, and the executable instruction is in a third page. If there is only one page of physical memory, you will get an endless number of page faults as you switch between the desired pages. Just start your testing at a minimum of three pages.