diff --git a/.github/workflows/make.yml b/.github/workflows/make.yml index 015d564..326c95a 100644 --- a/.github/workflows/make.yml +++ b/.github/workflows/make.yml @@ -13,12 +13,18 @@ env: jobs: build: + name: Build (PAGING=${{ matrix.demand_paging }}, FLOAT=${{ matrix.support_fp }}) # This uses a Unix Makefile and should run on Linux and Mac runs-on: ubuntu-latest + strategy: + matrix: + demand_paging: [0, 1] + support_fp: [0, 1] + steps: - uses: actions/checkout@v2 - name: Build and Test working-directory: ${{github.workspace}}/platforms/unix - run: make test ASAN=1 + run: make test ASAN=1 XCPPFLAGS="-DPF_SUPPORT_FP=${{ matrix.support_fp }} -DPF_DEMAND_PAGING=${{ matrix.demand_paging }} -D_DEFAULT_SOURCE -D_GNU_SOURCE" diff --git a/.github/workflows/make32.yml b/.github/workflows/make32.yml index 862407b..853dc34 100644 --- a/.github/workflows/make32.yml +++ b/.github/workflows/make32.yml @@ -13,9 +13,16 @@ env: jobs: build: + name: Build 32-bit (PAGING=${{ matrix.demand_paging }}, FLOAT=${{ matrix.support_fp }}, CELL=${{ matrix.cell_size }}) # This uses a Unix Makefile and should run on Linux and Mac runs-on: ubuntu-latest + strategy: + matrix: + demand_paging: [0, 1] + support_fp: [0, 1] + cell_size: [4, 8] + steps: - uses: actions/checkout@v2 @@ -24,5 +31,5 @@ jobs: - name: Build and Test working-directory: ${{github.workspace}}/platforms/unix - run: make test WIDTHOPT=-m32 ASAN=1 + run: make test WIDTHOPT=-m32 ASAN=1 XCPPFLAGS="-DPF_SUPPORT_FP=${{ matrix.support_fp }} -DPF_DEMAND_PAGING=${{ matrix.demand_paging }} -DPF_CELL_SIZE=${{ matrix.cell_size }} -D_DEFAULT_SOURCE -D_GNU_SOURCE" diff --git a/.gitignore b/.gitignore index b71a18c..a1359c7 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ fth/fatest1.txt fth/pforth.dic **/.DS_Store build/ +Testing/ CMakeCache.txt CMakeFiles/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e91b8d..0a42c4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,7 +70,7 @@ add_custom_command(OUTPUT ${PFORTH_DIC} COMMAND ./${PFORTH_EXE} -i ${PFORTH_FTH_DIR}/system.fth WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} DEPENDS pforth - COMMENT Building pforth.dic + COMMENT "Building pforth.dic" VERBATIM ) add_custom_target(pforth_dic DEPENDS ${PFORTH_DIC}) @@ -83,7 +83,7 @@ add_custom_command(OUTPUT ${PFORTH_DIC_HEADER} COMMAND ${CMAKE_COMMAND} -E rename pfdicdat.h ../csrc/pfdicdat.h WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} DEPENDS pforth_dic - COMMENT Building pfdicdat.h + COMMENT "Building pfdicdat.h" VERBATIM ) add_custom_target(pforth_dic_header DEPENDS ${PFORTH_DIC_HEADER}) diff --git a/csrc/paging/dmpaging.h b/csrc/paging/dmpaging.h new file mode 100644 index 0000000..34a464d --- /dev/null +++ b/csrc/paging/dmpaging.h @@ -0,0 +1,161 @@ +#ifndef _pf_dmpaging_h +#define _pf_dmpaging_h + +/*************************************************************** +** Include file for PForth Demand Paging +** +** Author: Phil Burk +** Copyright 1994 3DO, Phil Burk, Larry Polansky, David Rosenboom +** +** Permission to use, copy, modify, and/or distribute this +** software for any purpose with or without fee is hereby granted. +** +** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +** THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +** CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +** FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +** CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +** +***************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * To use demand paged memory, enable the code by passing -DPF_DEMAND_PAGING=1 + * to the C compiler. + */ +typedef ucell_t paging_address_t; /** an address in paged memory */ + +/* Basic memory access macros. */ +#if (PF_DEMAND_PAGING == 0) +/* Straight memory access. */ +#define DP_FETCH_U8(address) (*((const uint8_t *)(uintptr_t)(address))) +#define DP_FETCH_U16(address) (*((const uint16_t *)(uintptr_t)(address))) +#define DP_FETCH_CELL(address) (*((const cell_t *)(uintptr_t)(address))) +#define DP_FETCH_FLOAT(address) (*((const PF_FLOAT *)(uintptr_t)(address))) + +#define DP_STORE_U8(address, value) *((uint8_t *)(uintptr_t)(address)) = (uint8_t)(value) +#define DP_STORE_U16(address, value) *((uint16_t *)(uintptr_t)(address)) = (uint16_t)(value) +#define DP_STORE_CELL(address, value) *((cell_t *)(uintptr_t)(address)) = (cell_t)(value) +#define DP_STORE_FLOAT(address, value) *((PF_FLOAT *)(uintptr_t)(address)) = (PF_FLOAT)(value) + +#else /* PF_DEMAND_PAGING */ + +/* Use either physical or paged memory. */ +#define DP_FETCH_U8(address) pfFetchVirtualU8(PTR_TO_VMA(address)) +#define DP_FETCH_U16(address) pfFetchVirtualU16(PTR_TO_VMA(address)) +#define DP_FETCH_CELL(address) pfFetchVirtualCell(PTR_TO_VMA(address)) +#define DP_FETCH_FLOAT(address) pfFetchVirtualFloat(PTR_TO_VMA(address)) + +#define DP_STORE_U8(address, value) pfStoreVirtualU8(PTR_TO_VMA(address), (uint8_t)(value)) +#define DP_STORE_U16(address, value) pfStoreVirtualU16(PTR_TO_VMA(address), (uint16_t)(value)) +#define DP_STORE_CELL(address, value) pfStoreVirtualCell(PTR_TO_VMA(address), (cell_t)(value)) +#define DP_STORE_FLOAT(address, value) pfStoreVirtualFloat(PTR_TO_VMA(address), (PF_FLOAT)(value)) +#endif /* PF_DEMAND_PAGING */ + +uint8_t pfFetchVirtualU8(vm_address_t address); +uint16_t pfFetchVirtualU16(vm_address_t address); +cell_t pfFetchVirtualCell(vm_address_t address); + +void pfStoreVirtualU8(vm_address_t address, uint8_t value); +void pfStoreVirtualU16(vm_address_t address, uint16_t value); +void pfStoreVirtualCell(vm_address_t address, cell_t value); + +#ifdef PF_SUPPORT_FP +PF_FLOAT pfFetchVirtualFloat(vm_address_t address); +void pfStoreVirtualFloat(vm_address_t address, PF_FLOAT value); +#endif /* PF_SUPPORT_FP */ + +/* Memory region locking and unlocking + * A maximum of DP_MAX_REGIONS can be locked at one time. + * The regions cannot overlap. + * Adjacent virtual regions will not be adjacent in physical memory! + */ + +/* maximum number of locked regions */ +#define DP_MAX_REGIONS (4) +/* maximum number of bytes per region */ +#define DP_MAX_REGION_SIZE (256) + +void pfResetLockedMemory(void); + +/** + * Lock a read-only region of demand paging in physical memory. + * Load the data from demand paging if not already loaded. + * A region should only be locked temporarily, for example within + * a single primitive. + * If the virtual memory address is not in paged memory then it will be passed through. + * You cannot lock more than DP_MAX_REGION_SIZE bytes. + * + * @param vp virtual address in physical or paged memory + * @param numBytes number of byte in region + * @return physical address that can be used by normal C code as “const” memory. + */ +const uint8_t *pfLockMemoryReadOnly(vm_address_t vp, uint32_t numBytes); + +/** + * Lock a read-write region of demand paging in physical memory. + * Load the data from demand paging if not already loaded. + * If the virtual memory address is not in paged memory then it will be passed through. + * You cannot lock more than DP_MAX_REGION_SIZE bytes. + * + * @param vp virtual address in physical or paged memory + * @param numBytes number of byte in region + * @return physical address that can be read or written by normal C code. + */ +uint8_t *pfLockMemoryReadWrite(vm_address_t vp, uint32_t numBytes); + +/** + * Release a previously locked region of memory. + * Writable regions will be written back to serial memory. + * Regions may be kept in physical memory on an LRU basis + * to improve performance. + * @param vp virtual address in physical or paged memory + * @param pp physical memory address + * @return negative code if an error occured else zero + */ +int pfUnlockMemory(vm_address_t vp, const uint8_t *pp); + +/** Copy from virtual to physical memory. + * @param destination target address in physical memory + * @param source data address in physical or paged memory + * @return destination + */ +void *pfCopyFromVirtualMemory(void *destination, + vm_address_t source, + uint32_t numBytes); + +/** Copy from physical to virtual memory. + * @param destination target address in physical or paged memory + * @param source in physical memory + * @return destination + */ +vm_address_t pfCopyToVirtualMemory(vm_address_t destination, + const void *source, + uint32_t numBytes); + +/** Free virtual memory. + * @param address may be in physical or paged memory + */ +void pfFreeVirtualMemory(vm_address_t address); + +/** Set a region of virtual memory to a byte value. + * @param destination in physical or paged memory + * @param value fill memory with this value + * @param numBytes how many bytes to set + * @return destination + */ +vm_address_t pfSetVirtualMemory(vm_address_t destination, + uint8_t value, + uint32_t numBytes); + +#ifdef __cplusplus +} +#endif + +#endif /* _pf_dmpaging_h */ diff --git a/csrc/paging/lockpage.c b/csrc/paging/lockpage.c new file mode 100644 index 0000000..4bb2bd6 --- /dev/null +++ b/csrc/paging/lockpage.c @@ -0,0 +1,296 @@ +/*************************************************************** +** Demand Paged Memory Region Locking +** +** Author: Phil Burk +** Copyright 1994 3DO, Phil Burk, Larry Polansky, David Rosenboom +** +** Permission to use, copy, modify, and/or distribute this +** software for any purpose with or without fee is hereby granted. +** +** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +** THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +** CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +** FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +** CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +** +***************************************************************/ + +#include "../pf_all.h" +#include "dmpaging.h" + +#if PF_DEMAND_PAGING == 0 + +void pfResetLockedMemory(void) { +} + +const uint8_t *pfLockMemoryReadOnly(vm_address_t vp, uint32_t numBytes) { + return ((const uint8_t *) (uintptr_t) (vp)); +} + +uint8_t *pfLockMemoryReadWrite(vm_address_t vp, uint32_t numBytes) { + return ((uint8_t *) (uintptr_t) (vp)); +} + +int pfUnlockMemory(vm_address_t vp, const uint8_t *pp) { + return 0; +} + +int pfIsAddressInPagedMemory(vm_address_t p) { + return FALSE; +} + +void *pfCopyFromVirtualMemory(void *destination, + vm_address_t source, + uint32_t numBytes) { + return pfCopyMemory(destination, (const void *) (uintptr_t) source, numBytes); +} + +vm_address_t pfCopyToVirtualMemory(vm_address_t destination, + const void *source, + uint32_t numBytes) { + return PTR_TO_VMA(pfCopyMemory((void *) (uintptr_t) destination, source, numBytes)); +} + +void pfFreeVirtualMemory(vm_address_t address) { + pfFreeMem((void *) (uintptr_t) address); +} + +vm_address_t pfSetVirtualMemory(vm_address_t destination, + uint8_t value, + uint32_t numBytes) { + return PTR_TO_VMA(pfSetMemory((void *) (uintptr_t) destination, value, numBytes)); +} + +#else /* PF_DEMAND_PAGING */ + +#define DP_MAGIC (0x5A9E) + +struct RegionControlBlock { + vm_address_t virtual; + uint32_t magic; + uint32_t length; + uint8_t writable; + uint8_t locked; + uint8_t pad1; + uint8_t pad2; + uint8_t physical[DP_MAX_REGION_SIZE]; +}; + +struct RegionControlBlock sLockedRegions[DP_MAX_REGIONS]; + +void pfResetLockedMemory(void) { + int i; + pfSetMemory(sLockedRegions, 0, sizeof(sLockedRegions)); + for (i = 0; i < DP_MAX_REGIONS; i++) { + struct RegionControlBlock *region = &sLockedRegions[i]; + region->magic = DP_MAGIC; + } +} + +static uint8_t *pfLockMemoryInternal(vm_address_t vp, uint32_t numBytes, int writable) { + int i; + struct RegionControlBlock *region = NULL; + if (pfIsAddressInPagedMemory(vp) == 0) { /* not in paged memory so locking not needed */ + return (uint8_t *) (uintptr_t) vp; + } + PF_ASSERT(numBytes <= DP_MAX_REGION_SIZE); + + /* Find an empty region. */ + for (i = 0; i < DP_MAX_REGIONS; i++) { + if (sLockedRegions[i].locked == 0) { + region = &sLockedRegions[i]; + break; + } + } + if (region == NULL) { + printf("ERROR: no available region!"); + return NULL; + } + + /* Read serial data into the buffer. */ + cell_t numRead = pfReadPagedMemory(®ion->physical[0], vp, numBytes); + if (numRead != numBytes) { + printf("ERROR: could not read data for region!"); + return NULL; + } + + region->virtual = vp; + region->length = (uint32_t) numBytes; + region->locked = 1; + region->writable = writable; + return ®ion->physical[0]; +} + +const uint8_t *pfLockMemoryReadOnly(vm_address_t vp, uint32_t numBytes) { + return ((const uint8_t *)(pfLockMemoryInternal(vp, numBytes, 0))); +} + +uint8_t *pfLockMemoryReadWrite(vm_address_t vp, uint32_t numBytes) { + return pfLockMemoryInternal(vp, numBytes, 1); +} + +int pfUnlockMemory(vm_address_t vp, const uint8_t *pp) { + if (vp == PTR_TO_VMA(pp) ) { + return 0; /* not paged memory */ + } + const size_t kPhysicalOffset = (((uint8_t *)&sLockedRegions[0].physical[0]) + - ((uint8_t *)&sLockedRegions[0])); + struct RegionControlBlock *region = (struct RegionControlBlock *)(uintptr_t)(pp - kPhysicalOffset); + if (region->magic != DP_MAGIC) { + printf("ERROR: physical address was not in a region!\n"); + return -1; + } + if (region->locked == 0) { + printf("ERROR: physical address was not locked!\n"); + return -1; + } + if (region->virtual != vp) { + printf("ERROR: virtual address did not match region\n"); + return -1; + } + if (region->writable != 0) { + /* write back to serial memory */ + cell_t numWritten = pfWritePagedMemory(vp, ®ion->physical[0], region->length); + + if (numWritten != region->length) { + printf("ERROR: virtual could not be written back, only wrote %d bytes\n", + region->length); + return -1; + } + } + region->virtual = PF_VM_NULL; + region->length = 0; + region->locked = 0; + region->writable = 0; + return 0; +} + +uint8_t pfFetchVirtualU8(vm_address_t address) { + if (pfIsAddressInPagedMemory(address)) { + uint8_t value; + pfReadPagedMemory(&value, (paging_address_t) address, sizeof(value)); + return value; + } else { + return *((uint8_t *) (uintptr_t) address); + } +} + +uint16_t pfFetchVirtualU16(vm_address_t address) { + if (pfIsAddressInPagedMemory(address)) { + uint16_t value; + pfReadPagedMemory(&value, (paging_address_t) address, sizeof(value)); + return value; + } else { + return *((uint16_t *) (uintptr_t) address); + } +} + +cell_t pfFetchVirtualCell(vm_address_t address) { + if (pfIsAddressInPagedMemory(address)) { + cell_t value; + pfReadPagedMemory(&value, (paging_address_t) address, sizeof(value)); + return value; + } else { + return *((cell_t *) (uintptr_t) address); + } +} + +#ifdef PF_SUPPORT_FP +PF_FLOAT pfFetchVirtualFloat(vm_address_t address) { + if (pfIsAddressInPagedMemory(address)) { + PF_FLOAT value; + pfReadPagedMemory(&value, (paging_address_t) address, sizeof(value)); + return value; + } else { + return *((PF_FLOAT *) (uintptr_t) address); + } +} +#endif /* PF_SUPPORT_FP */ + +void pfStoreVirtualU8(vm_address_t address, uint8_t value) { + if (pfIsAddressInPagedMemory(address)) { + pfWritePagedMemory((paging_address_t) address, &value, sizeof(value)); + } else { + *((uint8_t *) (uintptr_t) address) = value; + } +} + +void pfStoreVirtualU16(vm_address_t address, uint16_t value) { + if (pfIsAddressInPagedMemory(address)) { + pfWritePagedMemory((paging_address_t) address, &value, sizeof(value)); + } else { + *((uint16_t *) (uintptr_t) address) = value; + } +} +void pfStoreVirtualCell(vm_address_t address, cell_t value) { + if (pfIsAddressInPagedMemory(address)) { + pfWritePagedMemory((paging_address_t) address, &value, sizeof(value)); + } else { + *((cell_t *) (uintptr_t) address) = value; + } +} + +#ifdef PF_SUPPORT_FP +void pfStoreVirtualFloat(vm_address_t address, PF_FLOAT value) { + if (pfIsAddressInPagedMemory(address)) { + pfWritePagedMemory((paging_address_t) address, &value, sizeof(value)); + } else { + *((PF_FLOAT *) (uintptr_t) address) = value; + } +} +#endif /* PF_SUPPORT_FP */ + +void *pfCopyFromVirtualMemory(void *destination, + vm_address_t source, + uint32_t numBytes) { + if (pfIsAddressInPagedMemory(source)) { + pfReadPagedMemory(destination, (paging_address_t) source, numBytes); + return (void *) destination; + } else { + return pfCopyMemory(destination, (void *) (uintptr_t) source, numBytes); + } +} + +vm_address_t pfCopyToVirtualMemory(vm_address_t destination, + const void *source, + uint32_t numBytes) { + if (pfIsAddressInPagedMemory(destination)) { + pfWritePagedMemory((paging_address_t) destination, source, numBytes); + return destination; + } else { + return PTR_TO_VMA(pfCopyMemory((void *) (uintptr_t) destination, source, numBytes)); + } +} + +void pfFreeVirtualMemory(vm_address_t address) { + if (pfIsAddressInPagedMemory(address)) { + pfFreePagedMemory((paging_address_t) address); + } else { + pfFreeMem((void *) (uintptr_t) address); + } +} + +vm_address_t pfSetVirtualMemory(vm_address_t destination, + uint8_t value, + uint32_t numBytes) { + if (pfIsAddressInPagedMemory(destination)) { + /* Set memory in blocks for faster writes. */ + vm_address_t vp = destination; + uint8_t buffer[16]; + pfSetMemory(buffer, value, sizeof(buffer)); + while (numBytes > 0) { + uint32_t bytesToWrite = (numBytes < sizeof(buffer)) ? numBytes : sizeof(buffer); + pfWritePagedMemory(vp, buffer, bytesToWrite); + numBytes -= bytesToWrite; + vp += bytesToWrite; + } + return destination; + } else { + return PTR_TO_VMA(pfSetMemory((void *) (uintptr_t) destination, value, numBytes)); + } +} + +#endif /* PF_DEMAND_PAGING */ diff --git a/csrc/paging/pagedmem.c b/csrc/paging/pagedmem.c new file mode 100644 index 0000000..4a04cf1 --- /dev/null +++ b/csrc/paging/pagedmem.c @@ -0,0 +1,134 @@ +/*************************************************************** +** Demand Paged Memory Simulator +** +** This should be replaced by a hardware specific implementation +** when doing actual demand paging. The real code may, for example, +** access SPI RAM. +** +** Author: Phil Burk +** Copyright 1994 3DO, Phil Burk, Larry Polansky, David Rosenboom +** +** Permission to use, copy, modify, and/or distribute this +** software for any purpose with or without fee is hereby granted. +** +** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +** THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +** CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +** FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +** CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +** +***************************************************************/ + +#include "../pf_all.h" +#include "dmpaging.h" + +#if PF_DEMAND_PAGING + +#ifndef PF_DP_AVAILABLE_SPACE +#define PF_DP_AVAILABLE_SPACE (512*1024) +#endif + +static cell_t sDpNextAvailable = 0; +#define DP_ALIGNMENT_MASK (DP_ALIGNMENT_SIZE - 1) + +/* By munging the virtual address, we can cause memory errors if it is not converted. + * This will help us catch areas where we need to use functions like pfLockMemoryReadWrite(). + * You may need to use a different mask on your system depending on the memory layout. + */ +#define MUNGE_BY_OFFSET 0 +#if (MUNGE_BY_OFFSET) + /* Allocate twice the needed space. Munged addresses point to the high half. */ + static uint8_t sFakeSerialRAM[2 * PF_DP_AVAILABLE_SPACE]; + /* Munge by offsetting the address. This might be helpful if high address bits are ignored, + * causing the XOR bits to be ignored. */ + #define PF_DP_MUNGE(addr) ((vm_address_t)((PTR_TO_VMA(addr)) + PF_DP_AVAILABLE_SPACE)) + #define PF_DP_UNMUNGE(paging_addr) ((uintptr_t)((paging_addr) - PF_DP_AVAILABLE_SPACE)) + #define DEAD_MARKER ((uint8_t)0x00) +#else + static uint8_t sFakeSerialRAM[PF_DP_AVAILABLE_SPACE]; + #if (PF_POINTER_SIZE == 8) + #define PF_DP_MUNGE_KEY (0x005A000000000000) + #elif (PF_POINTER_SIZE == 4) + #define PF_DP_MUNGE_KEY (0x50000000) + #endif + + #define PF_DP_MUNGE(addr) ((vm_address_t)((PTR_TO_VMA(addr)) ^ PF_DP_MUNGE_KEY)) + #define PF_DP_UNMUNGE(paging_addr) ((uintptr_t)((paging_addr) ^ PF_DP_MUNGE_KEY)) +#endif + +void pfResetPagedMemory(void) { + printf("pfResetPagedMemory: cell = %d\n", (int)sizeof(cell_t)); + sDpNextAvailable = 0; + +#if (MUNGE_BY_OFFSET) + /* Fill memory that should not be touched with a marker. */ + memset(&sFakeSerialRAM[PF_DP_AVAILABLE_SPACE], DEAD_MARKER, PF_DP_AVAILABLE_SPACE); +#endif +} + +int pfCheckPagedMemory(void) { + /* Check to see if anything wrote to the high memory. */ +#if (MUNGE_BY_OFFSET) + uint32_t i; + int numErrors = 0; + uint8_t *ram = &sFakeSerialRAM[PF_DP_AVAILABLE_SPACE]; /* point to high unused half */ + for (i = 0; i < PF_DP_AVAILABLE_SPACE; i++) { + uint8_t value = *ram++; + if (value != DEAD_MARKER) { + printf("pfCheckPagedMemory: bad byte 0x%02X at offset %u\n", value, i); + numErrors++; + } + } + printf("pfCheckPagedMemory: found %d errors\n", numErrors); + return numErrors; +#else + return 0; +#endif +} +int pfIsAddressInPagedMemory(vm_address_t p) { + uintptr_t addr = PF_DP_UNMUNGE(p); + cell_t offset = (uint8_t *)addr - sFakeSerialRAM; + return (offset >= 0) && (offset < PF_DP_AVAILABLE_SPACE); +} + +vm_address_t pfAllocatePagedMemory(const ucell_t numBytes) { + cell_t alignedNumBytes = (numBytes + DP_ALIGNMENT_MASK) & (~DP_ALIGNMENT_MASK); + cell_t finalAvailable = sDpNextAvailable + alignedNumBytes; + if (finalAvailable > PF_DP_AVAILABLE_SPACE) { + printf("ERROR - Out of Demand Paged Memory! need %d, have %d\n", + (int)numBytes, (int)(PF_DP_AVAILABLE_SPACE - sDpNextAvailable)); + return 0; + } + vm_address_t virtualAddress = PTR_TO_VMA(&sFakeSerialRAM[sDpNextAvailable]); + sDpNextAvailable = finalAvailable; + return PF_DP_MUNGE(virtualAddress); +} + +void pfFreePagedMemory(vm_address_t p) {} + +size_t pfReadPagedMemory(void *destination, + paging_address_t source, + uint32_t numBytes) { + PF_ASSERT(pfIsAddressInPagedMemory(source)); + if (numBytes == 0) return 0; + PF_ASSERT(pfIsAddressInPagedMemory(source + numBytes - 1)); + void *pAddr = (void *)(uintptr_t) PF_DP_UNMUNGE(source); + pfCopyMemory(destination, pAddr, numBytes); + return numBytes; +} + +size_t pfWritePagedMemory(paging_address_t destination, + const void *source, + uint32_t numBytes) { + PF_ASSERT(pfIsAddressInPagedMemory(destination)); + if (numBytes == 0) return 0; + PF_ASSERT(pfIsAddressInPagedMemory(destination + numBytes - 1)); + void *pAddr = (void *)(uintptr_t) PF_DP_UNMUNGE(destination); + pfCopyMemory(pAddr, source, numBytes); + return numBytes; + } + +#endif /* PF_DEMAND_PAGING */ diff --git a/csrc/paging/pagedmem.h b/csrc/paging/pagedmem.h new file mode 100644 index 0000000..7f33f35 --- /dev/null +++ b/csrc/paging/pagedmem.h @@ -0,0 +1,93 @@ +#ifndef _pf_pagedmem_h +#define _pf_pagedmem_h + +/*************************************************************** +** Include file for PForth Paged Memory +** +** If you want to use demand paging then you will need to replace +** the paged memory simulator in pagedmem.c with your own function. +** +** Author: Phil Burk +** Copyright 1994 3DO, Phil Burk, Larry Polansky, David Rosenboom +** +** Permission to use, copy, modify, and/or distribute this +** software for any purpose with or without fee is hereby granted. +** +** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +** THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +** CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +** FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +** CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +** +***************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Serial Memory Access + * A demand paging simulator is provided for testing the framework on a host. + * The actual demand paging interface must be provided by the user. + */ + +/** Is the given address pointing to paged memory? + * @return 1 if in paged memory, else 0 + */ +int pfIsAddressInPagedMemory(vm_address_t p); + +#if PF_DEMAND_PAGING +#define DP_ALIGNMENT_SIZE (16) + +/** + * Reset the memory allocator. + * This may or may not clear the memory. + */ +void pfResetPagedMemory(void); + +/** + * Check the paged memory for corruption. + * This is only implemented for the memory simulator. + * This can be a NOOP in a real implementation. + * @return zero if memory untouched else number of bad bytes + */ +int pfCheckPagedMemory(void); + +/** Allocate demand paged memory from SPI or other storage. + * Memory blocks will be aligned on DP_ALIGNMENT_SIZE byte boundaries. + */ +paging_address_t pfAllocatePagedMemory(ucell_t numBytes); + +/** Free demand paged memory. + * This may simply be a NOOP. So just allocate + * what you need at the beginning and don't rely on being able to free it. + */ +void pfFreePagedMemory(vm_address_t p); + +/** Read from virtual to physical memory. + * Only one async read or write can be pending at a time. + * @param micros if zero then issue an async transfer, else timeout in micros + * @return number of bytes read or 0 if timed out + */ +size_t pfReadPagedMemory(void *destination, + paging_address_t source, + uint32_t numBytes); + +/** Read from virtual to physical memory. + * Only one async read or write can be pending at a time. + * @param micros if zero then issue an async transfer, else timeout in micros + * @return number of bytes written or 0 if timed out + */ +size_t pfWritePagedMemory(paging_address_t destination, + const void *source, + uint32_t numBytes); + +#endif /* PF_DEMAND_PAGING */ + +#ifdef __cplusplus +} +#endif + +#endif /* _pf_pagedmem_h */ diff --git a/csrc/paging/qadmpage.c b/csrc/paging/qadmpage.c new file mode 100644 index 0000000..25b0e84 --- /dev/null +++ b/csrc/paging/qadmpage.c @@ -0,0 +1,303 @@ + +/*************************************************************** +** Unit Tests file for pForth Demand Paging +** +** Author: Phil Burk +** Copyright 1994 3DO, Phil Burk, Larry Polansky, David Rosenboom +** +** Permission to use, copy, modify, and/or distribute this +** software for any purpose with or without fee is hereby granted. +** +** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +** THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +** CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +** FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +** CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +** +***************************************************************/ + +#include "../pf_all.h" +#include "unittest.h" +#include "qadmpage.h" + +#if PF_DEMAND_PAGING + +PFQA_INSTANTIATE_GLOBALS; + + +static int pfQaTestAllocate(void) { + printf("pfQaDemandPaging : pfQaTestAllocate\n"); + pfResetPagedMemory(); + vm_address_t vm1 = pfAllocatePagedMemory(1); + ASSERT_NE(vm1, 0); + vm_address_t vm2 = pfAllocatePagedMemory(128); + ASSERT_NE(vm2, 0); + /* Check alignment. */ + ASSERT_EQ((vm2 - vm1), DP_ALIGNMENT_SIZE); + + int x = 0; + ASSERT_EQ(pfIsAddressInPagedMemory(PTR_TO_VMA(&x)), 0); + ASSERT_EQ(pfIsAddressInPagedMemory((vm2 + 5)), 1); + return 0; +error: + return 1; +} + +static int pfQaTestFetchStore(void) { + printf("pfQaDemandPaging : pfQaTestFetchStore\n"); + cell_t c1 = 123456; + cell_t c2; + uint16_t w1 = 1234; + uint16_t w2; + uint8_t b1 = 91; + uint8_t b2; + + pfResetPagedMemory(); + vm_address_t vm1 = pfAllocatePagedMemory(1024); + ASSERT_NE(vm1, 0); + + DP_STORE_U8(vm1 + 16, b1); + DP_STORE_U16(vm1 + 32, w1); + DP_STORE_CELL(vm1 + 48, c1); + + b2 = DP_FETCH_U8(vm1 + 16); + ASSERT_EQ(b1, b2); + w2 = DP_FETCH_U16(vm1 + 32); + ASSERT_EQ(w1, w2); + c2 = DP_FETCH_CELL(vm1 + 48); + ASSERT_EQ(c1, c2); + return 0; +error: + return 1; +} + +#ifdef PF_SUPPORT_FP +static int pfQaTestFetchStoreFloat(void) { + printf("pfQaDemandPaging : pfQaTestFetchStoreFloat\n"); + PF_FLOAT f1 = 3.14159; + PF_FLOAT f2; + + pfResetPagedMemory(); + vm_address_t vm1 = pfAllocatePagedMemory(1024); + ASSERT_NE(vm1, 0); + + DP_STORE_FLOAT(vm1 + 64, f1); + f2 = DP_FETCH_FLOAT(vm1 + 64); + ASSERT_EQ(f1, f2); + return 0; +error: + return 1; +} +#endif /* PF_SUPPORT_FP */ + +static int pfQaTestReadWrite(void) { + printf("pfQaDemandPaging : pfQaTestReadWrite\n"); + uint8_t buffer1[73]; + uint8_t buffer2[sizeof(buffer1)]; + const int kBufferSize = sizeof(buffer1); + int i; + for (i = 0; i < kBufferSize; i++) { + buffer1[i] = i; + } + pfResetPagedMemory(); + vm_address_t vm1 = pfAllocatePagedMemory(kBufferSize); + ASSERT_NE(vm1, 0); + cell_t written = pfWritePagedMemory(vm1, buffer1, kBufferSize); + ASSERT_EQ(written, kBufferSize); + cell_t numRead = pfReadPagedMemory(buffer2, vm1, kBufferSize); + ASSERT_EQ(numRead, kBufferSize); + for (i = 0; i < kBufferSize; i++) { + ASSERT_EQ(buffer2[i], buffer1[i]); + } + + return 0; +error: + return 1; +} + +static int pfQaTestRegionLock(void) { + printf("pfQaDemandPaging : pfQaTestRegionLock\n"); + pfResetLockedMemory(); + const int kBufferSize = 123; + int result = 0; + int i; + pfResetPagedMemory(); + vm_address_t vm1 = pfAllocatePagedMemory(kBufferSize); + ASSERT_NE(0, vm1); + + uint8_t *pm1 = pfLockMemoryReadWrite(vm1, kBufferSize); + ASSERT_NE(PTR_TO_VMA(pm1), PF_VM_NULL); + for (i = 0; i < kBufferSize; i++) { + pm1[i] = i; + } + result = pfUnlockMemory(vm1, pm1); + ASSERT_EQ(0, result); + + const uint8_t *pm2 = pfLockMemoryReadOnly(vm1, kBufferSize); + ASSERT_NE(PTR_TO_VMA(pm2), PF_VM_NULL); + for (i = 0; i < kBufferSize; i++) { + ASSERT_EQ(pm2[i], i); + } + result = pfUnlockMemory(vm1 + 8, pm2); /* Pass bad virtual address! */ + ASSERT_GE(0, result); + result = pfUnlockMemory(vm1, pm2 + 8); /* Pass bad physical address! */ + ASSERT_GE(0, result); + result = pfUnlockMemory(vm1, pm2); /* GOOD */ + ASSERT_EQ(0, result); + result = pfUnlockMemory(vm1, pm2); /* Unlock twice! */ + ASSERT_GE(0, result); + + return 0; +error: + return 1; +} + +#define PF_DP_TEST_PATHNAME "/tmp/pf_scratch_file" + +static cell_t pfQaTestCreateFile(size_t numBytes) { + uint8_t buffer[100]; + int i; + FileStream *fid = sdOpenFile(PF_DP_TEST_PATHNAME, "wb"); + if (fid == NULL) { + printf("ERROR: Could not open file %s\n",PF_DP_TEST_PATHNAME); + return -1; + } + for (i = 0; i < sizeof(buffer); i++) { + buffer[i] = i; + } + while (numBytes > 0) { + size_t bytesToWrite = (numBytes < sizeof(buffer)) + ? numBytes : sizeof(buffer); + size_t itemsWritten = sdWriteFile(buffer, 1, bytesToWrite, fid); + if (itemsWritten != bytesToWrite) { + printf("ERROR: writing file failed %d\n", (int)itemsWritten); + return -1; + } + numBytes -= bytesToWrite; + } + sdCloseFile(fid); + return 0; +} + +static int pfQaTestReadFileStandard(size_t numBytes) { + uint8_t buffer[100]; + int i; + printf("pfQaDemandPaging : pfQaCheckReadFile\n"); + FileStream *fid = sdOpenFile(PF_DP_TEST_PATHNAME, "rb"); + ASSERT_NE(PF_VM_NULL, PTR_TO_VMA(fid)); + while (numBytes > 0) { + size_t bytesToRead = (numBytes < sizeof(buffer)) ? numBytes : sizeof(buffer); + size_t result = sdReadFile(buffer, 1, (int32_t) bytesToRead, fid); /* use stdio */ + ASSERT_EQ(bytesToRead, result); + for (i = 0; i < bytesToRead; i++) { + ASSERT_EQ((i % 100), buffer[i]); + } + numBytes -= bytesToRead; + } + sdCloseFile(fid); + return 0; +error: + return 1; +} + +static int pfQaTestReadFilePaging(void) { + int i; + printf("pfQaDemandPaging : pfQaTestReadFile\n"); + pfResetLockedMemory(); + const int kBytesToRead = 1175; + cell_t result = pfQaTestCreateFile(kBytesToRead); + ASSERT_EQ(0, result); + vm_address_t vm1 = pfAllocatePagedMemory(kBytesToRead); + ASSERT_NE(PF_VM_NULL, vm1); + FileStream *fid = sdOpenFile(PF_DP_TEST_PATHNAME, "rb"); + ASSERT_NE(PF_VM_NULL, PTR_TO_VMA(fid)); + result = ffReadFile(vm1, 1, kBytesToRead, fid); /* use demand paging */ + ASSERT_EQ(kBytesToRead, result); + for (i = 0; i < kBytesToRead; i++) { + ASSERT_EQ((i % 100), DP_FETCH_U8(vm1 + i)); + } + sdCloseFile(fid); + return 0; +error: + return 1; +} + +static int pfQaTestWriteFilePaging(void) { + int i; + printf("pfQaDemandPaging : pfQaTestWriteFilePaging\n"); + pfResetLockedMemory(); + const int kBytesToWrite = 1175; + cell_t result = pfQaTestCreateFile(kBytesToWrite); + ASSERT_EQ(0, result); + vm_address_t vm1 = pfAllocatePagedMemory(kBytesToWrite); + ASSERT_NE(PF_VM_NULL, vm1); + for (i = 0; i < kBytesToWrite; i++) { + DP_STORE_U8((vm1 + i), (i % 100)); + } + FileStream *fid = sdOpenFile(PF_DP_TEST_PATHNAME, "wb"); + ASSERT_NE(PF_VM_NULL, PTR_TO_VMA(fid)); + result = ffWriteFile(vm1, 1, kBytesToWrite, fid); /* use demand paging */ + ASSERT_EQ(kBytesToWrite, result); + sdCloseFile(fid); + result = pfQaTestReadFileStandard(kBytesToWrite); + ASSERT_EQ(0, result); + return 0; +error: + return 1; +} + +static int pfQaTestSetVirtualMemory(void) { + int i; + printf("pfQaDemandPaging : pfQaTestSetVirtualMemory\n"); + pfResetLockedMemory(); + vm_address_t dest; + /* Paint a large region then paint a small region in the middle. */ + const int largeSize = 1175; + const int smallSize = 234; + const int smallOffset = 571; + vm_address_t vm1 = pfAllocatePagedMemory(largeSize); + ASSERT_NE(PF_VM_NULL, vm1); + dest = pfSetVirtualMemory(vm1, 0x5A, largeSize); + ASSERT_EQ(dest, vm1); + dest = pfSetVirtualMemory(vm1 + smallOffset, 0x3C, smallSize); + ASSERT_EQ(dest, vm1 + smallOffset); + i = 0; + for (; i < smallOffset; i++) { + ASSERT_EQ(0x5A, DP_FETCH_U8(vm1 + i)); + } + for (; i < smallOffset + smallSize; i++) { + ASSERT_EQ(0x3C, DP_FETCH_U8(vm1 + i)); + } + for (; i < largeSize; i++) { + ASSERT_EQ(0x5A, DP_FETCH_U8(vm1 + i)); + } + return 0; +error: + return 1; +} + +int pfQaDemandPaging(void) { + printf("pfQaDemandPaging called\n"); + ASSERT_EQ(sizeof(vm_address_t), sizeof(cell_t)); + ASSERT_EQ(pfQaTestAllocate(), 0); + ASSERT_EQ(pfQaTestReadWrite(), 0); + ASSERT_EQ(pfQaTestRegionLock(), 0); + ASSERT_EQ(pfQaTestFetchStore(), 0); +#ifdef PF_SUPPORT_FP + ASSERT_EQ(pfQaTestFetchStoreFloat(), 0); +#endif /* PF_SUPPORT_FP */ + ASSERT_EQ(pfQaTestReadFilePaging(), 0); + ASSERT_EQ(pfQaTestWriteFilePaging(), 0); + ASSERT_EQ(pfQaTestSetVirtualMemory(), 0); + printf("pfQaDemandPaging ended\n"); + +error: + pfResetPagedMemory(); + PFQA_PRINT_RESULT; + return PFQA_EXIT_RESULT; +} + +#endif /* PF_DEMAND_PAGING */ diff --git a/csrc/paging/qadmpage.h b/csrc/paging/qadmpage.h new file mode 100644 index 0000000..7c20b1e --- /dev/null +++ b/csrc/paging/qadmpage.h @@ -0,0 +1,37 @@ +#ifndef _pf_qadmpage_h +#define _pf_qadmpage_h + +/*************************************************************** +** Include file for PForth Paged Memory Unit Tests +** +** Author: Phil Burk +** Copyright 1994 3DO, Phil Burk, Larry Polansky, David Rosenboom +** +** Permission to use, copy, modify, and/or distribute this +** software for any purpose with or without fee is hereby granted. +** +** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +** THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +** CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +** FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +** CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +** +***************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +#if PF_DEMAND_PAGING +/* Run C unit tests for demand paging. */ +int pfQaDemandPaging(void); +#endif /* PF_DEMAND_PAGING */ + +#ifdef __cplusplus +} +#endif + +#endif /* _pf_qadmpage_h */ diff --git a/csrc/paging/unittest.h b/csrc/paging/unittest.h new file mode 100644 index 0000000..29c9ab7 --- /dev/null +++ b/csrc/paging/unittest.h @@ -0,0 +1,106 @@ +#ifndef _pf_unittest_h +#define _pf_unittest_h +/*************************************************************** +** Unit Test support for pForth +** +** Author: Phil Burk +** Copyright 1994 3DO, Phil Burk, Larry Polansky, David Rosenboom +** +** Permission to use, copy, modify, and/or distribute this +** software for any purpose with or without fee is hereby granted. +** +** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL +** THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +** CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING +** FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +** CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +** OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +** +***************************************************************/ + +extern int pfQaNumPassed; +extern int pfQaNumFailed; + +/* You must use this macro exactly once in each test program. */ +#define PFQA_INSTANTIATE_GLOBALS\ + int pfQaNumPassed = 0;\ + int pfQaNumFailed = 0 + +#if PF_CELL_SIZE == 4 +#define CELL_FORMAT "%ld" +#elif PF_CELL_SIZE == 8 +#define CELL_FORMAT "%lld" +#endif +/*------------------- Macros ------------------------------*/ +/* Print ERROR if it fails. Tally success or failure. Odd */ +/* do-while wrapper seems to be needed for some compilers. */ +#define CHECK_TRUE(_exp, _on_error) \ + do \ + { \ + if (_exp) {\ + pfQaNumPassed++; \ + } \ + else { \ + printf("ERROR at %s:%d, (%s) not true\n", \ + __FILE__, __LINE__, #_exp ); \ + pfQaNumFailed++; \ + _on_error; \ + } \ + } while(0) + +#define ASSERT_TRUE(_exp) CHECK_TRUE(_exp, goto error) +#define EXPECT_TRUE(_exp) CHECK_TRUE(_exp, (void)0) + +#define CHECK_AB(_a, _b, _op, _opn, _on_error) \ + do \ + { \ + cell_t mA = (cell_t)(_a); \ + cell_t mB = (cell_t)(_b); \ + if (mA _op mB) {\ + pfQaNumPassed++; \ + } \ + else { \ + printf("ERROR at %s:%d, (%s) %s (%s), " CELL_FORMAT " %s " CELL_FORMAT "\n", \ + __FILE__, __LINE__, #_a, #_opn, #_b, mA, #_opn, mB ); \ + pfQaNumFailed++; \ + _on_error; \ + } \ + } while(0) + +#define ASSERT_AB(_a, _b, _op, _opn) CHECK_AB(_a, _b, _op, _opn, goto error) +#define ASSERT_EQ(_a, _b) ASSERT_AB(_a, _b, ==, !=) +#define ASSERT_NE(_a, _b) ASSERT_AB(_a, _b, !=, ==) +#define ASSERT_GT(_a, _b) ASSERT_AB(_a, _b, >, <=) +#define ASSERT_GE(_a, _b) ASSERT_AB(_a, _b, >=, <) +#define ASSERT_LT(_a, _b) ASSERT_AB(_a, _b, <, >=) +#define ASSERT_LE(_a, _b) ASSERT_AB(_a, _b, <=, >) + +#define EXPECT_AB(_a, _b, _op, _opn) CHECK_AB(_a, _b, _op, _opn, (void)0) +#define EXPECT_EQ(_a, _b) EXPECT_AB(_a, _b, ==, !=) +#define EXPECT_NE(_a, _b) EXPECT_AB(_a, _b, !=, ==) +#define EXPECT_GT(_a, _b) EXPECT_AB(_a, _b, >, <=) +#define EXPECT_GE(_a, _b) EXPECT_AB(_a, _b, >=, <) +#define EXPECT_LT(_a, _b) EXPECT_AB(_a, _b, <, >=) +#define EXPECT_LE(_a, _b) EXPECT_AB(_a, _b, <=, >) + +#define HOPEFOR(_exp) \ + do \ + { \ + if ((_exp)) {\ + pfQaNumPassed++; \ + } \ + else { \ + printf("\nERROR - 0x%x - %s for %s\n", result, Pa_GetErrorText(result), #_exp ); \ + pfQaNumFailed++; \ + } \ + } while(0) + +#define PFQA_PRINT_RESULT \ + printf("QA Report: %d passed, %d failed.\n", pfQaNumPassed, pfQaNumFailed ) + +#define PFQA_EXIT_RESULT \ + (((pfQaNumFailed > 0) || (pfQaNumPassed == 0)) ? EXIT_FAILURE : EXIT_SUCCESS) + +#endif /* _pf_unittest_h */ diff --git a/csrc/pf_all.h b/csrc/pf_all.h index cfa3bca..8dae68b 100644 --- a/csrc/pf_all.h +++ b/csrc/pf_all.h @@ -50,6 +50,8 @@ #include "pforth.h" #include "pf_types.h" +#include "paging/dmpaging.h" +#include "paging/pagedmem.h" #include "pf_io.h" #include "pf_guts.h" #include "pf_text.h" diff --git a/csrc/pf_cglue.c b/csrc/pf_cglue.c index c2b65e5..2106190 100644 --- a/csrc/pf_cglue.c +++ b/csrc/pf_cglue.c @@ -86,7 +86,7 @@ DBUG(("CallUserFunction: Index = %d, ReturnMode = %d, NumParams = %d\n", Err CreateGlueToC( const char *CName, ucell_t Index, cell_t ReturnMode, int32_t NumParams ) { ucell_t Packed; - char FName[LONGEST_WORD_NAME+9]; /* +1 for length, up to +9 should not be used, but is here for safety */ + char FName[PF_NAME_SIZE_SAFE]; CStringToForth( FName, CName, sizeof(FName) ); Packed = (Index & 0xFFFF) | 0 | (NumParams << 24) | @@ -94,7 +94,7 @@ Err CreateGlueToC( const char *CName, ucell_t Index, cell_t ReturnMode, int32_t DBUG(("Packed = 0x%8x\n", Packed)); ffCreateSecondaryHeader( FName ); - CODE_COMMA( ID_CALL_C ); + CODE_COMMA(ID_CALL_C); CODE_COMMA(Packed); ffFinishSecondary(); diff --git a/csrc/pf_core.c b/csrc/pf_core.c index c0710fc..fc76622 100644 --- a/csrc/pf_core.c +++ b/csrc/pf_core.c @@ -37,6 +37,7 @@ ***************************************************************/ #include "pf_all.h" +#include "paging/qadmpage.h" /*************************************************************** ** Global Data @@ -51,6 +52,7 @@ ExecToken gLocalCompiler_XT; /* custom compiler for local variables */ ExecToken gNumberQ_XT; /* XT of NUMBER? */ ExecToken gQuitP_XT; /* XT of (QUIT) */ ExecToken gAcceptP_XT; /* XT of ACCEPT */ +cell_t gPfAssertEnabled = PF_ASSERT_ENABLED; /* Depth of data stack when colon called. */ cell_t gDepthAtColon; @@ -98,7 +100,7 @@ static void pfInit( void ) gCurrentDictionary = NULL; gNumPrimitives = 0; gLocalCompiler_XT = 0; - gVarContext = (cell_t)NULL; /* Points to last name field. */ + gVarContext = PF_VM_NULL; /* Points to last name field. */ gVarState = 0; /* 1 if compiling. */ gVarByeCode = 0; /* BYE-CODE */ gVarEcho = 0; /* Echo input. */ @@ -113,7 +115,9 @@ static void pfInit( void ) gVarTraceStack = 1; pfInitMemoryAllocator(); + pfResetLockedMemory(); ioInit(); + } static void pfTerm( void ) { @@ -127,6 +131,7 @@ static void pfTerm( void ) void pfDeleteTask( PForthTask task ) { pfTaskData_t *cftd = (pfTaskData_t *)task; + if (cftd == NULL) return; #ifdef PF_SUPPORT_FP FREE_VAR( cftd->td_FloatStackLimit ); #endif @@ -170,7 +175,7 @@ PForthTask pfCreateTask( cell_t UserStackDepth, cell_t ReturnStackDepth ) cftd->td_InputStream = PF_STDIN; - cftd->td_SourcePtr = &cftd->td_TIB[0]; + cftd->td_SourcePtr = PTR_TO_VMA(&cftd->td_TIB[0]); cftd->td_SourceNum = 0; return (PForthTask) cftd; @@ -188,7 +193,7 @@ PForthTask pfCreateTask( cell_t UserStackDepth, cell_t ReturnStackDepth ) ThrowCode pfExecIfDefined( const char *CString ) { ThrowCode result = 0; - if( NAME_BASE != (cell_t)NULL) + if( NAME_BASE != PF_VM_NULL) { ExecToken XT; if( ffFindC( CString, &XT ) ) @@ -209,8 +214,8 @@ void pfDeleteDictionary( PForthDictionary dictionary ) if( dic->dic_Flags & PF_DICF_ALLOCATED_SEGMENTS ) { - FREE_VAR( dic->dic_HeaderBaseUnaligned ); - FREE_VAR( dic->dic_CodeBaseUnaligned ); + FREE_VM_VAR( dic->dic_HeaderBaseUnaligned ); + FREE_VM_VAR( dic->dic_CodeBaseUnaligned ); } pfFreeMem( dic ); } @@ -243,11 +248,17 @@ PForthDictionary pfCreateDictionary( cell_t HeaderSize, cell_t CodeSize ) /* Allocate memory for header. */ if( HeaderSize > 0 ) { - dic->dic_HeaderBaseUnaligned = (ucell_t) pfAllocMem( (ucell_t) HeaderSize + DIC_ALIGNMENT_SIZE ); + +#if PF_DEMAND_PAGING + dic->dic_HeaderBaseUnaligned = pfAllocatePagedMemory( (ucell_t) HeaderSize + DIC_ALIGNMENT_SIZE); +#else + + dic->dic_HeaderBaseUnaligned = PTR_TO_VMA(pfAllocMem((ucell_t) HeaderSize + DIC_ALIGNMENT_SIZE )); +#endif if( !dic->dic_HeaderBaseUnaligned ) goto nomem; /* Align header base. */ dic->dic_HeaderBase = DIC_ALIGN(dic->dic_HeaderBaseUnaligned); - pfSetMemory( (char *) dic->dic_HeaderBase, 0xA5, (ucell_t) HeaderSize); + pfSetVirtualMemory((vm_address_t) dic->dic_HeaderBase, 0xA5, (uint32_t) HeaderSize); dic->dic_HeaderLimit = dic->dic_HeaderBase + HeaderSize; dic->dic_HeaderPtr = dic->dic_HeaderBase; } @@ -257,13 +268,17 @@ PForthDictionary pfCreateDictionary( cell_t HeaderSize, cell_t CodeSize ) } /* Allocate memory for code. */ - dic->dic_CodeBaseUnaligned = (ucell_t) pfAllocMem( (ucell_t) CodeSize + DIC_ALIGNMENT_SIZE ); +#if PF_DEMAND_PAGING + dic->dic_CodeBaseUnaligned = pfAllocatePagedMemory( (ucell_t) CodeSize + DIC_ALIGNMENT_SIZE ); +#else + dic->dic_CodeBaseUnaligned = PTR_TO_VMA(pfAllocMem( (ucell_t) CodeSize + DIC_ALIGNMENT_SIZE )); +#endif if( !dic->dic_CodeBaseUnaligned ) goto nomem; dic->dic_CodeBase = DIC_ALIGN(dic->dic_CodeBaseUnaligned); - pfSetMemory( (char *) dic->dic_CodeBase, 0x5A, (ucell_t) CodeSize); + pfSetVirtualMemory((vm_address_t) dic->dic_CodeBase, 0x5A, (uint32_t) CodeSize); dic->dic_CodeLimit = dic->dic_CodeBase + CodeSize; - dic->dic_CodePtr.Byte = ((uint8_t *) (dic->dic_CodeBase + QUADUP(NUM_PRIMITIVES))); + dic->dic_CodePtr = (vm_address_t) (dic->dic_CodeBase + (QUADUP(NUM_PRIMITIVES) * PF_CELL_SIZE)); return (PForthDictionary) dic; nomem: @@ -422,12 +437,11 @@ void pfDebugMessage( const char *CString ) /*************************************************************** ** Print a decimal number to debug output. */ -void pfDebugPrintDecimalNumber( int n ) +void pfDebugPrintDecimalNumber( cell_t n ) { pfDebugMessage( ConvertNumberToText( n, 10, TRUE, 1 ) ); } - /*************************************************************** ** Output 'C' string message. ** This is provided to help avoid the use of printf() and other I/O @@ -444,7 +458,7 @@ void pfMessage( const char *CString ) */ ThrowCode pfDoForth( const char *DicFileName, const char *SourceName, cell_t IfInit ) { - pfTaskData_t *cftd; + pfTaskData_t *cftd = NULL; pfDictionary_t *dic = NULL; ThrowCode Result = 0; ExecToken EntryPoint = 0; @@ -456,6 +470,11 @@ ThrowCode pfDoForth( const char *DicFileName, const char *SourceName, cell_t IfI pfInit(); +#if PF_DEMAND_PAGING + Result = pfQaDemandPaging(); /* TODO move to standalone qa test */ + if (Result != 0) goto error2; +#endif + /* Allocate Task structure. */ pfDebugMessage("pfDoForth: call pfCreateTask()\n"); cftd = pfCreateTask( DEFAULT_USER_DEPTH, DEFAULT_RETURN_DEPTH ); @@ -573,6 +592,10 @@ ThrowCode pfDoForth( const char *DicFileName, const char *SourceName, cell_t IfI pfTerm(); +#if PF_DEMAND_PAGING + pfCheckPagedMemory(); +#endif + #ifdef PF_USER_TERM PF_USER_TERM; #endif @@ -592,11 +615,11 @@ ThrowCode pfDoForth( const char *DicFileName, const char *SourceName, cell_t IfI return -1; } - #ifdef PF_UNIT_TEST cell_t pfUnitTest( void ) { cell_t numErrors = 0; + MSG("pfUnitTest() called\n"); numErrors += pfUnitTestText(); return numErrors; } diff --git a/csrc/pf_core.h b/csrc/pf_core.h index 46b5753..3c8cdfa 100644 --- a/csrc/pf_core.h +++ b/csrc/pf_core.h @@ -26,16 +26,15 @@ extern "C" { #endif -void pfInitGlobals( void ); +void pfInitGlobals(void); -void pfDebugMessage( const char *CString ); -void pfDebugPrintDecimalNumber( int n ); +void pfDebugMessage(const char *CString); +void pfDebugPrintDecimalNumber(cell_t n); -cell_t pfUnitTestText( void ); +cell_t pfUnitTestText(void); #ifdef __cplusplus } #endif - #endif /* _pf_core_h */ diff --git a/csrc/pf_guts.h b/csrc/pf_guts.h index 1a49647..62f1ee2 100644 --- a/csrc/pf_guts.h +++ b/csrc/pf_guts.h @@ -26,8 +26,8 @@ ** PFORTH_VERSION changes when PForth is modified. ** See README file for version info. */ -#define PFORTH_VERSION_CODE 33 -#define PFORTH_VERSION_NAME "2.1.1" +#define PFORTH_VERSION_CODE 34 +#define PFORTH_VERSION_NAME "2.2.0" /* * NOTES about PF_SUPPORT_LONG_NAMES @@ -88,14 +88,15 @@ #ifdef PF_SUPPORT_LONG_NAMES #define FLAG_SMUDGE (0x80) -#define MASK_NAME_SIZE (0x3F) +#define PF_NAME_SIZE (0x40) #else #define FLAG_SMUDGE (0x20) -#define MASK_NAME_SIZE (0x1F) +#define PF_NAME_SIZE (0x20) #endif -/* these the same, but have different names for clarity */ -#define LONGEST_WORD_NAME MASK_NAME_SIZE +/* These are the same, but have different names for clarity. */ +#define MASK_NAME_SIZE (PF_NAME_SIZE - 1) +#define PF_NAME_SIZE_SAFE (PF_NAME_SIZE + 8) /* Debug TRACE flags */ #define TRACE_INNER (0x0002) @@ -431,15 +432,15 @@ typedef struct pfTaskData_s PF_FLOAT *td_FloatStackBase; PF_FLOAT *td_FloatStackLimit; #endif - cell_t *td_InsPtr; /* Instruction pointer, "PC" */ + cell_t *td_InsPtr; /* Instruction pointer, "PC" */ FileStream *td_InputStream; /* Terminal. */ - char td_TIB[TIB_SIZE]; /* Buffer for terminal input. */ - cell_t td_IN; /* Index into Source */ - cell_t td_SourceNum; /* #TIB after REFILL */ - char *td_SourcePtr; /* Pointer to TIB or other source. */ - cell_t td_LineNumber; /* Incremented on every refill. */ - cell_t td_OUT; /* Current output column. */ + char td_TIB[TIB_SIZE]; /* Buffer for terminal input. */ + cell_t td_IN; /* Index into Source */ + cell_t td_SourceNum; /* #TIB after REFILL */ + vm_address_t td_SourcePtr; /* Pointer to TIB or other source. */ + cell_t td_LineNumber; /* Incremented on every refill. */ + cell_t td_OUT; /* Current output column. */ } pfTaskData_t; typedef struct pfNode @@ -448,44 +449,46 @@ typedef struct pfNode struct pfNode *n_Prev; } pfNode; +/* Define offsets to fields in an header entry. */ +#define PF_HEADER_OFFSET_PREVIOUS_NAME (0) +#define PF_HEADER_OFFSET_EXEC_TOKEN (PF_CELL_SIZE) +#define PF_HEADER_OFFSET_NFA (PF_HEADER_OFFSET_EXEC_TOKEN + PF_CELL_SIZE) + +#if 0 /* Structure of header entry in dictionary. These will be stored in dictionary specific endian format*/ typedef struct cfNameLinks { - cell_t cfnl_PreviousName; /* name relative address of previous */ + cell_t cfnl_PreviousName; /* name relative address of previous */ ExecToken cfnl_ExecToken; /* Execution token for word. */ /* Followed by variable length name field. */ } cfNameLinks; +#endif #define PF_DICF_ALLOCATED_SEGMENTS ( 0x0001) typedef struct pfDictionary_s { pfNode dic_Node; - ucell_t dic_Flags; + ucell_t dic_Flags; /* Headers contain pointers to names and dictionary. */ + vm_address_t dic_HeaderBaseUnaligned; - ucell_t dic_HeaderBaseUnaligned; - - ucell_t dic_HeaderBase; - ucell_t dic_HeaderPtr; - ucell_t dic_HeaderLimit; + vm_address_t dic_HeaderBase; + vm_address_t dic_HeaderPtr; + vm_address_t dic_HeaderLimit; /* Code segment contains tokenized code and data. */ - ucell_t dic_CodeBaseUnaligned; - ucell_t dic_CodeBase; - union - { - cell_t *Cell; - uint8_t *Byte; - } dic_CodePtr; - ucell_t dic_CodeLimit; + vm_address_t dic_CodeBaseUnaligned; + vm_address_t dic_CodeBase; + vm_address_t dic_CodePtr; + vm_address_t dic_CodeLimit; } pfDictionary_t; /* Save state of include when nesting files. */ typedef struct IncludeFrame { FileStream *inf_FileID; - cell_t inf_LineNumber; - cell_t inf_SourceNum; - cell_t inf_IN; + cell_t inf_LineNumber; + cell_t inf_SourceNum; + cell_t inf_IN; char inf_SaveTIB[TIB_SIZE]; } IncludeFrame; @@ -567,19 +570,16 @@ extern cell_t gIncludeIndex; #else -#define WRITE_FLOAT_DIC(addr,data) { *((PF_FLOAT *)(addr)) = (PF_FLOAT)(data); } -#define WRITE_CELL_DIC(addr,data) { *((cell_t *)(addr)) = (cell_t)(data); } -#define WRITE_SHORT_DIC(addr,data) { *((int16_t *)(addr)) = (int16_t)(data); } -#define READ_FLOAT_DIC(addr) ( *((PF_FLOAT *)(addr)) ) -#define READ_CELL_DIC(addr) ( *((const ucell_t *)(addr)) ) -#define READ_SHORT_DIC(addr) ( *((const uint16_t *)(addr)) ) - +#define WRITE_FLOAT_DIC(addr,data) DP_STORE_FLOAT(addr,data) +#define WRITE_CELL_DIC(addr,data) DP_STORE_CELL(addr,data) +#define WRITE_SHORT_DIC(addr,data) DP_STORE_U16(addr,data) +#define READ_FLOAT_DIC(addr) DP_FETCH_FLOAT(addr) +#define READ_CELL_DIC(addr) DP_FETCH_CELL(addr) +#define READ_SHORT_DIC(addr) DP_FETCH_U16(addr) #endif - -#define HEADER_HERE (gCurrentDictionary->dic_HeaderPtr.Cell) -#define CODE_HERE (gCurrentDictionary->dic_CodePtr.Cell) -#define CODE_COMMA( N ) WRITE_CELL_DIC(CODE_HERE++,(N)) +#define CODE_HERE (gCurrentDictionary->dic_CodePtr) +#define CODE_COMMA(N) { WRITE_CELL_DIC(CODE_HERE,(N)); CODE_HERE += PF_CELL_SIZE; } #define NAME_BASE (gCurrentDictionary->dic_HeaderBase) #define CODE_BASE (gCurrentDictionary->dic_CodeBase) #define NAME_SIZE (gCurrentDictionary->dic_HeaderLimit - gCurrentDictionary->dic_HeaderBase) @@ -599,12 +599,15 @@ extern cell_t gIncludeIndex; /* The check for >0 is only needed for CLONE testing. !!! */ #define IsTokenPrimitive(xt) ((xt=0)) -#define FREE_VAR(v) { if (v) { pfFreeMem((void *)(v)); v = 0; } } +#define FREE_VAR(pm_var) { pfFreeMem(pm_var); pm_var = 0; } +/* For virtual memory addresses we have to avoid narrowing of the address. */ +#define FREE_VM_VAR(vm_var) { pfFreeVirtualMemory(vm_var); vm_var = 0; } #define DATA_STACK_DEPTH (gCurrentTask->td_StackBase - gCurrentTask->td_StackPtr) #define DROP_DATA_STACK (gCurrentTask->td_StackPtr++) #define POP_DATA_STACK (*gCurrentTask->td_StackPtr++) #define PUSH_DATA_STACK(x) {*(--(gCurrentTask->td_StackPtr)) = (cell_t) x; } +#define PUSH_PTR_DATA_STACK(x) {*(--(gCurrentTask->td_StackPtr)) = PTR_TO_VMA(x); } /* Force Quad alignment. */ #define QUADUP(x) (((x)+3)&~3) diff --git a/csrc/pf_inner.c b/csrc/pf_inner.c index 7ade2d2..e9ba6ca 100644 --- a/csrc/pf_inner.c +++ b/csrc/pf_inner.c @@ -84,7 +84,7 @@ ** Misc Forth macros ***************************************************************/ -#define M_BRANCH { InsPtr = (cell_t *) (((uint8_t *) InsPtr) + READ_CELL_DIC(InsPtr)); } +#define M_BRANCH { InsPtr = InsPtr + READ_CELL_DIC(InsPtr); } /* Cache top of data stack like in JForth. */ #ifdef PF_SUPPORT_FP @@ -128,7 +128,7 @@ ffDotS( ); \ LOAD_REGISTERS; -#define DO_VAR(varname) { PUSH_TOS; TOS = (cell_t) &varname; } +#define DO_VAR(varname) { PUSH_TOS; TOS = PTR_TO_VMA(&varname); } #ifdef PF_SUPPORT_FP #define M_THROW(err) \ @@ -195,7 +195,7 @@ static void TraceNames( ExecToken Token, cell_t Level ) #endif /* PF_NO_SHELL */ /* Use local copy of CODE_BASE for speed. */ -#define LOCAL_CODEREL_TO_ABS( a ) ((cell_t *) (((cell_t) a) + CodeBase)) +#define LOCAL_CODEREL_TO_ABS( a ) (PTR_TO_VMA(a) + CodeBase) /* Truncate the unsigned double cell integer LO/HI to an uint64_t. */ static uint64_t UdToUint64( ucell_t Lo, ucell_t Hi ) @@ -281,7 +281,7 @@ ThrowCode pfCatch( ExecToken XT ) register cell_t TopOfStack; /* Cache for faster execution. */ register cell_t *DataStackPtr; register cell_t *ReturnStackPtr; - register cell_t *InsPtr = NULL; + register vm_address_t InsPtr = PF_VM_NULL; register cell_t Token; cell_t Scratch; @@ -303,14 +303,9 @@ ThrowCode pfCatch( ExecToken XT ) char *CharPtr; cell_t *CellPtr; FileStream *FileID; - uint8_t *CodeBase = (uint8_t *) CODE_BASE; + vm_address_t CodeBase = CODE_BASE; ThrowCode ExceptionReturnCode = 0; -/* FIXME - gExecutionDepth += 1; - PRT(("pfCatch( 0x%x ), depth = %d\n", XT, gExecutionDepth )); -*/ - /* ** Initialize FakeSecondary this way to avoid having stuff in the data section, ** which is not supported for some embedded system loaders. @@ -352,10 +347,11 @@ DBUG(("pfCatch: Token = 0x%x\n", Token )); M_R_PUSH( InsPtr ); /* Convert execution token to absolute address. */ - InsPtr = (cell_t *) ( LOCAL_CODEREL_TO_ABS(Token) ); + InsPtr = LOCAL_CODEREL_TO_ABS(Token); /* Fetch token at IP. */ - Token = READ_CELL_DIC(InsPtr++); + Token = READ_CELL_DIC(InsPtr); + InsPtr += PF_CELL_SIZE; #ifdef PF_SUPPORT_TRACE /* Bump level for trace display */ @@ -376,7 +372,7 @@ DBUG(("pfCatch: Token = 0x%x\n", Token )); ** Used to implement semicolon. ** Put first in switch because ID_EXIT==0 */ case ID_EXIT: - InsPtr = ( cell_t *) M_R_POP; + InsPtr = M_R_POP; #ifdef PF_SUPPORT_TRACE Level--; #endif @@ -396,8 +392,10 @@ DBUG(("pfCatch: Token = 0x%x\n", Token )); case ID_2LITERAL_P: /* hi part stored first, put on top of stack */ PUSH_TOS; - TOS = READ_CELL_DIC(InsPtr++); - M_PUSH(READ_CELL_DIC(InsPtr++)); + TOS = READ_CELL_DIC(InsPtr); + InsPtr += PF_CELL_SIZE; + M_PUSH(READ_CELL_DIC(InsPtr)); + InsPtr += PF_CELL_SIZE; endcase; case ID_2MINUS: TOS -= 2; endcase; @@ -446,8 +444,10 @@ DBUG(("pfCatch: Token = 0x%x\n", Token )); endcase; case ID_ACCEPT_P: /* ( c-addr +n1 -- +n2 ) */ - CharPtr = (char *) M_POP; + Temp = M_POP; + CharPtr = (char *) pfLockMemoryReadWrite((vm_address_t) Temp, TOS); TOS = ioAccept( CharPtr, TOS ); + pfUnlockMemory((vm_address_t) Temp, (const uint8_t *)CharPtr); endcase; #ifndef PF_NO_SHELL @@ -459,7 +459,8 @@ DBUG(("pfCatch: Token = 0x%x\n", Token )); case ID_ALITERAL_P: PUSH_TOS; - TOS = (cell_t) LOCAL_CODEREL_TO_ABS( READ_CELL_DIC(InsPtr++) ); + TOS = (cell_t) LOCAL_CODEREL_TO_ABS( READ_CELL_DIC(InsPtr) ); + InsPtr += PF_CELL_SIZE; endcase; /* Allocate some extra and put validation identifier at base */ @@ -480,14 +481,14 @@ DBUG(("pfCatch: Token = 0x%x\n", Token )); { /* This was broken into two steps because different compilers incremented ** CellPtr before or after the XOR step. */ - Temp = (cell_t)CellPtr ^ PF_MEMORY_VALIDATOR; + Temp = (cell_t) (uintptr_t) CellPtr ^ PF_MEMORY_VALIDATOR; *CellPtr++ = Temp; - M_PUSH( (cell_t) CellPtr ); + M_PUSH(PTR_TO_VMA(CellPtr)); TOS = 0; } else { - M_PUSH( 0 ); + M_PUSH(0); TOS = -1; /* FIXME Fix error code. */ } endcase; @@ -530,7 +531,8 @@ DBUGX(("After Branch: IP = 0x%x\n", InsPtr )); case ID_CALL_C: SAVE_REGISTERS; - Scratch = READ_CELL_DIC(InsPtr++); + Scratch = READ_CELL_DIC(InsPtr); + InsPtr += PF_CELL_SIZE; CallUserFunction( Scratch & 0xFFFF, (Scratch >> 31) & 1, (Scratch >> 24) & 0x7F ); @@ -547,15 +549,16 @@ DBUGX(("After Branch: IP = 0x%x\n", InsPtr )); TOS = TOS * sizeof(cell_t); endcase; - case ID_CFETCH: TOS = *((uint8_t *) TOS); endcase; + case ID_CFETCH: TOS = DP_FETCH_U8(TOS); endcase; case ID_CMOVE: /* ( src dst n -- ) */ { - register char *DstPtr = (char *) M_POP; /* dst */ - CharPtr = (char *) M_POP; /* src */ + vm_address_t dstVAddr = (vm_address_t) M_POP; /* dst */ + vm_address_t srcVAddr = (vm_address_t) M_POP; /* src */ for( Scratch=0; (ucell_t) Scratch < (ucell_t) TOS ; Scratch++ ) { - *DstPtr++ = *CharPtr++; + uint8_t value = DP_FETCH_U8(srcVAddr++); + DP_STORE_U8(dstVAddr++, value); } M_DROP; } @@ -563,11 +566,12 @@ DBUGX(("After Branch: IP = 0x%x\n", InsPtr )); case ID_CMOVE_UP: /* ( src dst n -- ) */ { - register char *DstPtr = ((char *) M_POP) + TOS; /* dst */ - CharPtr = ((char *) M_POP) + TOS;; /* src */ + vm_address_t dstVAddr = ((vm_address_t) M_POP) + TOS; /* dst */ + vm_address_t srcVAddr = ((vm_address_t) M_POP) + TOS; /* src */ for( Scratch=0; (ucell_t) Scratch < (ucell_t) TOS ; Scratch++ ) { - *(--DstPtr) = *(--CharPtr); + uint8_t value = DP_FETCH_U8(--srcVAddr); + DP_STORE_U8(--dstVAddr, value); } M_DROP; } @@ -580,19 +584,31 @@ DBUGX(("After Branch: IP = 0x%x\n", InsPtr )); LOAD_REGISTERS; endcase; case ID_COLON_P: /* ( $name xt -- ) */ - CreateDicEntry( TOS, (char *) M_POP, 0 ); - M_DROP; + { + vm_address_t vp = (vm_address_t) M_POP; + cell_t length = DP_FETCH_U8(vp); + const char *pName = (const char *) pfLockMemoryReadOnly(vp, length + 1); + CreateDicEntry( TOS, pName, 0 ); + pfUnlockMemory(vp, (const uint8_t *) pName); + M_DROP; + } endcase; #endif /* !PF_NO_SHELL */ case ID_COMPARE: { const char *s1, *s2; + vm_address_t v1, v2; cell_t len1; - s2 = (const char *) M_POP; + cell_t len2 = TOS; + v2 = (vm_address_t) M_POP; len1 = M_POP; - s1 = (const char *) M_POP; - TOS = ffCompare( s1, len1, s2, TOS ); + v1 = (vm_address_t) M_POP; + s2 = (const char *) pfLockMemoryReadOnly(v2, len2); + s1 = (const char *) pfLockMemoryReadOnly(v1, len1); + TOS = ffCompare( s1, len1, s2, len2 ); + pfUnlockMemory(v1, (const uint8_t *) s1); + pfUnlockMemory(v2, (const uint8_t *) s2); } endcase; @@ -643,11 +659,12 @@ DBUGX(("After Branch: IP = 0x%x\n", InsPtr )); case ID_CREATE_P: PUSH_TOS; /* Put address of body on stack. Insptr points after code start. */ - TOS = (cell_t) ((char *)InsPtr - sizeof(cell_t) + CREATE_BODY_OFFSET ); + TOS = (cell_t) (InsPtr - sizeof(cell_t) + CREATE_BODY_OFFSET); endcase; case ID_CSTORE: /* ( c caddr -- ) */ - *((uint8_t *) TOS) = (uint8_t) M_POP; + /* *((uint8_t *) TOS) = (uint8_t) M_POP; */ + DP_STORE_U8(TOS, M_POP); M_DROP; endcase; @@ -895,11 +912,13 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); case ID_DROP: M_DROP; endcase; - case ID_DUMP: - Scratch = M_POP; - DumpMemory( (char *) Scratch, TOS ); + case ID_DUMP: /* ( addr cnt -- ) */ { + cell_t cnt = TOS; + vm_address_t vAddr = (vm_address_t) M_POP; + DumpMemory(vAddr, cnt); M_DROP; - endcase; + } + endcase; case ID_DUP: M_DUP; endcase; @@ -934,19 +953,19 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); case ID_EXECUTE: /* Save IP on return stack like a JSR. */ - M_R_PUSH( InsPtr ); + M_R_PUSH(InsPtr); #ifdef PF_SUPPORT_TRACE /* Bump level for trace. */ Level++; #endif if( IsTokenPrimitive( TOS ) ) { - WRITE_CELL_DIC( (cell_t *) &FakeSecondary[0], TOS); /* Build a fake secondary and execute it. */ - InsPtr = &FakeSecondary[0]; + InsPtr = PTR_TO_VMA(&FakeSecondary[0]); + WRITE_CELL_DIC(InsPtr, TOS); /* Build a fake secondary and execute it. */ } else { - InsPtr = (cell_t *) LOCAL_CODEREL_TO_ABS(TOS); + InsPtr = LOCAL_CODEREL_TO_ABS(TOS); } M_DROP; endcase; @@ -959,41 +978,42 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); } else { - TOS = *((cell_t *)TOS); + TOS = DP_FETCH_CELL(TOS); } #else - TOS = *((cell_t *)TOS); + TOS = DP_FETCH_CELL(TOS); #endif endcase; case ID_FILE_CREATE: /* ( c-addr u fam -- fid ior ) */ -/* Build NUL terminated name string. */ Scratch = M_POP; /* u */ - Temp = M_POP; /* caddr */ if( Scratch < TIB_SIZE-2 ) { + vm_address_t vName = (vm_address_t) M_POP; /* caddr */ const char *famText = pfSelectFileModeCreate( TOS ); - pfCopyMemory( gScratch, (char *) Temp, (ucell_t) Scratch ); + /* Build NUL terminated name string. */ + pfCopyFromVirtualMemory(gScratch, vName, Scratch); gScratch[Scratch] = '\0'; DBUG(("Create file = %s with famTxt %s\n", gScratch, famText )); FileID = sdOpenFile( gScratch, famText ); TOS = ( FileID == NULL ) ? -1 : 0 ; - M_PUSH( (cell_t) FileID ); + M_PUSH(PTR_TO_VMA(FileID)); } else { ERR("Filename too large for name buffer.\n"); + M_DROP; M_PUSH( 0 ); TOS = -2; } endcase; case ID_FILE_DELETE: /* ( c-addr u -- ior ) */ -/* Build NUL terminated name string. */ - Temp = M_POP; /* caddr */ if( TOS < TIB_SIZE-2 ) { - pfCopyMemory( gScratch, (char *) Temp, (ucell_t) TOS ); + vm_address_t vName = (vm_address_t) M_POP; /* caddr */ + /* Build NUL terminated name string. */ + pfCopyFromVirtualMemory(gScratch, vName, TOS); gScratch[TOS] = '\0'; DBUG(("Delete file = %s\n", gScratch )); TOS = sdDeleteFile( gScratch ); @@ -1001,42 +1021,47 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); else { ERR("Filename too large for name buffer.\n"); + M_DROP; TOS = -2; } endcase; case ID_FILE_OPEN: /* ( c-addr u fam -- fid ior ) */ -/* Build NUL terminated name string. */ Scratch = M_POP; /* u */ - Temp = M_POP; /* caddr */ if( Scratch < TIB_SIZE-2 ) { const char *famText = pfSelectFileModeOpen( TOS ); - pfCopyMemory( gScratch, (char *) Temp, (ucell_t) Scratch ); + vm_address_t vName = (vm_address_t) M_POP; /* caddr */ + /* Build NUL terminated name string. */ + pfCopyFromVirtualMemory(gScratch, vName, Scratch); gScratch[Scratch] = '\0'; DBUG(("Open file = %s\n", gScratch )); FileID = sdOpenFile( gScratch, famText ); TOS = ( FileID == NULL ) ? -1 : 0 ; - M_PUSH( (cell_t) FileID ); + M_PUSH(PTR_TO_VMA(FileID)); } else { ERR("Filename too large for name buffer.\n"); + M_DROP; M_PUSH( 0 ); TOS = -2; } endcase; case ID_FILE_CLOSE: /* ( fid -- ior ) */ - TOS = sdCloseFile( (FileStream *) TOS ); + TOS = sdCloseFile( (FileStream *) (uintptr_t) TOS ); endcase; case ID_FILE_READ: /* ( addr len fid -- u2 ior ) */ - FileID = (FileStream *) TOS; + FileID = (FileStream *) (uintptr_t) TOS; Scratch = M_POP; - CharPtr = (char *) M_POP; - Temp = sdReadFile( CharPtr, 1, Scratch, FileID ); + { + vm_address_t vAddr = (vm_address_t) M_POP; + /* warning, only 32-bit nItems */ + Temp = ffReadFile( vAddr, 1, (int32_t) Scratch, FileID ); + } /* TODO check feof() or ferror() */ M_PUSH(Temp); TOS = 0; @@ -1044,8 +1069,8 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); /* TODO Why does this crash when passed an illegal FID? */ case ID_FILE_SIZE: /* ( fid -- ud ior ) */ -/* Determine file size by seeking to end and returning position. */ - FileID = (FileStream *) TOS; + /* Determine file size by seeking to the end and returning position. */ + FileID = (FileStream *) (uintptr_t) TOS; { file_offset_t endposition = -1; file_offset_t original = sdTellFile( FileID ); @@ -1073,11 +1098,13 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); endcase; case ID_FILE_WRITE: /* ( addr len fid -- ior ) */ - FileID = (FileStream *) TOS; + FileID = (FileStream *) (uintptr_t) TOS; Scratch = M_POP; - CharPtr = (char *) M_POP; - Temp = sdWriteFile( CharPtr, 1, Scratch, FileID ); - TOS = (Temp != Scratch) ? -3 : 0; + { + vm_address_t vAddr = (vm_address_t) M_POP; + Temp = ffWriteFile( vAddr, 1, (int32_t)Scratch, FileID ); + TOS = (Temp != Scratch) ? -3 : 0; + } endcase; case ID_FILE_REPOSITION: /* ( ud fid -- ior ) */ @@ -1085,7 +1112,7 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); file_offset_t offset; cell_t offsetHigh; cell_t offsetLow; - FileID = (FileStream *) TOS; + FileID = (FileStream *) (uintptr_t) TOS; offsetHigh = M_POP; offsetLow = M_POP; /* We do not support double precision file offsets in pForth. @@ -1104,7 +1131,7 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); case ID_FILE_POSITION: /* ( fid -- ud ior ) */ { file_offset_t position; - FileID = (FileStream *) TOS; + FileID = (FileStream *) (uintptr_t) TOS; position = sdTellFile( FileID ); if (position < 0) { @@ -1143,22 +1170,30 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); case ID_FILE_FLUSH: /* ( fileid -- ior ) */ { - FileStream *Stream = (FileStream *) TOS; + FileStream *Stream = (FileStream *) (uintptr_t) TOS; TOS = (sdFlushFile( Stream ) == 0) ? 0 : THROW_FLUSH_FILE; } endcase; - case ID_FILE_RENAME: /* ( oldName newName -- ior ) */ - { - char *New = (char *) TOS; - char *Old = (char *) M_POP; - TOS = sdRenameFile( Old, New ); - } - endcase; + case ID_FILE_RENAME: /* ( oldName newName -- ior ) */ + { + /* oldName and newName are NUL terminated C strings */ + /* TODO: consider changing the API for (RENAME_FILE) to match ANS. + * Then we will have the name lengths. + */ + vm_address_t vNewName = (vm_address_t)TOS; + const char *pNewName = (const char *)pfLockMemoryReadOnly(vNewName, DP_MAX_REGION_SIZE); + vm_address_t vOldName = (vm_address_t)M_POP; + const char *pOldName = (const char *)pfLockMemoryReadOnly(vOldName, DP_MAX_REGION_SIZE); + TOS = sdRenameFile( pOldName, pNewName ); + pfUnlockMemory(vOldName, (const uint8_t *)pOldName); + pfUnlockMemory(vNewName, (const uint8_t *)pNewName); + } + endcase; case ID_FILE_RESIZE: /* ( ud fileid -- ior ) */ { - FileStream *File = (FileStream *) TOS; + FileStream *File = (FileStream *) (uintptr_t) TOS; ucell_t SizeHi = (ucell_t) M_POP; ucell_t SizeLo = (ucell_t) M_POP; TOS = ( UdIsUint64( SizeHi ) @@ -1169,26 +1204,44 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); case ID_FILL: /* ( caddr num charval -- ) */ { - register char *DstPtr; + vm_address_t dstVAddr; Temp = M_POP; /* num */ - DstPtr = (char *) M_POP; /* dst */ - for( Scratch=0; (ucell_t) Scratch < (ucell_t) Temp ; Scratch++ ) - { - *DstPtr++ = (char) TOS; - } + dstVAddr = (vm_address_t) M_POP; /* dst */ + pfSetVirtualMemory(dstVAddr, TOS, (uint32_t) Temp); M_DROP; } endcase; #ifndef PF_NO_SHELL case ID_FIND: /* ( $addr -- $addr 0 | xt +-1 ) */ - TOS = ffFind( (char *) TOS, (ExecToken *) &Temp ); - M_PUSH( Temp ); + { + vm_address_t vaddr = (vm_address_t) TOS; + cell_t totalLength = DP_FETCH_U8(vaddr) + 1; /* length including count */ + const char *pAddr = (const char *) pfLockMemoryReadOnly(vaddr, totalLength); + TOS = ffFind(pAddr, (ExecToken *) &Temp ); + pfUnlockMemory(vaddr, (const uint8_t *)pAddr); + if (TOS != 0) { + M_PUSH( Temp ); /* xt */ + } else { + M_PUSH( vaddr ); /* $addr */ + } + } endcase; - case ID_FINDNFA: - TOS = ffFindNFA( (const ForthString *) TOS, (const ForthString **) &Temp ); - M_PUSH( (cell_t) Temp ); + case ID_FINDNFA: /* ( $name -- $addr 0 | nfa -1 | nfa 1 , find NFA in dictionary ) */ + { + vm_address_t nfa = PF_VM_NULL; + vm_address_t vName = (vm_address_t) TOS; + cell_t totalLength = DP_FETCH_U8(vName) + 1; /* length including count */ + const char *pAddr = (const char *) pfLockMemoryReadOnly(vName, totalLength); + TOS = ffFindNFA((const ForthString *)pAddr, &nfa ); + pfUnlockMemory(vName, (const uint8_t *)pAddr); + if (TOS != 0) { + M_PUSH( nfa ); + } else { + M_PUSH( vName ); /* $addr */ + } + } endcase; #endif /* !PF_NO_SHELL */ @@ -1205,9 +1258,10 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); } else { - CellPtr = (cell_t *) TOS; + CellPtr = (cell_t *) (uintptr_t) TOS; CellPtr--; - if( ((ucell_t)*CellPtr) != ((ucell_t)CellPtr ^ PF_MEMORY_VALIDATOR)) + if( ((ucell_t)*CellPtr) != + (((ucell_t) (uintptr_t) CellPtr) ^ PF_MEMORY_VALIDATOR)) { TOS = -2; /* FIXME error code */ } @@ -1227,16 +1281,21 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); TOS = (cell_t)CODE_HERE; endcase; - case ID_NUMBERQ_P: /* ( addr -- 0 | n 1 ) */ -/* Convert using number converter in 'C'. -** Only supports single precision for bootstrap. -*/ - TOS = (cell_t) ffNumberQ( (char *) TOS, &Temp ); + case ID_NUMBERQ_P: /* ( addr -- 0 | n 1 ) */ { + /* Convert using number converter in 'C'. + ** Only supports single precision for bootstrap. + */ + vm_address_t vAddr = (vm_address_t) TOS; + Scratch = MASK_NAME_SIZE & DP_FETCH_U8(vAddr); /* Length of string. */ + const char *pAddr = (const char *) pfLockMemoryReadOnly(vAddr, Scratch + 1); + TOS = (cell_t) ffNumberQ(pAddr, &Temp ); + pfUnlockMemory(vAddr, (const uint8_t *) pAddr); if( TOS == NUM_TYPE_SINGLE) { M_PUSH( Temp ); /* Push single number */ } - endcase; + } + endcase; case ID_I: /* ( -- i , DO LOOP index ) */ PUSH_TOS; @@ -1245,12 +1304,15 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); #ifndef PF_NO_SHELL case ID_INCLUDE_FILE: - FileID = (FileStream *) TOS; + FileID = (FileStream *) (uintptr_t) TOS; M_DROP; /* Drop now so that INCLUDE has a clean stack. */ SAVE_REGISTERS; Scratch = ffIncludeFile( FileID ); LOAD_REGISTERS; - if( Scratch ) M_THROW(Scratch) + if( Scratch ) M_THROW(Scratch); +#if PF_DEMAND_PAGING + pfCheckPagedMemory(); +#endif endcase; #endif /* !PF_NO_SHELL */ @@ -1284,9 +1346,9 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); #endif /* !PF_NO_SHELL */ case ID_LITERAL_P: - DBUG(("ID_LITERAL_P: InsPtr = 0x%x, *InsPtr = 0x%x\n", InsPtr, *InsPtr )); PUSH_TOS; - TOS = READ_CELL_DIC(InsPtr++); + TOS = READ_CELL_DIC(InsPtr); + InsPtr += PF_CELL_SIZE; endcase; #ifndef PF_NO_SHELL @@ -1346,7 +1408,7 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); /* End of locals. Create stack frame */ DBUG(("LocalEntry: before RP@ = 0x%x, LP = 0x%x\n", TORPTR, LocalsPtr)); - M_R_PUSH(LocalsPtr); + M_R_PUSH(PTR_TO_VMA(LocalsPtr)); LocalsPtr = TORPTR; TORPTR -= TOS; DBUG(("LocalEntry: after RP@ = 0x%x, LP = 0x%x\n", @@ -1364,7 +1426,7 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); DBUG(("LocalExit: before RP@ = 0x%x, LP = 0x%x\n", TORPTR, LocalsPtr)); TORPTR = LocalsPtr; - LocalsPtr = (cell_t *) M_R_POP; + LocalsPtr = (cell_t *) (uintptr_t) M_R_POP; DBUG(("LocalExit: after RP@ = 0x%x, LP = 0x%x\n", TORPTR, LocalsPtr)); endcase; @@ -1398,7 +1460,7 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); Scratch = M_R_POP + 1; /* index */ if( Scratch == Temp ) { - InsPtr++; /* skip branch offset, exit loop */ + InsPtr += PF_CELL_SIZE; /* skip branch offset, exit loop */ } else { @@ -1426,11 +1488,11 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); #ifndef PF_NO_SHELL case ID_NAME_TO_TOKEN: - TOS = (cell_t) NameToToken((ForthString *)TOS); + TOS = (cell_t) NameToToken(TOS); endcase; case ID_NAME_TO_PREVIOUS: - TOS = (cell_t) NameToPrevious((ForthString *)TOS); + TOS = (cell_t) NameToPrevious(TOS); endcase; #endif @@ -1460,10 +1522,14 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); } else { - *((cell_t *)TOS) += M_POP; + Scratch = DP_FETCH_CELL(TOS); + Scratch += M_POP; + DP_STORE_CELL(TOS, Scratch); } #else - *((cell_t *)TOS) += M_POP; + Scratch = DP_FETCH_CELL(TOS); + Scratch += M_POP; + DP_STORE_CELL(TOS, Scratch); #endif M_DROP; endcase; @@ -1480,12 +1546,9 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); (x^y)<0 is equivalent to (x<0) != (y<0) */ if( ((OldDiff ^ (OldDiff + Delta)) /* is the limit crossed? */ & (OldDiff ^ Delta)) /* is it a wrap-around? */ - < 0 ) - { - InsPtr++; /* skip branch offset, exit loop */ - } - else - { + < 0 ) { + InsPtr += PF_CELL_SIZE; /* skip branch offset, exit loop */ + } else { /* Push index and limit back to R */ M_R_PUSH( NewIndex ); M_R_PUSH( Limit ); @@ -1507,7 +1570,7 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); { M_R_PUSH( TOS ); M_R_PUSH( Scratch ); - InsPtr++; /* skip branch offset, enter loop */ + InsPtr += PF_CELL_SIZE; /* skip branch offset, enter loop */ } M_DROP; endcase; @@ -1548,13 +1611,13 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); /* Resize memory allocated by ALLOCATE. */ case ID_RESIZE: /* ( addr1 u -- addr2 result ) */ { - cell_t *Addr1 = (cell_t *) M_POP; + cell_t *Addr1 = (cell_t *) (uintptr_t) M_POP; /* Point to validator below users address. */ cell_t *FreePtr = Addr1 - 1; - if( ((ucell_t)*FreePtr) != ((ucell_t)FreePtr ^ PF_MEMORY_VALIDATOR)) + if( ((ucell_t)*FreePtr) != (((ucell_t) (uintptr_t) FreePtr) ^ PF_MEMORY_VALIDATOR)) { /* 090218 - Fixed bug, was returning zero. */ - M_PUSH( Addr1 ); + M_PUSH(PTR_TO_VMA(Addr1)); TOS = -3; } else @@ -1565,10 +1628,10 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); { /* Copy memory including validation. */ pfCopyMemory( (char *) CellPtr, (char *) FreePtr, TOS + sizeof(cell_t) ); - *CellPtr = (cell_t)(((ucell_t)CellPtr) ^ (ucell_t)PF_MEMORY_VALIDATOR); + *CellPtr = (cell_t)(((uintptr_t)CellPtr) ^ (ucell_t)PF_MEMORY_VALIDATOR); /* 090218 - Fixed bug that was incrementing the address twice. Thanks Reinhold Straub. */ /* Increment past validator to user address. */ - M_PUSH( (cell_t) (CellPtr + 1) ); + M_PUSH(PTR_TO_VMA(CellPtr + 1)); TOS = 0; /* Result code. */ /* Mark old cell as dead so we can't free it twice. */ FreePtr[0] = 0xDeadBeef; @@ -1577,7 +1640,7 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); else { /* 090218 - Fixed bug, was returning zero. */ - M_PUSH( Addr1 ); + M_PUSH(PTR_TO_VMA(Addr1)); TOS = -4; /* FIXME Fix error code. */ } } @@ -1590,17 +1653,17 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); */ case ID_RP_FETCH: /* ( -- rp , address of top of return stack ) */ PUSH_TOS; - TOS = (cell_t)TORPTR; /* value before calling RP@ */ + TOS = PTR_TO_VMA(TORPTR); /* value before calling RP@ */ endcase; case ID_RP_STORE: /* ( rp -- , address of top of return stack ) */ - TORPTR = (cell_t *) TOS; + TORPTR = (cell_t *) (uintptr_t) TOS; M_DROP; endcase; case ID_R_ZERO: /* ( -- rbase , base of return stack ) */ PUSH_TOS; - TOS = (cell_t)gCurrentTask->td_ReturnBase; + TOS = PTR_TO_VMA(gCurrentTask->td_ReturnBase); endcase; case ID_ROLL: /* ( xu xu-1 xu-1 ... x0 u -- xu-1 xu-1 ... x0 xu ) */ @@ -1634,11 +1697,16 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); case ID_SAVE_FORTH_P: /* ( $name Entry NameSize CodeSize -- err ) */ { cell_t NameSize, CodeSize, EntryPoint; + vm_address_t vName; CodeSize = TOS; NameSize = M_POP; EntryPoint = M_POP; - ForthStringToC( gScratch, (char *) M_POP, sizeof(gScratch) ); + vName = (vm_address_t) M_POP; + Temp = DP_FETCH_U8(vName); /* length */ + const char *pFString = (const char *) pfLockMemoryReadOnly(vName, Temp + 1); + ForthStringToC( gScratch, pFString, sizeof(gScratch) ); TOS = ffSaveForth( gScratch, EntryPoint, NameSize, CodeSize ); + pfUnlockMemory(vName, (const uint8_t *) pFString); } endcase; #endif @@ -1649,11 +1717,11 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); case ID_SP_FETCH: /* ( -- sp , address of top of stack, sorta ) */ PUSH_TOS; - TOS = (cell_t)STKPTR; + TOS = PTR_TO_VMA(STKPTR); endcase; case ID_SP_STORE: /* ( sp -- , address of top of stack, sorta ) */ - STKPTR = (cell_t *) TOS; + STKPTR = (cell_t *) (uintptr_t) TOS; M_DROP; endcase; @@ -1665,19 +1733,25 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); } else { - *((cell_t *)TOS) = M_POP; + DP_STORE_CELL(TOS, M_POP); } #else - *((cell_t *)TOS) = M_POP; + DP_STORE_CELL(TOS, M_POP); #endif M_DROP; endcase; case ID_SCAN: /* ( addr cnt char -- addr' cnt' ) */ - Scratch = M_POP; /* cnt */ - Temp = M_POP; /* addr */ - TOS = ffScan( (char *) Temp, Scratch, (char) TOS, &CharPtr ); - M_PUSH((cell_t) CharPtr); + { + const char *dummy = 0; + char charToFind = TOS; + cell_t cnt = M_POP; + vm_address_t vAddr = (vm_address_t) M_POP; + const char *lockedMemory = (const char *) pfLockMemoryReadOnly(vAddr, cnt); + TOS = ffScan( lockedMemory, cnt, charToFind, &dummy ); + pfUnlockMemory(vAddr, (const uint8_t *) lockedMemory); + M_PUSH((cell_t) (vAddr + (cnt - TOS))); /* offset address by (cnt - cnt') */ + } endcase; #ifndef PF_NO_SHELL @@ -1690,10 +1764,15 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); #endif /* !PF_NO_SHELL */ case ID_SKIP: /* ( addr cnt char -- addr' cnt' ) */ - Scratch = M_POP; /* cnt */ - Temp = M_POP; /* addr */ - TOS = ffSkip( (char *) Temp, Scratch, (char) TOS, &CharPtr ); - M_PUSH((cell_t) CharPtr); + { + const char *dummy; + cell_t cnt = M_POP; /* cnt */ + vm_address_t vAddr = (vm_address_t) M_POP; + const char *lockedMemory = (const char *) pfLockMemoryReadOnly(vAddr, cnt); + TOS = ffSkip( lockedMemory, cnt, (char) TOS, &dummy ); + pfUnlockMemory(vAddr, (const uint8_t *) lockedMemory); + M_PUSH((cell_t) (vAddr + (cnt - TOS))); /* offset address by (cnt - cnt') */ + } endcase; case ID_SOURCE: /* ( -- c-addr num ) */ @@ -1703,7 +1782,7 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); endcase; case ID_SOURCE_SET: /* ( c-addr num -- ) */ - gCurrentTask->td_SourcePtr = (char *) M_POP; + gCurrentTask->td_SourcePtr = (vm_address_t) M_POP; gCurrentTask->td_SourceNum = TOS; M_DROP; endcase; @@ -1783,11 +1862,14 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); case ID_TIMES: BINARY_OP( * ); endcase; - case ID_TYPE: + case ID_TYPE: { Scratch = M_POP; /* addr */ - ioType( (char *) Scratch, TOS ); + const char * pText = (const char *) pfLockMemoryReadOnly((vm_address_t) Scratch, TOS); + ioType(pText, TOS); + pfUnlockMemory((vm_address_t) Scratch, (const uint8_t *) pText); M_DROP; - endcase; + } + endcase; case ID_TO_R: M_R_PUSH( TOS ); @@ -1799,7 +1881,7 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); case ID_VAR_CODE_BASE: DO_VAR(gCurrentDictionary->dic_CodeBase); endcase; case ID_VAR_CODE_LIMIT: DO_VAR(gCurrentDictionary->dic_CodeLimit); endcase; case ID_VAR_CONTEXT: DO_VAR(gVarContext); endcase; - case ID_VAR_DP: DO_VAR(gCurrentDictionary->dic_CodePtr.Cell); endcase; + case ID_VAR_DP: DO_VAR(gCurrentDictionary->dic_CodePtr); endcase; case ID_VAR_ECHO: DO_VAR(gVarEcho); endcase; case ID_VAR_HEADERS_BASE: DO_VAR(gCurrentDictionary->dic_HeaderBase); endcase; case ID_VAR_HEADERS_LIMIT: DO_VAR(gCurrentDictionary->dic_HeaderLimit); endcase; @@ -1830,10 +1912,10 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); } else { - TOS = *((uint16_t *)TOS); + TOS = DP_FETCH_U16(TOS); } #else - TOS = *((uint16_t *)TOS); + TOS = DP_FETCH_U16(TOS); #endif endcase; @@ -1846,10 +1928,10 @@ DBUG(("XX ah,m,l = 0x%8x,%8x,%8x - qh,l = 0x%8x,%8x\n", ah,am,al, qh,ql )); } else { - *((uint16_t *)TOS) = (uint16_t) M_POP; + DP_STORE_U16(TOS, (uint16_t) M_POP); } #else - *((uint16_t *)TOS) = (uint16_t) M_POP; + DP_STORE_U16(TOS, (uint16_t) M_POP); #endif M_DROP; endcase; @@ -1866,7 +1948,7 @@ DBUGX(("Before 0Branch: IP = 0x%x\n", InsPtr )); } else { - InsPtr++; /* skip over offset */ + InsPtr += PF_CELL_SIZE; /* skip over offset */ } M_DROP; DBUGX(("After 0Branch: IP = 0x%x\n", InsPtr )); @@ -1886,13 +1968,16 @@ DBUGX(("After 0Branch: IP = 0x%x\n", InsPtr )); ERR("pfCatch: Unrecognised token = 0x"); ffDotHex(Token); ERR(" at 0x"); - ffDotHex((cell_t) InsPtr); + ffDotHex(InsPtr); EMIT_CR; - InsPtr = 0; + InsPtr = PF_VM_NULL; endcase; } - if(InsPtr) Token = READ_CELL_DIC(InsPtr++); /* Traverse to next token in secondary. */ + if(InsPtr) { + Token = READ_CELL_DIC(InsPtr); /* Traverse to next token in secondary. */ + InsPtr += PF_CELL_SIZE; + } #ifdef PF_DEBUG M_DOTS; @@ -1902,7 +1987,7 @@ DBUGX(("After 0Branch: IP = 0x%x\n", InsPtr )); if( _CrtCheckMemory() == 0 ) { ERR("_CrtCheckMemory abort: InsPtr = 0x"); - ffDotHex((int)InsPtr); + ffDotHex(InsPtr); ERR("\n"); } #endif @@ -1913,3 +1998,4 @@ DBUGX(("After 0Branch: IP = 0x%x\n", InsPtr )); return ExceptionReturnCode; } + diff --git a/csrc/pf_io.c b/csrc/pf_io.c index d0c75d1..c4cd62f 100644 --- a/csrc/pf_io.c +++ b/csrc/pf_io.c @@ -96,7 +96,7 @@ cell_t ioKey( void ) cell_t ioAccept( char *buffer, cell_t maxChars ) { int c; - int len; + cell_t len; char *p; DBUGX(("ioAccept(0x%x, 0x%x)\n", buffer, len )); @@ -149,7 +149,6 @@ DBUGX(("ioAccept(0x%x, 0x%x)\n", buffer, len )); #define UNIMPLEMENTED(name) { MSG(name); MSG("is unimplemented!\n"); } - /***********************************************************************************/ /*********** File I/O **************************************************************/ /***********************************************************************************/ diff --git a/csrc/pf_io.h b/csrc/pf_io.h index 4db8faf..667f5f2 100644 --- a/csrc/pf_io.h +++ b/csrc/pf_io.h @@ -119,9 +119,9 @@ void ioTerm( void ); #else #ifdef PF_USER_FILEIO -/* Get user prototypes or macros from include file. -** API must match that defined above for the stubs. -*/ + /* Get user prototypes or macros from include file. + ** API must match that defined above for the stubs. + */ #include PF_USER_FILEIO #else diff --git a/csrc/pf_mem.c b/csrc/pf_mem.c index fed36eb..f412022 100644 --- a/csrc/pf_mem.c +++ b/csrc/pf_mem.c @@ -1,5 +1,5 @@ /*************************************************************** -** Memory allocator for systems that don't have real one. +** Memory allocator for systems that don't have malloc(). ** This might be useful when bringing up a new computer with no OS. ** ** For PForth based on 'C' diff --git a/csrc/pf_save.c b/csrc/pf_save.c index 6e3f331..1e1c079 100644 --- a/csrc/pf_save.c +++ b/csrc/pf_save.c @@ -354,7 +354,7 @@ static int Write32ToFile( FileStream *fid, uint32_t Val ) } /***************************************************************/ -static cell_t WriteChunkToFile( FileStream *fid, cell_t ID, char *Data, int32_t NumBytes ) +static cell_t WriteChunkToFile( FileStream *fid, cell_t ID, vm_address_t Data, int32_t NumBytes ) { cell_t numw; cell_t EvenNumW; @@ -366,7 +366,8 @@ static cell_t WriteChunkToFile( FileStream *fid, cell_t ID, char *Data, int32_t assert(EvenNumW <= UINT32_MAX); if( Write32ToFile( fid, (uint32_t)EvenNumW ) < 0 ) goto error; - numw = sdWriteFile( Data, 1, EvenNumW, fid ); + /* use demand paging just in case */ + numw = ffWriteFile( Data, 1, (int32_t)EvenNumW, fid ); if( numw != EvenNumW ) goto error; return 0; error: @@ -406,7 +407,7 @@ cell_t ffSaveForth( const char *FileName, ExecToken EntryPoint, cell_t NameSize, /* Write P4DI Dictionary Info ------------------ */ SD.sd_Version = PF_FILE_VERSION; - relativeCodePtr = ABS_TO_CODEREL(gCurrentDictionary->dic_CodePtr.Byte); /* 940225 */ + relativeCodePtr = (uint32_t) ABS_TO_CODEREL(gCurrentDictionary->dic_CodePtr); /* 940225 */ SD.sd_RelCodePtr = relativeCodePtr; SD.sd_UserStackSize = sizeof(cell_t) * (gCurrentTask->td_StackBase - gCurrentTask->td_StackLimit); SD.sd_ReturnStackSize = sizeof(cell_t) * (gCurrentTask->td_ReturnBase - gCurrentTask->td_ReturnLimit); @@ -474,21 +475,19 @@ cell_t ffSaveForth( const char *FileName, ExecToken EntryPoint, cell_t NameSize, CodeSize = MAX( (ucell_t)CodeSize, (CodeChunkSize + 2048) ); SD.sd_CodeSize = CodeSize; - convertDictionaryInfoWrite (&SD); - if( WriteChunkToFile( fid, ID_P4DI, (char *) &SD, sizeof(DictionaryInfoChunk) ) < 0 ) goto error; + if( WriteChunkToFile( fid, ID_P4DI, PTR_TO_VMA(&SD), sizeof(DictionaryInfoChunk) ) < 0 ) goto error; /* Write Name Fields if NameSize non-zero ------- */ if( NameSize > 0 ) { - if( WriteChunkToFile( fid, ID_P4NM, (char *) NAME_BASE, + if( WriteChunkToFile( fid, ID_P4NM, (vm_address_t) NAME_BASE, NameChunkSize ) < 0 ) goto error; } /* Write Code Fields ---------------------------- */ - if( WriteChunkToFile( fid, ID_P4CD, (char *) CODE_BASE, - CodeChunkSize ) < 0 ) goto error; + if( WriteChunkToFile( fid, ID_P4CD, (vm_address_t) CODE_BASE, CodeChunkSize ) < 0 ) goto error; FormSize = (uint32_t) sdTellFile( fid ) - 8; sdSeekFile( fid, 4, PF_SEEK_SET ); @@ -658,15 +657,14 @@ DBUG(("pfLoadDictionary( %s )\n", FileName )); if( sd->sd_NameSize > 0 ) { gVarContext = NAMEREL_TO_ABS(sd->sd_RelContext); /* Restore context. */ - gCurrentDictionary->dic_HeaderPtr = (ucell_t)(uint8_t *) - NAMEREL_TO_ABS(sd->sd_RelHeaderPtr); + gCurrentDictionary->dic_HeaderPtr = NAMEREL_TO_ABS(sd->sd_RelHeaderPtr); } else { gVarContext = 0; - gCurrentDictionary->dic_HeaderPtr = (ucell_t)NULL; + gCurrentDictionary->dic_HeaderPtr = PF_VM_NULL; } - gCurrentDictionary->dic_CodePtr.Byte = (uint8_t *) CODEREL_TO_ABS(sd->sd_RelCodePtr); + gCurrentDictionary->dic_CodePtr = (vm_address_t) CODEREL_TO_ABS(sd->sd_RelCodePtr); gNumPrimitives = sd->sd_NumPrimitives; /* Must match compiled dictionary. */ /* Pass EntryPoint back to caller. */ if( EntryPointPtr != NULL ) *EntryPointPtr = sd->sd_EntryPoint; @@ -693,7 +691,8 @@ DBUG(("pfLoadDictionary( %s )\n", FileName )); pfReportError("pfLoadDictionary", PF_ERR_TOO_BIG); goto error; } - numr = sdReadFile( (char *) NAME_BASE, 1, ChunkSize, fid ); + /* read using demand paging if needed */ + numr = ffReadFile( (vm_address_t) NAME_BASE, 1, ChunkSize, fid ); if( numr != ChunkSize ) goto read_error; BytesLeft -= ChunkSize; #endif /* PF_NO_SHELL */ @@ -710,7 +709,8 @@ DBUG(("pfLoadDictionary( %s )\n", FileName )); pfReportError("pfLoadDictionary", PF_ERR_TOO_BIG); goto error; } - numr = sdReadFile( (uint8_t *) CODE_BASE, 1, ChunkSize, fid ); + /* read using demand paging if needed */ + numr = ffReadFile( (vm_address_t) CODE_BASE, 1, ChunkSize, fid ); if( numr != ChunkSize ) goto read_error; BytesLeft -= ChunkSize; break; @@ -816,17 +816,27 @@ PForthDictionary pfLoadStaticDictionary( void ) gCurrentDictionary = dic = pfCreateDictionary( NewNameSize, NewCodeSize ); if( !dic ) goto nomem_error; - pfCopyMemory( (uint8_t *) dic->dic_HeaderBase, MinDicNames, sizeof(MinDicNames) ); - pfCopyMemory( (uint8_t *) dic->dic_CodeBase, MinDicCode, sizeof(MinDicCode) ); +#if PF_DEMAND_PAGING + pfWritePagedMemory((paging_address_t) dic->dic_HeaderBase, MinDicNames, sizeof(MinDicNames)); +#else + pfCopyMemory((uint8_t *) (uintptr_t) dic->dic_HeaderBase, MinDicNames, sizeof(MinDicNames)); +#endif + +#if PF_DEMAND_PAGING + pfWritePagedMemory((paging_address_t) dic->dic_CodeBase, MinDicCode, sizeof(MinDicCode)); +#else + pfCopyMemory((uint8_t *) (uintptr_t) dic->dic_CodeBase, MinDicCode, sizeof(MinDicCode)); +#endif + DBUG(("Static data copied to newly allocated dictionaries.\n")); - dic->dic_CodePtr.Byte = (uint8_t *) CODEREL_TO_ABS(CODEPTR); + dic->dic_CodePtr = CODEREL_TO_ABS(CODEPTR); gNumPrimitives = NUM_PRIMITIVES; if( NAME_BASE != 0) { /* Setup name space. */ - dic->dic_HeaderPtr = (ucell_t)(uint8_t *) NAMEREL_TO_ABS(HEADERPTR); + dic->dic_HeaderPtr = (vm_address_t) NAMEREL_TO_ABS(HEADERPTR); gVarContext = NAMEREL_TO_ABS(RELCONTEXT); /* Restore context. */ /* Find special words in dictionary for global XTs. */ diff --git a/csrc/pf_save.h b/csrc/pf_save.h index 098cd8e..05e4a18 100644 --- a/csrc/pf_save.h +++ b/csrc/pf_save.h @@ -27,7 +27,8 @@ typedef struct DictionaryInfoChunk { -/* All fields are stored in BIG ENDIAN format for consistency in data files. +/* Do NOT change this structure because it is stored in files. + * All fields are stored in BIG ENDIAN format for consistency in data files. * All fields must be the same size for easy endian conversion. * All fields must be 32 bit for file compatibility with older versions. */ diff --git a/csrc/pf_text.c b/csrc/pf_text.c index d491ecf..d90241d 100644 --- a/csrc/pf_text.c +++ b/csrc/pf_text.c @@ -300,35 +300,39 @@ char *ConvertNumberToText( cell_t Num, cell_t Base, int32_t IfSigned, int32_t Mi /*************************************************************** ** Diagnostic routine that prints memory in table format. */ -void DumpMemory( void *addr, cell_t cnt) +void DumpMemory(vm_address_t vAddr, cell_t cnt) { - cell_t ln, cn, nlines; - unsigned char *ptr, *cptr, c; - - nlines = (cnt + 15) / 16; - - ptr = (unsigned char *) addr; + const cell_t kLineSize = 16; + cell_t lineIndex, byteIndex; + cell_t numLines = (cnt + (kLineSize - 1)) / kLineSize; /* print whole last line */ + vm_address_t vPtr = vAddr; EMIT_CR; - for (ln=0; ln '}')) c = '.'; EMIT(c); } + + /* TODO handle dump of larger virtual areas. */ + pfUnlockMemory(vPtr, pAddr); + EMIT_CR; + vPtr += kLineSize; } } @@ -345,8 +349,6 @@ void TypeName( const char *Name ) ioType( FirstChar, Len ); } - - #ifdef PF_UNIT_TEST /* Unit test for string conversion routines. */ #define ASSERT_PAD_IS( index, value, msg ) \ diff --git a/csrc/pf_text.h b/csrc/pf_text.h index a816b06..f35e952 100644 --- a/csrc/pf_text.h +++ b/csrc/pf_text.h @@ -61,7 +61,7 @@ cell_t ffCompare( const char *s1, cell_t len1, const char *s2, cell_t len2 ); cell_t ffCompareText( const char *s1, const char *s2, cell_t len ); cell_t ffCompareTextCaseN( const char *s1, const char *s2, cell_t len ); -void DumpMemory( void *addr, cell_t cnt); +void DumpMemory(vm_address_t vAddr, cell_t cnt); char *ConvertNumberToText( cell_t Num, cell_t Base, int32_t IfSigned, int32_t MinChars ); void TypeName( const char *Name ); diff --git a/csrc/pf_words.c b/csrc/pf_words.c index dc183c7..6910cc5 100644 --- a/csrc/pf_words.c +++ b/csrc/pf_words.c @@ -75,9 +75,9 @@ void ffDotS( void ) } /* ( addr cnt char -- addr' cnt' , skip leading characters ) */ -cell_t ffSkip( char *AddrIn, cell_t Cnt, char c, char **AddrOut ) +cell_t ffSkip(const char *AddrIn, cell_t Cnt, char c, const char **AddrOut) { - char *s; + const char *s; s = AddrIn; @@ -105,9 +105,9 @@ DBUGX(("ffSkip: %c=0x%x, %d\n", *s, Cnt )); } /* ( addr cnt char -- addr' cnt' , scan for char ) */ -cell_t ffScan( char *AddrIn, cell_t Cnt, char c, char **AddrOut ) +cell_t ffScan(const char *AddrIn, cell_t Cnt, char c, const char **AddrOut) { - char *s; + const char *s; s = AddrIn; @@ -212,11 +212,11 @@ cell_t ffNumberQ( const char *FWord, cell_t *Num ) */ static char * Word ( char c, int Upcase ) { - char *s1,*s2,*s3; + const char *s1,*s2,*s3; cell_t n1, n2, n3; cell_t i, nc; - s1 = gCurrentTask->td_SourcePtr + gCurrentTask->td_IN; + s1 = (const char *)(uintptr_t)gCurrentTask->td_SourcePtr + gCurrentTask->td_IN; n1 = gCurrentTask->td_SourceNum - gCurrentTask->td_IN; n2 = ffSkip( s1, n1, c, &s2 ); DBUGX(("Word: s2=%c, %d\n", *s2, n2 )); @@ -251,3 +251,59 @@ char * ffLWord( char c ) { return Word( c, FALSE ); } + +size_t ffReadFile( vm_address_t vp, size_t Size, size_t nItems, FileStream * Stream ) +{ + uint32_t numBytes = (uint32_t)(Size * nItems); + if (numBytes == 0) { + return 0; + } else if (pfIsAddressInPagedMemory(vp)) { + /* Read file in blocks that will fit in locked regions. */ + cell_t numBytes = Size * nItems; + cell_t bytesRead = 0; + while (numBytes > 0) { + uint32_t bytesToRead = (numBytes < DP_MAX_REGION_SIZE) ? numBytes : DP_MAX_REGION_SIZE; + uint8_t *buffer = pfLockMemoryReadWrite(vp, bytesToRead); + cell_t numRead = sdReadFile(buffer, 1, bytesToRead, Stream); + pfUnlockMemory(vp, buffer); /* writes to backing storage */ + if (numRead < bytesToRead) { + numBytes = 0; /* no more data left */ + } else { + numBytes -= bytesToRead; + } + bytesRead += numRead; + vp += numRead; + } + return bytesRead / Size; + } else { + return sdReadFile( (void *)(uintptr_t) vp, Size, nItems, Stream); + } +} + +size_t ffWriteFile( vm_address_t vp, size_t Size, size_t nItems, FileStream * Stream ) +{ + uint32_t numBytes = (uint32_t)(Size * nItems); + if (numBytes == 0) { + return 0; + } else if (pfIsAddressInPagedMemory(vp)) { + /* Write file in blocks that will fit in locked regions. */ + cell_t bytesWritten = 0; + while (numBytes > 0) { + uint32_t bytesToWrite = (numBytes < DP_MAX_REGION_SIZE) ? numBytes : DP_MAX_REGION_SIZE; + const uint8_t *buffer = pfLockMemoryReadOnly(vp, bytesToWrite); + cell_t numWritten = sdWriteFile(buffer, 1, bytesToWrite, Stream); + pfUnlockMemory(vp, buffer); /* writes to backing storage */ + + if (numWritten < bytesToWrite) { + numBytes = 0; /* no more data left */ + } else { + numBytes -= bytesToWrite; + } + bytesWritten += numWritten; + vp += numWritten; + } + return bytesWritten / Size; + } else { + return sdWriteFile( (void *)(uintptr_t) vp, Size, nItems, Stream); + } +} diff --git a/csrc/pf_words.h b/csrc/pf_words.h index 33ec368..3bf3e30 100644 --- a/csrc/pf_words.h +++ b/csrc/pf_words.h @@ -29,8 +29,11 @@ extern "C" { void ffDot( cell_t n ); void ffDotHex( cell_t n ); void ffDotS( void ); -cell_t ffSkip( char *AddrIn, cell_t Cnt, char c, char **AddrOut ); -cell_t ffScan( char *AddrIn, cell_t Cnt, char c, char **AddrOut ); +cell_t ffSkip(const char *AddrIn, cell_t Cnt, char c, const char **AddrOut); +cell_t ffScan(const char *AddrIn, cell_t Cnt, char c, const char **AddrOut); + +size_t ffReadFile( vm_address_t vp, size_t Size, size_t nItems, FileStream * Stream ); +size_t ffWriteFile( vm_address_t vp, size_t Size, size_t nItems, FileStream * Stream ); #ifdef __cplusplus } diff --git a/csrc/pfcompil.c b/csrc/pfcompil.c index 87ba056..9849c9a 100644 --- a/csrc/pfcompil.c +++ b/csrc/pfcompil.c @@ -23,12 +23,12 @@ ** 941004 PLB Extracted IO calls from pforth_main.c ** 950320 RDG Added underflow checking for FP stack ***************************************************************/ - +#include #include "pf_all.h" #include "pfcompil.h" #define ABORT_RETURN_CODE (10) -#define UINT32_MASK ((sizeof(ucell_t)-1)) +#define UCELL_MASK (((ucell_t)PF_CELL_SIZE) - 1) /***************************************************************/ /************** Static Prototypes ******************************/ @@ -57,40 +57,41 @@ cell_t NotCompiled( const char *FunctionName ) ** Create an entry in the Dictionary for the given ExecutionToken. ** FName is name in Forth format. */ -void CreateDicEntry( ExecToken XT, const ForthStringPtr FName, ucell_t Flags ) +void CreateDicEntry(ExecToken XT, const char *FName, ucell_t Flags) { - cfNameLinks *cfnl; - - cfnl = (cfNameLinks *) gCurrentDictionary->dic_HeaderPtr; + vm_address_t headerPtr = gCurrentDictionary->dic_HeaderPtr; + vm_address_t previousNamePtr = headerPtr + PF_HEADER_OFFSET_PREVIOUS_NAME; + vm_address_t execTokenPtr = headerPtr + PF_HEADER_OFFSET_EXEC_TOKEN; + vm_address_t nfaPtr = headerPtr + PF_HEADER_OFFSET_NFA; /* Set link to previous header, if any. */ if( gVarContext ) { - WRITE_CELL_DIC( &cfnl->cfnl_PreviousName, ABS_TO_NAMEREL( gVarContext ) ); + WRITE_CELL_DIC(previousNamePtr, ABS_TO_NAMEREL( gVarContext )); } else { - cfnl->cfnl_PreviousName = 0; + WRITE_CELL_DIC(previousNamePtr, 0); } /* Put Execution token in header. */ - WRITE_CELL_DIC( &cfnl->cfnl_ExecToken, XT ); + WRITE_CELL_DIC(execTokenPtr, XT); -/* Advance Header Dictionary Pointer */ - gCurrentDictionary->dic_HeaderPtr += sizeof(cfNameLinks); +/* Advance Header Dictionary Pointer to the NFA. */ + gCurrentDictionary->dic_HeaderPtr = nfaPtr; /* Laydown name. */ gVarContext = gCurrentDictionary->dic_HeaderPtr; - pfCopyMemory( (uint8_t *) gCurrentDictionary->dic_HeaderPtr, FName, (*FName)+1 ); + pfCopyToVirtualMemory(gCurrentDictionary->dic_HeaderPtr, FName, (*FName)+1); gCurrentDictionary->dic_HeaderPtr += (*FName)+1; /* Set flags. */ - *(char*)gVarContext |= (char) Flags; + DP_STORE_U8(gVarContext, DP_FETCH_U8(gVarContext) | (uint8_t)Flags); -/* Align to quad byte boundaries with zeroes. */ - while( gCurrentDictionary->dic_HeaderPtr & UINT32_MASK ) +/* Align to cell byte boundaries with zeroes. */ + while( gCurrentDictionary->dic_HeaderPtr & UCELL_MASK ) { - *(char*)(gCurrentDictionary->dic_HeaderPtr++) = 0; + DP_STORE_U8(gCurrentDictionary->dic_HeaderPtr++, 0); } } @@ -99,45 +100,38 @@ void CreateDicEntry( ExecToken XT, const ForthStringPtr FName, ucell_t Flags ) */ void CreateDicEntryC( ExecToken XT, const char *CName, ucell_t Flags ) { - ForthString FName[LONGEST_WORD_NAME+9]; /* +1 for length, up to +9 should not be used, but is here for safety */ + ForthString FName[PF_NAME_SIZE_SAFE]; CStringToForth( FName, CName, sizeof(FName) ); CreateDicEntry( XT, FName, Flags ); } +/* Define offsets to fields in an header entry. */ +#define PF_HEADER_OFFSET_PREVIOUS_NAME (0) +#define PF_HEADER_OFFSET_EXEC_TOKEN (PF_CELL_SIZE) +#define PF_HEADER_OFFSET_NFA (PF_HEADER_OFFSET_EXEC_TOKEN + PF_CELL_SIZE) + /*************************************************************** ** Convert absolute namefield address to previous absolute name ** field address or NULL. */ -const ForthString *NameToPrevious( const ForthString *NFA ) +vm_address_t NameToPrevious(vm_address_t NFA) { - cell_t RelNamePtr; - const cfNameLinks *cfnl; - -/* DBUG(("\nNameToPrevious: NFA = 0x%x\n", (cell_t) NFA)); */ - cfnl = (const cfNameLinks *) ( ((const char *) NFA) - sizeof(cfNameLinks) ); - - RelNamePtr = READ_CELL_DIC((const cell_t *) (&cfnl->cfnl_PreviousName)); -/* DBUG(("\nNameToPrevious: RelNamePtr = 0x%x\n", (cell_t) RelNamePtr )); */ - if( RelNamePtr ) - { - return ( (ForthString *) NAMEREL_TO_ABS( RelNamePtr ) ); - } - else - { - return NULL; + vm_address_t previousNamePtr = NFA - PF_HEADER_OFFSET_NFA + PF_HEADER_OFFSET_PREVIOUS_NAME; + cell_t relativeNamePtr = READ_CELL_DIC(previousNamePtr); + if (relativeNamePtr) { + return NAMEREL_TO_ABS( relativeNamePtr ); + } else { + return PF_VM_NULL; } } + /*************************************************************** ** Convert NFA to ExecToken. */ -ExecToken NameToToken( const ForthString *NFA ) +ExecToken NameToToken(vm_address_t NFA) { - const cfNameLinks *cfnl; - -/* Convert absolute namefield address to absolute link field address. */ - cfnl = (const cfNameLinks *) ( ((const char *) NFA) - sizeof(cfNameLinks) ); - - return READ_CELL_DIC((const cell_t *) (&cfnl->cfnl_ExecToken)); + vm_address_t tokenPtr = NFA - PF_HEADER_OFFSET_NFA + PF_HEADER_OFFSET_EXEC_TOKEN; + return READ_CELL_DIC(tokenPtr); } /*************************************************************** @@ -415,14 +409,14 @@ PForthDictionary pfBuildDictionary( cell_t HeaderSize, cell_t CodeSize ) ** ( xt -- nfa 1 , x 0 , find NFA in dictionary from XT ) ** 1 for IMMEDIATE values */ -cell_t ffTokenToName( ExecToken XT, const ForthString **NFAPtr ) +cell_t ffTokenToName( ExecToken XT, vm_address_t *NFAPtr ) { - const ForthString *NameField; + vm_address_t NameField; cell_t Searching = TRUE; cell_t Result = 0; ExecToken TempXT; - NameField = (ForthString *) gVarContext; + NameField = gVarContext; DBUGX(("\ffCodeToName: gVarContext = 0x%x\n", gVarContext)); do @@ -439,7 +433,7 @@ DBUGX(("ffCodeToName: NFA = 0x%x\n", NameField)); else { NameField = NameToPrevious( NameField ); - if( NameField == NULL ) + if( NameField == PF_VM_NULL ) { *NFAPtr = 0; Searching = FALSE; @@ -454,44 +448,50 @@ DBUGX(("ffCodeToName: NFA = 0x%x\n", NameField)); ** ( $name -- $addr 0 | nfa -1 | nfa 1 , find NFA in dictionary ) ** 1 for IMMEDIATE values */ -cell_t ffFindNFA( const ForthString *WordName, const ForthString **NFAPtr ) +cell_t ffFindNFA( const ForthString *WordName, vm_address_t *NFAPtr ) { const ForthString *WordChar; uint8_t WordLen; - const char *NameField, *NameChar; + vm_address_t vNameField, vNextNameField; int8_t NameLen; cell_t Searching = TRUE; cell_t Result = 0; + const char *pNameField; + const char *pNameChar; WordLen = (uint8_t) ((ucell_t)*WordName & MASK_NAME_SIZE); WordChar = WordName+1; - NameField = (ForthString *) gVarContext; + vNameField = gVarContext; DBUG(("\nffFindNFA: WordLen = %d, WordName = %*s\n", WordLen, WordLen, WordChar )); DBUG(("\nffFindNFA: gVarContext = 0x%x\n", gVarContext)); do { - NameLen = (uint8_t) ((ucell_t)(*NameField) & MASK_NAME_SIZE); - NameChar = NameField+1; + uint8_t countAndFlags = DP_FETCH_U8(vNameField); + NameLen = (uint8_t) (countAndFlags & MASK_NAME_SIZE); /* DBUG((" %c\n", (*NameField & FLAG_SMUDGE) ? 'S' : 'V' )); */ - if( ((*NameField & FLAG_SMUDGE) == 0) && + pNameField = (const char *) pfLockMemoryReadOnly(vNameField, NameLen + 1); + pNameChar = pNameField + 1; + if( ((countAndFlags & FLAG_SMUDGE) == 0) && (NameLen == WordLen) && - ffCompareTextCaseN( NameChar, WordChar, WordLen ) ) /* FIXME - slow */ + ffCompareTextCaseN( pNameChar, WordChar, WordLen ) ) /* FIXME - slow */ { DBUG(("ffFindNFA: found it at NFA = 0x%x\n", NameField)); - *NFAPtr = NameField ; - Result = ((*NameField) & FLAG_IMMEDIATE) ? 1 : -1; + *NFAPtr = vNameField ; + Result = (countAndFlags & FLAG_IMMEDIATE) ? 1 : -1; Searching = FALSE; } else { - NameField = NameToPrevious( NameField ); - if( NameField == NULL ) + vNextNameField = NameToPrevious( vNameField ); + if( vNextNameField == PF_VM_NULL ) { - *NFAPtr = WordName; + *NFAPtr = PTR_TO_VMA(WordName); Searching = FALSE; } } + pfUnlockMemory(vNameField, (const uint8_t *) pNameField); + vNameField = vNextNameField; } while ( Searching); DBUG(("ffFindNFA: returns 0x%x\n", Result)); return Result; @@ -504,7 +504,7 @@ DBUG(("ffFindNFA: returns 0x%x\n", Result)); */ cell_t ffFind( const ForthString *WordName, ExecToken *pXT ) { - const ForthString *NFA; + vm_address_t NFA; cell_t Result; Result = ffFindNFA( WordName, &NFA ); @@ -515,7 +515,7 @@ DBUG(("ffFind: %8s at 0x%x\n", WordName+1, NFA)); /* WARNING, not NUL terminated } else { - *pXT = (ExecToken) WordName; + *pXT = (ExecToken)PTR_TO_VMA(WordName); } return Result; @@ -543,16 +543,16 @@ DBUG(("ffFindC: %s\n", WordName )); static cell_t ffCheckDicRoom( void ) { cell_t RoomLeft; - RoomLeft = (char *)gCurrentDictionary->dic_HeaderLimit - - (char *)gCurrentDictionary->dic_HeaderPtr; + RoomLeft = gCurrentDictionary->dic_HeaderLimit - + gCurrentDictionary->dic_HeaderPtr; if( RoomLeft < DIC_SAFETY_MARGIN ) { pfReportError("ffCheckDicRoom", PF_ERR_HEADER_ROOM); return PF_ERR_HEADER_ROOM; } - RoomLeft = (char *)gCurrentDictionary->dic_CodeLimit - - (char *)gCurrentDictionary->dic_CodePtr.Byte; + RoomLeft = gCurrentDictionary->dic_CodeLimit - + gCurrentDictionary->dic_CodePtr; if( RoomLeft < DIC_SAFETY_MARGIN ) { pfReportError("ffCheckDicRoom", PF_ERR_CODE_ROOM); @@ -567,13 +567,13 @@ static cell_t ffCheckDicRoom( void ) void ffCreateSecondaryHeader( const ForthStringPtr FName) { pfDebugMessage("ffCreateSecondaryHeader()\n"); -/* Check for dictionary overflow. */ + /* Check for dictionary overflow. */ if( ffCheckDicRoom() ) return; pfDebugMessage("ffCreateSecondaryHeader: CheckRedefinition()\n"); CheckRedefinition( FName ); -/* Align CODE_HERE */ - CODE_HERE = (cell_t *)( (((ucell_t)CODE_HERE) + UINT32_MASK) & ~UINT32_MASK); + /* Align CODE_HERE */ + CODE_HERE = (CODE_HERE + UCELL_MASK) & ~UCELL_MASK; CreateDicEntry( (ExecToken) ABS_TO_CODEREL(CODE_HERE), FName, FLAG_SMUDGE ); } @@ -656,7 +656,7 @@ void ffStringDefer( const ForthStringPtr FName, ExecToken DefaultXT ) /* Convert name then create deferred dictionary entry. */ static void CreateDeferredC( ExecToken DefaultXT, const char *CName ) { - char FName[LONGEST_WORD_NAME+9]; /* +1 for length, up to +9 should not be used, but is here for safety */ + char FName[PF_NAME_SIZE_SAFE]; CStringToForth( FName, CName, sizeof(FName) ); ffStringDefer( FName, DefaultXT ); } @@ -677,7 +677,7 @@ void ffDefer( void ) /* Unsmudge the word to make it visible. */ static void ffUnSmudge( void ) { - *(char*)gVarContext &= ~FLAG_SMUDGE; + DP_STORE_U8(gVarContext, DP_FETCH_U8(gVarContext) & (uint8_t)(~FLAG_SMUDGE)); } /* Implement ; */ @@ -728,25 +728,20 @@ void ffLiteral( cell_t Num ) #ifdef PF_SUPPORT_FP void ffFPLiteral( PF_FLOAT fnum ) { - /* Hack for Metrowerks compiler which won't compile the - * original expression. - */ - PF_FLOAT *temp; - cell_t *dicPtr; + vm_address_t dicPtr; /* Make sure that literal float data is float aligned. */ - dicPtr = CODE_HERE + 1; - while( (((ucell_t) dicPtr++) & (sizeof(PF_FLOAT) - 1)) != 0) + dicPtr = CODE_HERE + PF_CELL_SIZE; + while( (((ucell_t) dicPtr) & (sizeof(PF_FLOAT) - 1)) != 0) { + dicPtr += PF_CELL_SIZE; DBUG((" comma NOOP to align FPLiteral\n")); CODE_COMMA( ID_NOOP ); } CODE_COMMA( ID_FP_FLITERAL_P ); - temp = (PF_FLOAT *)CODE_HERE; - WRITE_FLOAT_DIC(temp,fnum); /* Write to dictionary. */ - temp++; - CODE_HERE = (cell_t *) temp; + WRITE_FLOAT_DIC(CODE_HERE, fnum); /* Write to dictionary. */ + CODE_HERE += sizeof(PF_FLOAT); } #endif /* PF_SUPPORT_FP */ @@ -784,7 +779,7 @@ DBUG(("FindAndCompile: IMMEDIATE, theWord = 0x%x\n", theWord )); cell_t NumResult; DBUG(("FindAndCompile: not found, try number?\n" )); - PUSH_DATA_STACK( theWord ); /* Push text of number */ + PUSH_PTR_DATA_STACK( theWord ); /* Push text of number */ exception = pfCatch( gNumberQ_XT ); if( exception ) goto error; @@ -839,11 +834,14 @@ ThrowCode ffInterpret( void ) cell_t flag; char *theWord; ThrowCode exception = 0; + /* td_SourcePtr may be pointing to a string in the dictionary. */ + vm_address_t saveSource = gCurrentTask->td_SourcePtr; + /* It may be difficult to make td_SourcePtr const because of refill(). */ + gCurrentTask->td_SourcePtr = PTR_TO_VMA(pfLockMemoryReadOnly(saveSource, gCurrentTask->td_SourceNum)); /* Is there any text left in Source ? */ while( gCurrentTask->td_IN < (gCurrentTask->td_SourceNum) ) { - pfDebugMessage("ffInterpret: calling ffWord(()\n"); theWord = ffLWord( BLANK ); DBUG(("ffInterpret: theWord = 0x%x, Len = %d\n", theWord, *theWord )); @@ -853,7 +851,7 @@ ThrowCode ffInterpret( void ) flag = 0; if( gLocalCompiler_XT ) { - PUSH_DATA_STACK( theWord ); /* Push word. */ + PUSH_PTR_DATA_STACK( theWord ); /* Push word. */ exception = pfCatch( gLocalCompiler_XT ); if( exception ) goto error; flag = POP_DATA_STACK; /* Compiled local? */ @@ -869,6 +867,7 @@ ThrowCode ffInterpret( void ) gCurrentTask->td_SourceNum ) ); } error: + pfUnlockMemory(saveSource, (const uint8_t *)(uintptr_t) gCurrentTask->td_SourcePtr); return exception; } @@ -956,7 +955,7 @@ ThrowCode ffIncludeFile( FileStream *InputFile ) exception = ffOuterInterpreterLoop(); if( exception ) { - int i; + cell_t i; /* Report line number and nesting level. */ MSG("INCLUDE error on line #"); ffDot(gCurrentTask->td_LineNumber); MSG(", level = "); ffDot(gIncludeIndex ); @@ -965,7 +964,7 @@ ThrowCode ffIncludeFile( FileStream *InputFile ) /* Dump line of error and show offset in line for >IN */ for( i=0; itd_SourceNum; i++ ) { - char c = gCurrentTask->td_SourcePtr[i]; + char c = ((char *)(uintptr_t)gCurrentTask->td_SourcePtr)[i]; if( c == '\t' ) c = ' '; EMIT(c); } @@ -1071,7 +1070,7 @@ cell_t ffConvertStreamToSourceID( FileStream *Stream ) } else { - Result = (cell_t) Stream; + Result = PTR_TO_VMA(Stream); } return Result; } @@ -1093,7 +1092,7 @@ FileStream * ffConvertSourceIDToStream( cell_t id ) } else { - stream = (FileStream *) id; + stream = (FileStream *) (uintptr_t) id; } return stream; } @@ -1106,7 +1105,7 @@ FileStream * ffConvertSourceIDToStream( cell_t id ) static cell_t readLineFromStream( char *buffer, cell_t maxChars, FileStream *stream ) { int c; - int len; + cell_t len; char *p; static int lastChar = 0; int done = 0; @@ -1182,7 +1181,7 @@ cell_t ffRefill( void ) } else { - Num = readLineFromStream( gCurrentTask->td_SourcePtr, TIB_SIZE, + Num = readLineFromStream( (char *)(uintptr_t)gCurrentTask->td_SourcePtr, TIB_SIZE, gCurrentTask->td_InputStream ); if( Num == EOF ) { @@ -1197,7 +1196,7 @@ cell_t ffRefill( void ) /* echo input if requested */ if( gVarEcho && ( Num > 0)) { - ioType( gCurrentTask->td_SourcePtr, gCurrentTask->td_SourceNum ); + ioType( (const char *)(uintptr_t)gCurrentTask->td_SourcePtr, gCurrentTask->td_SourceNum ); EMIT_CR; } diff --git a/csrc/pfcompil.h b/csrc/pfcompil.h index f95ca43..a94522e 100644 --- a/csrc/pfcompil.h +++ b/csrc/pfcompil.h @@ -28,25 +28,25 @@ extern "C" { #endif Err ffPushInputStream( FileStream *InputFile ); -ExecToken NameToToken( const ForthString *NFA ); +ExecToken NameToToken(vm_address_t NFA); FileStream * ffConvertSourceIDToStream( cell_t id ); FileStream *ffPopInputStream( void ); cell_t ffConvertStreamToSourceID( FileStream *Stream ); cell_t ffFind( const ForthString *WordName, ExecToken *pXT ); cell_t ffFindC( const char *WordName, ExecToken *pXT ); -cell_t ffFindNFA( const ForthString *WordName, const ForthString **NFAPtr ); +cell_t ffFindNFA( const ForthString *WordName, vm_address_t *NFAPtr ); cell_t ffNumberQ( const char *FWord, cell_t *Num ); cell_t ffRefill( void ); -cell_t ffTokenToName( ExecToken XT, const ForthString **NFAPtr ); +cell_t ffTokenToName( ExecToken XT, vm_address_t *NFAPtr ); cell_t *NameToCode( ForthString *NFA ); PForthDictionary pfBuildDictionary( cell_t HeaderSize, cell_t CodeSize ); char *ffWord( char c ); char *ffLWord( char c ); -const ForthString *NameToPrevious( const ForthString *NFA ); +vm_address_t NameToPrevious(vm_address_t NFA); cell_t FindSpecialCFAs( void ); cell_t FindSpecialXTs( void ); cell_t NotCompiled( const char *FunctionName ); -void CreateDicEntry( ExecToken XT, const ForthStringPtr FName, ucell_t Flags ); +void CreateDicEntry(ExecToken XT, const char *FName, ucell_t Flags); void CreateDicEntryC( ExecToken XT, const char *CName, ucell_t Flags ); void ff2Literal( cell_t dHi, cell_t dLo ); void ffALiteral( cell_t Num ); diff --git a/csrc/pfinnrfp.h b/csrc/pfinnrfp.h index 3e35185..85c4154 100644 --- a/csrc/pfinnrfp.h +++ b/csrc/pfinnrfp.h @@ -59,10 +59,10 @@ } else { - *((PF_FLOAT *) TOS) = FP_TOS; + DP_STORE_FLOAT(TOS, FP_TOS); } #else - *((PF_FLOAT *) TOS) = FP_TOS; + DP_STORE_FLOAT(TOS, FP_TOS); #endif M_FP_DROP; /* drop FP value */ M_DROP; /* drop addr */ @@ -147,10 +147,10 @@ } else { - FP_TOS = *((PF_FLOAT *) TOS); + FP_TOS = DP_FETCH_FLOAT(TOS); } #else - FP_TOS = *((PF_FLOAT *) TOS); + FP_TOS = DP_FETCH_FLOAT(TOS); #endif M_DROP; break; @@ -283,17 +283,10 @@ case ID_FP_FLITERAL_P: PUSH_FP_TOS; -#if 0 -/* Some wimpy compilers can't handle this! */ - FP_TOS = *(((PF_FLOAT *)InsPtr)++); -#else { - PF_FLOAT *fptr; - fptr = (PF_FLOAT *)InsPtr; - FP_TOS = READ_FLOAT_DIC( fptr++ ); - InsPtr = (cell_t *) fptr; + FP_TOS = READ_FLOAT_DIC(InsPtr); + InsPtr += sizeof(PF_FLOAT); } -#endif endcase; case ID_FP_FLN: /* ( -- ) ( F: r1 -- r2 ) */ diff --git a/csrc/pforth.h b/csrc/pforth.h index 8dfdcc2..b8e5f3d 100644 --- a/csrc/pforth.h +++ b/csrc/pforth.h @@ -25,19 +25,66 @@ ** ***************************************************************/ -/* Define stubs for data types so we can pass pointers but not touch inside. */ +/* Opaque pointer types used to hide internal structures. */ typedef void *PForthTask; typedef void *PForthDictionary; #include + +#if INTPTR_MAX == INT64_MAX + #define PF_POINTER_SIZE 8 +#elif INTPTR_MAX == INT32_MAX + #define PF_POINTER_SIZE 4 +#elif INTPTR_MAX == INT16_MAX + #define PF_POINTER_SIZE 2 +#else + #error "Unsupported pointer size" +#endif + +/* Set CELL size to match pointer size if not defined. */ +#ifndef PF_CELL_SIZE + #if PF_POINTER_SIZE >= 4 + #define PF_CELL_SIZE PF_POINTER_SIZE + #else + #define PF_CELL_SIZE 4 + #endif +#endif /* PF_CELL_SIZE */ + +#if (PF_CELL_SIZE < PF_POINTER_SIZE) + #error "PF_CELL_SIZE must be at least as big as a pointer." +#endif + /* Integer types for Forth cells, signed and unsigned: */ -typedef intptr_t cell_t; -#define PF_SIZEOF_CELL __SIZEOF_INTPTR__ -typedef uintptr_t ucell_t; +#if (PF_CELL_SIZE == 8) + typedef int64_t cell_t; + typedef uint64_t ucell_t; +#elif (PF_CELL_SIZE == 4) + typedef int32_t cell_t; + typedef uint32_t ucell_t; +#else + #error "Unsupported PF_CELL_SIZE" +#endif -typedef ucell_t ExecToken; /* Execution Token */ +typedef cell_t ExecToken; /* Execution Token */ typedef cell_t ThrowCode; +#ifndef PF_DEMAND_PAGING + #if INTPTR_MAX == INT16_MAX + /* The only way to address enough RAM for the dictionary is through demand paging. */ + #define PF_DEMAND_PAGING 1 + #else /* INTPTR_MAX */ + #define PF_DEMAND_PAGING 0 + #endif /* INTPTR_MAX */ +#endif /* PF_DEMAND_PAGING */ + +typedef ucell_t vm_address_t; /** an address that may be in physical or paged memory */ +#define PTR_TO_VMA(p) ((vm_address_t)(uintptr_t)(p)) +#define PF_VM_NULL PTR_TO_VMA(0) + +#ifndef PF_ASSERT_ENABLED +#define PF_ASSERT_ENABLED 1 +#endif /* PF_ASSERT_ENABLED */ + #ifdef __cplusplus extern "C" { #endif @@ -90,8 +137,20 @@ ThrowCode pfIncludeFile( const char *FileName ); /* Execute a Forth word by name. */ ThrowCode pfExecIfDefined( const char *CString ); +/* Assertion macro for pForth. */ +extern cell_t gPfAssertEnabled; +#define PF_ASSERT(_expr) do { \ + if (!(_expr)) { \ + if (gPfAssertEnabled) { \ + pfMessage("PF_ASSERT failed: " #_expr "\n"); \ + (void)(*(volatile int *)0); \ + } \ + } \ +} while(0) + #ifdef __cplusplus } #endif #endif /* _pforth_h */ + diff --git a/csrc/posix/pf_io_posix.c b/csrc/posix/pf_io_posix.c index 526726f..b4c518d 100644 --- a/csrc/posix/pf_io_posix.c +++ b/csrc/posix/pf_io_posix.c @@ -157,7 +157,7 @@ cell_t sdSleepMillis(cell_t msec) while (micros > 0) { napTime = (micros > kMaxMicros) ? kMaxMicros : micros; - if (usleep(napTime)) + if (usleep((useconds_t)napTime)) { perror("sdSleepMillis: usleep failed"); return -1; diff --git a/csrc/sources.cmake b/csrc/sources.cmake index c4dcf43..0aa2100 100644 --- a/csrc/sources.cmake +++ b/csrc/sources.cmake @@ -18,6 +18,7 @@ pfcompfp.h pfcompil.h pfinnrfp.h pforth.h +paging/dmpaging.h pf_cglue.c pf_clib.c pf_core.c @@ -30,4 +31,7 @@ pf_text.c pf_words.c pfcompil.c pfcustom.c +paging/pagedmem.c +paging/lockpage.c +paging/qadmpage.c diff --git a/fth/misc2.fth b/fth/misc2.fth index b1ae8ea..b4285f3 100644 --- a/fth/misc2.fth +++ b/fth/misc2.fth @@ -210,20 +210,26 @@ VARIABLE SPAN CODELIMIT HERE - ; +: U.HEX ( n -- , print a as hex unsigned ) + base @ + swap hex u. + base ! +; + : MAP ( -- , dump interesting dictionary info ) ." Code Segment" cr - ." CODEBASE = " codebase .hex cr - ." HERE = " here .hex cr - ." CODELIMIT = " codelimit .hex cr + ." CODEBASE = 0x" codebase u.hex cr + ." HERE = 0x" here u.hex cr + ." CODELIMIT = 0x" codelimit u.hex cr ." Compiled Code Size = " here codebase - . cr ." CODE-SIZE = " code-size @ . cr ." Code Room UNUSED = " UNUSED . cr ." Name Segment" cr - ." NAMEBASE = " namebase .hex cr - ." HEADERS-PTR @ = " headers-ptr @ .hex cr - ." NAMELIMIT = " namelimit .hex cr - ." CONTEXT @ = " context @ .hex cr - ." LATEST = " latest .hex ." = " latest id. cr + ." NAMEBASE = 0x" namebase u.hex cr + ." HEADERS-PTR @ = 0x" headers-ptr @ u.hex cr + ." NAMELIMIT = 0x" namelimit u.hex cr + ." CONTEXT @ = 0x" context @ u.hex cr + ." LATEST = 0x" latest u.hex ." = " latest id. cr ." Compiled Name size = " headers-ptr @ namebase - . cr ." HEADERS-SIZE = " headers-size @ . cr ." Name Room Left = " namelimit headers-ptr @ - . cr diff --git a/fth/t_file.fth b/fth/t_file.fth index a947d82..b550767 100644 --- a/fth/t_file.fth +++ b/fth/t_file.fth @@ -59,7 +59,6 @@ include? }T{ t_tools.fth true fp-require-e ! - false value verbose : testing @@ -180,6 +179,7 @@ T{ 0. FID2 @ REPOSITION-FILE -> 0 }T T{ CBUF BUF 29 FID2 @ READ-FILE -> 29 0 }T T{ PAD 29 BUF 29 S= -> TRUE }T T{ PAD 30 BUF 30 S= -> FALSE }T +TEST2 T{ CBUF BUF 29 FID2 @ READ-FILE -> 21 0 }T T{ PAD 29 + 21 BUF 21 S= -> TRUE }T T{ FID2 @ FILE-SIZE DROP FID2 @ FILE-POSITION DROP D= -> TRUE }T diff --git a/fth/t_floats.fth b/fth/t_floats.fth index 2d95f7a..66c49c8 100644 --- a/fth/t_floats.fth +++ b/fth/t_floats.fth @@ -163,5 +163,9 @@ T{ my-abcs abc.f1 f@ 23.45 0.0 F~ }T{ true }T T{ my-abcs abc.w2 @ }T{ 98765 }T \ ----------------------------------------------------- \ +\ Compile FLOAT into the dictionary. +: TF.123 123.456 ; +T{ TF.123 123.456 0.0 F~ }T{ true }T + }TEST diff --git a/platforms/unix/Makefile b/platforms/unix/Makefile index 43f59de..67f9131 100644 --- a/platforms/unix/Makefile +++ b/platforms/unix/Makefile @@ -47,7 +47,7 @@ PFDICDAT = pfdicdat.h PFORTHAPP = pforth_standalone FULL_WARNINGS = \ - --std=c89 \ + --std=c99 \ -fsigned-char \ -fno-builtin \ -fno-unroll-loops \ @@ -69,26 +69,39 @@ ASAN ?= 0 # Set ASANOPTS based on ASAN value ifeq ($(ASAN),1) ASANOPTS = -fsanitize=address + ifneq ($(UNAME),Darwin) + ASANOPTS += -static-libasan + LDADD += -latomic + endif else ASANOPTS = endif +# Enable FLOAT support by default. Turn it off by passing FLOAT=0 +FLOAT ?= 1 +ifeq ($(FLOAT),1) + FPOPTS = -DPF_SUPPORT_FP +else + FPOPTS = +endif + EMBCCOPTS = -DPF_STATIC_DIC #-DPF_NO_FILEIO ####################################### PFINCLUDES = pf_all.h pf_cglue.h pf_clib.h pf_core.h pf_float.h \ pf_guts.h pf_host.h pf_inc1.h pf_io.h pf_mem.h pf_save.h \ pf_text.h pf_types.h pf_win32.h pf_words.h pfcompfp.h \ - pfcompil.h pfinnrfp.h pforth.h + pfcompil.h pfinnrfp.h pforth.h paging/dmpaging.h PFBASESOURCE = pf_cglue.c pf_clib.c pf_core.c pf_inner.c \ - pf_io.c pf_io_none.c pf_main.c pf_mem.c pf_save.c \ - pf_text.c pf_words.c pfcompil.c pfcustom.c -PFSOURCE = $(PFBASESOURCE) $(IO_SOURCE) + pf_io.c pf_io_none.c pf_mem.c pf_save.c \ + pf_text.c pf_words.c pfcompil.c pfcustom.c \ + paging/pagedmem.c paging/lockpage.c paging/qadmpage.c +PFSOURCE = pf_main.c $(PFBASESOURCE) $(IO_SOURCE) VPATH = .:$(CSRCDIR):$(CSRCDIR)/posix:$(CSRCDIR)/stdio:$(CSRCDIR)/win32_console:$(CSRCDIR)/win32 XCFLAGS = $(CCOPTS) -XCPPFLAGS = -DPF_SUPPORT_FP -D_DEFAULT_SOURCE -D_GNU_SOURCE +XCPPFLAGS = $(FPOPTS) -D_DEFAULT_SOURCE -D_GNU_SOURCE XLDFLAGS = $(WIDTHOPT) CPPFLAGS = -I. $(XCPPFLAGS) @@ -163,6 +176,10 @@ help: @echo "" @echo " The file 'fth/pfdicdat.h' is generated by pForth. It contains a binary image of the Forth dictionary." @echo " It allows pForth to work as a standalone image that does not need to load a dictionary file." + @echo "" + @echo "Options:" + @echo " ASAN=1 , turns on address sanitization" + @echo " FLOAT=0 , turn OFF float support, ON by default" test: $(PFORTHAPP) cd $(FTHDIR) && $(BUILDDIR)/$(PFORTHAPP) -q t_corex.fth diff --git a/platforms/unix/paging/README.md b/platforms/unix/paging/README.md new file mode 100644 index 0000000..642a374 --- /dev/null +++ b/platforms/unix/paging/README.md @@ -0,0 +1 @@ +This folder is needed for building pForth with demand paging on a Unix system.