From c9d8e55926ab583641b314d09bc94a8f4d498cb0 Mon Sep 17 00:00:00 2001 From: sy2002 Date: Sat, 22 Aug 2015 01:11:21 +0200 Subject: [PATCH] fixed instruction fetch from BRAM, updated qnice toolchain & docs --- .gitignore | 2 + doc/nice_can.pdf | Bin 0 -> 185133 bytes doc/qnice_intro.pdf | Bin 0 -> 185930 bytes emulator/ide_simulation.c | 721 +++++++++++++ emulator/ide_simulation.h | 13 + emulator/make.bash | 2 + emulator/qnice.c | 976 ++++++++++++++++++ emulator/uart_2681.c | 236 +++++ emulator/uart_2681.h | 48 + monitor/io_library.asm | 216 ++++ monitor/math_library.asm | 50 + monitor/mem_library.asm | 38 + monitor/monitor.asm | 8 + monitor/monitor.lis | 1796 +++++++++++++++++++++++++++++++++ monitor/monitor.out | 1768 ++++++++++++++++++++++++++++++++ monitor/qmon.asm | 247 +++++ monitor/string_library.asm | 133 +++ monitor/sysdef.asm | 31 + test_programs/bram.asm | 44 +- test_programs/bram.lis | 63 +- test_programs/bram.out | 44 +- test_programs/bram.rom | 30 +- test_programs/moves.asm | 3 + test_programs/moves.lis | 66 -- test_programs/moves.out | 51 - test_programs/moves.rom | 51 - test_programs/ramstacksub.asm | 2 + test_programs/ramstacksub.lis | 58 -- test_programs/ramstacksub.out | 57 -- test_programs/ramstacksub.rom | 57 -- test_programs/regbank.lis | 129 --- test_programs/regbank.out | 87 -- test_programs/regbank.rom | 87 -- test_programs/til_count.lis | 67 -- test_programs/til_count.out | 45 - test_programs/til_count.rom | 45 - test_programs/til_count_rom | 45 - tools/asm | 33 + tools/qasm.c | 1065 +++++++++++++++++++ vhdl/env1_globals.vhd | 8 +- vhdl/qnice_cpu.vhd | 51 +- 41 files changed, 7579 insertions(+), 894 deletions(-) create mode 100644 doc/nice_can.pdf create mode 100644 doc/qnice_intro.pdf create mode 100644 emulator/ide_simulation.c create mode 100755 emulator/ide_simulation.h create mode 100755 emulator/make.bash create mode 100644 emulator/qnice.c create mode 100644 emulator/uart_2681.c create mode 100644 emulator/uart_2681.h create mode 100644 monitor/io_library.asm create mode 100644 monitor/math_library.asm create mode 100644 monitor/mem_library.asm create mode 100644 monitor/monitor.asm create mode 100644 monitor/monitor.lis create mode 100644 monitor/monitor.out create mode 100644 monitor/qmon.asm create mode 100644 monitor/string_library.asm create mode 100644 monitor/sysdef.asm delete mode 100644 test_programs/moves.lis delete mode 100644 test_programs/moves.out delete mode 100644 test_programs/moves.rom delete mode 100644 test_programs/ramstacksub.lis delete mode 100644 test_programs/ramstacksub.out delete mode 100644 test_programs/ramstacksub.rom delete mode 100644 test_programs/regbank.lis delete mode 100644 test_programs/regbank.out delete mode 100644 test_programs/regbank.rom delete mode 100644 test_programs/til_count.lis delete mode 100644 test_programs/til_count.out delete mode 100644 test_programs/til_count.rom delete mode 100644 test_programs/til_count_rom create mode 100755 tools/asm create mode 100644 tools/qasm.c diff --git a/.gitignore b/.gitignore index 780d7651..dbd00535 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ impact.xsl impact_impact.xwbt qasm2rom register_file.sym +qnice +qasm diff --git a/doc/nice_can.pdf b/doc/nice_can.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8186e2bbb54abd66a30fac01c49f23679c38d5ef GIT binary patch literal 185133 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 literal 0 HcmV?d00001 diff --git a/doc/qnice_intro.pdf b/doc/qnice_intro.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f8006df6d25cc1054ff8c1b70797da75ae410496 GIT binary patch literal 185930 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 literal 0 HcmV?d00001 diff --git a/emulator/ide_simulation.c b/emulator/ide_simulation.c new file mode 100644 index 00000000..48e4f1e7 --- /dev/null +++ b/emulator/ide_simulation.c @@ -0,0 +1,721 @@ +/* This programm emulates an IDE controller of a compact flash card (cf) in True IDE mode. +* +* Following restrictions apply to this simulation: +* --- The simulation doesn't care about the mapping between the I/O IDE registers in the hosts main memory +* and his internal registers (in other words the physical connection). The I/O registers of the host are +* not used. A read or write to the hosts registers results in a direct read or write of the internal drive +* registers. The signal management nessecary to provide this in reality is ignored. +*--- A IDE controller can have more than one device attached. +* The device selection is done by setting the DEV bit (bit 4) of the SDH_REGISTER. Anyway the registers are +* always written to any attached drive. The drives embbeded controller then decides wether to take action or +* not. The DEV bit is expected to be set to 0 and is further ignored. It is always assumed that device0 +* should execute the given commands. +*--- The Simulator does not support Power Management Features: The device is always expected to be powered up. +* Other modes like Sleep Mode are not supported. The power up itself, which does a lot of things like +* internal diagnostic, is not simulated either. Only the register initialization must be done by calling +* initializeIDEDevice() once. The qnice simulator should do this before interpreting any assembler code. +*--- Only a small subset of the possible and partly required (at least by the cf or ATA specification) +* commands are supported: +* Read Sector(s) +* Read Verify +* Request Sense +* Write Sector(s) +* Write Verify +*--- The BSY bit is set like in a real drive although you can never verify that in assembler: when the next +* assembler directive is interpreted the BSY bit has been reset to zero (we would need multithreading to +* implement it otherwise). Anyway the BSY bit should always been checked in the assembler code because with +* a real drive it may take some time until a command is executed or aborted and the BSY bit is reseted to 0. +*--- Currently only CHS addressing is supported even though according to cf spec LBA should be supported too. +* @Author: Kai Lutterbeck +*/ + +#include +#include +#include +#include "ide_simulation.h" + +#undef DEBUG + +/* A 32MB Compact Flash Card (CF) is defined because available memory is limited. +* The numbers for CHS are from the SanDisc CF specifiction. CFs from other vendors may +* have a diffrent partitioning. */ +#define NO_OF_CYLINDERS 490 +#define NO_OF_HEADS 4 +#define NO_OF_SECTORS 32 +#define NO_OF_BYTES_PER_SECTOR 512 +#define MAX_SECTORS_PER_ACCESS 256 + +//All registers are 8bit long except the data register which is 16bit long. +#define NO_OF_INTERNAL_IDE_REGISTERS 12 +#define DATA_REGISTER 0 //this is the only 16bit register +#define ERROR_REGISTER 1 +#define FEATURES_REGISTER 2 +#define SECTOR_COUNT_REGISTER 3 +#define SECTOR_NUMBER_REGISTER 4 +#define CYLINDER_LOW_REGISTER 5 +#define CYLINDER_HIGH_REGISTER 6 +#define SDH_REGISTER 7 //"Sector Size, Drive, Head Register" aka "Device/Head Register" +#define STATUS_REGISTER 8 +#define COMMAND_REGISTER 9 +#define ALTERNATE_STATUS_REGISTER 10 +#define DEVICE_CONTROL_REGISTER 11 + +#define READ_MODE TRUE +#define WRITE_MODE FALSE +/*All information in the ide_device struct are specific for one device.*/ +typedef struct ide_device +{ + int device[NO_OF_CYLINDERS][NO_OF_HEADS][NO_OF_SECTORS][NO_OF_BYTES_PER_SECTOR], + registers[NO_OF_INTERNAL_IDE_REGISTERS], + buffer[NO_OF_BYTES_PER_SECTOR], + current_cylinder, + current_head, + current_sector, + no_sectors_to_access, + sector_count, + pio_datain_in_progress, + pio_dataout_in_progress, + no_of_bytes_transfered, + buffer_filled, + extended_error_code; +} ide_device; + +ide_device gbl$device0; + + +void writeRegister(unsigned int address, unsigned int value); +void writeDataRegister(unsigned int value); +unsigned int readRegister(unsigned int address); +unsigned int readDataRegister(); + +void executeCommand(); +void handleReadWriteSectors(int isReadMode, int isVerifyOnly); +void verifyReadSectors(); +void prepareNextSectorForPIO(int isReadMode, int isVerifyOnly); + + + + +/* Just writes the value to the register. Ensures register size (8 or 16 bits) + * and validity of array position. */ +void writeRegister(unsigned int address, unsigned int value) +{ + if (address == DATA_REGISTER) /*Data register is 16 bits long */ + value &= 0xffff; + else /*All other registers are 8 bits long */ + value &= 0xff; + + if (address < NO_OF_INTERNAL_IDE_REGISTERS) { + gbl$device0.registers[address] = value; + } else { + printf("writeRegister: Illegal address during write: %x!\n Internal register array out of bound.\n", + address); + exit(-1); + } +} + + +/* Just reads the value from the register. Ensures register size (8 or 16 bits) + * and validity of array position. */ +unsigned int readRegister(unsigned int address) +{ + unsigned int returnValue; + if (address < NO_OF_INTERNAL_IDE_REGISTERS) { + returnValue = gbl$device0.registers[address]; + } else { + printf("readRegister: Illegal address during read: %x!\n Internal register array out of bound.\n", + address); + exit(-1); + } + //is paranoia epidemic? + if (address == DATA_REGISTER) /*Data register is 16 bits long */ + returnValue &= 0xffff; + else /*All other registers are 8 bits long */ + returnValue &= 0xff; + + return returnValue; +} + + +/* Selects the correct register to write to from the hosts I/O register address + * The result of writing to the + * Command register while the BSY bit is equal to one or the DRQ bit is equal to one is unpredictable and may + * result in data corruption. Writes to other command block registers are ignored by the device when BSY is set + * except for writing the reset flag to the Device Control Register, which immediately results in a software reset. + */ +void writeIDEDeviceRegister(unsigned int address, unsigned int value) +{ + /*write access is ignored except for the Device Control Register when BSY is set + (although this can never happen in non multithreaded simulation) */ + if ( (!(readRegister(STATUS_REGISTER) & 0x80)) || address == 8) { +#ifdef DEBUG + printf("writeIDEDeviceRegister: write to register '%X' with value '%X' requested.\n", address, value); +#endif + switch(address) + { + case 0: + writeDataRegister(value); + break; + case 1: + writeRegister(FEATURES_REGISTER,value); + break; + case 2: + writeRegister(SECTOR_COUNT_REGISTER,value); + break; + case 3: + writeRegister(SECTOR_NUMBER_REGISTER,value); + break; + case 4: + writeRegister(CYLINDER_LOW_REGISTER,value); + break; + case 5: + writeRegister(CYLINDER_HIGH_REGISTER,value); + break; + case 6: + writeRegister(SDH_REGISTER,value | 0xA0); // bit 5 and 7 must always be set + break; + case 7: + writeRegister(COMMAND_REGISTER,value); + executeCommand (); + break; + case 8: + writeRegister(DEVICE_CONTROL_REGISTER,value); + //software reset immediately results after writing reset flag to the Device Control Register + if (readRegister(DEVICE_CONTROL_REGISTER) & 0x04) { + initializeIDEDevice(); + } + break; + default: + printf("writeIDEDeviceRegister: Illegal register access in write mode at address: %x!\n", address); + exit(-1); + } + } else { +#ifdef DEBUG + printf("writeIDEDeviceRegister: write to register '%X' with value '%X' ignored because drive is busy.\n", + address, value); +#endif + } +} + +/* Selects the correct register to read from according to the hosts I/O register read */ +unsigned int readIDEDeviceRegister(unsigned int address) { + unsigned int returnCode; +#ifdef DEBUG + printf("readIDEDeviceRegister: read of register '%X' requested.\n", address); +#endif + switch(address) + { + case 0: + returnCode = readDataRegister(); + break; + case 1: + returnCode = readRegister(ERROR_REGISTER); + break; + case 2: + returnCode = readRegister(SECTOR_COUNT_REGISTER); + break; + case 3: + returnCode = readRegister(SECTOR_NUMBER_REGISTER); + break; + case 4: + returnCode = readRegister(CYLINDER_LOW_REGISTER); + break; + case 5: + returnCode = readRegister(CYLINDER_HIGH_REGISTER); + break; + case 6: + returnCode = readRegister(SDH_REGISTER); + break; + case 7: + returnCode = readRegister(STATUS_REGISTER); + break; + case 8: + returnCode = readRegister(ALTERNATE_STATUS_REGISTER); + break; + default: + printf("readIDEDeviceRegister: Illegal register access in read mode at address: %x!\n", address); + exit(-1); + } +#ifdef DEBUG + printf("readIDEDeviceRegister: read from register '%X' returns value '%X'.\n", address, returnCode); +#endif + return returnCode; +} + +/* Executes the command written to the COMMAND_REGISTER */ +void executeCommand () +{ +#ifdef DEBUG + printf("Execution of command '%X' requested.\n",readRegister(COMMAND_REGISTER)); +#endif + + //each command sets the drive status to busy as long as DRQ is not set + if (!(readRegister(STATUS_REGISTER) & 0x08)) + { + //Setting BSY (Bit 7) - No other bits in this register are valid when this bit is set. + writeRegister(STATUS_REGISTER, readRegister(STATUS_REGISTER) | 0x80); + //Setting BSY(Bit 7) - No other bits in this register are valid when this bit is set. + writeRegister(ALTERNATE_STATUS_REGISTER, readRegister(ALTERNATE_STATUS_REGISTER) | 0x80); + } + + switch(readRegister(COMMAND_REGISTER)) + { + case 0x03: + /* Request sense - cf specific command ... but maybe helpful. + * Request sense is not part of the ATA-3 specification and is not supported by IDE hard disks. + * It is part the CFA Feature Set //(CFA = The CompactFlash Association that created the + * specification for compact flash memory that uses the ATA interface) that was first mentioned + * and integrated in the ATA-4 specification. It returns an extended error code for the last + * issued command in the error register. Request sense can be called multiple times and will + * always return the extended error code of the last issued command that was not request sense + * command itself */ + + //Setting the extended error code + writeRegister(ERROR_REGISTER, gbl$device0.extended_error_code); + //Setting DRDY and DSC and clearing BSY + writeRegister(STATUS_REGISTER, 0x50); + writeRegister(ALTERNATE_STATUS_REGISTER, 0x50); + break; + case 0x20: //Read Sector(s) with retries + //Executed the same way as 0x21, because we wan't have any trouble with invalid sectors :-) + handleReadWriteSectors(READ_MODE, FALSE); + break; + case 0x21: //Read Sector(s) without retries + handleReadWriteSectors(READ_MODE, FALSE); + break; + case 0x30: //Write Sector(s) with retries + //Executed the same way as 0x31, because we wan't have any trouble with invalid sectors :-) + handleReadWriteSectors(WRITE_MODE, FALSE); + break; + case 0x31: //Write Sector(s) without retries + handleReadWriteSectors(WRITE_MODE, FALSE); + break; + case 0x40: //Read Verify Sector(s) with retries + //Executed the same way as 0x41, because we wan't have any trouble with invalid sectors :-) + handleReadWriteSectors(READ_MODE, TRUE); + break; + case 0x41: //Read Verify Sector(s) without retries + /* This command is identical to the Read Sectors command, except that DRQ is never set and no + * data is transferred to the host. See method verifySectors for more information. + */ + handleReadWriteSectors(READ_MODE, TRUE); + break; + case 0x3C: //Write Verify + /* This command is similar to the Write Sector(s) command, except each sector is verified + * immediately after being written. This command has the same protocol as the Write Sector(s) + * command. So we can execute is the same way as 0x31 because write errors can't happen in simulation. + */ + handleReadWriteSectors(WRITE_MODE, FALSE); + break; + default: +#ifdef DEBUG + printf("executeCommand: Unknown or unimplemented command: %x!\n", readRegister(COMMAND_REGISTER)); +#endif + //Setting ABRT (Command aborted) + writeRegister(ERROR_REGISTER, 0x04); + //Extended error code: invalid command + gbl$device0.extended_error_code=0x20; + //Setting DRDY, DSC and ERR and clearing BSY + writeRegister(STATUS_REGISTER, 0x51); + writeRegister(ALTERNATE_STATUS_REGISTER, 0x51); + } +} + + +void handleReadWriteSectors(int isReadMode, int isVerifyOnly) { + int error = FALSE; + /* reset error register - it still may contain extended error codes or flags from + last command execution. */ + writeRegister(ERROR_REGISTER, 0x00); + + /*check addressing mode - only CHS is supported + We have to exit here because a controller should support both modes and for this reason + no error code or behavior is defined + */ + if ((readRegister(SDH_REGISTER) & 0x40)) { + //LBA (bit 6) is set. + printf("LBA addressing is currently not supported!\n"); + exit(-1); + } + + //begin determine parameters + + //determine starting cylinder + gbl$device0.current_cylinder = + (readRegister(CYLINDER_HIGH_REGISTER)<<8) + | readRegister(CYLINDER_LOW_REGISTER); + + //determine starting head - lower 4 bits of SDH Register + gbl$device0.current_head = readRegister(SDH_REGISTER) & 0xf; + + //determine starting sector + gbl$device0.current_sector = readRegister(SECTOR_NUMBER_REGISTER); + + //determine numbers of sectors to read or write + gbl$device0.no_sectors_to_access = readRegister(SECTOR_COUNT_REGISTER); + //Zero in sector count means maximum block read or write + if (gbl$device0.no_sectors_to_access == 0) + gbl$device0.no_sectors_to_access = MAX_SECTORS_PER_ACCESS; + //end determine parameterss +#ifdef DEBUG + printf("handleReadWriteSectors: Parameters found:\nCylinder: %i\nHead: %i\nSector: %i\n", + gbl$device0.current_cylinder, gbl$device0.current_head, gbl$device0.current_sector); +#endif + //check if parameters are valid + if ( gbl$device0.current_cylinder == 0 || gbl$device0.current_cylinder > NO_OF_CYLINDERS || + gbl$device0.current_head == 0 || gbl$device0.current_head > NO_OF_HEADS || + gbl$device0.current_sector == 0 || gbl$device0.current_sector > NO_OF_SECTORS) { +#ifdef DEBUG + printf("handleReadWriteSectors: Invalid parameter(s):\nCylinder: %x\nHead: %x\nSector: %x\n", + gbl$device0.current_cylinder, gbl$device0.current_head, gbl$device0.current_sector); +#endif + //Setting ABRT (Command aborted) + writeRegister(ERROR_REGISTER, 0x04); + //Extended error code: invalid address + gbl$device0.extended_error_code=0x21; + //Setting DRDY, DSC and ERR and clearing BSY + writeRegister(STATUS_REGISTER, 0x51); + writeRegister(ALTERNATE_STATUS_REGISTER, 0x51); + error = TRUE; + } + + if (! error) { + if (isReadMode) { + if (isVerifyOnly) { + verifyReadSectors(); + } else { + gbl$device0.pio_datain_in_progress=TRUE; + gbl$device0.pio_dataout_in_progress=FALSE; + gbl$device0.sector_count=0; + prepareNextSectorForPIO(READ_MODE,FALSE); + } + + } else { + gbl$device0.pio_datain_in_progress=FALSE; + gbl$device0.pio_dataout_in_progress=TRUE; + gbl$device0.sector_count=0; + prepareNextSectorForPIO(WRITE_MODE,FALSE); + } + } +} + +/*Prepares the next sector for pio if there is one or finshes command excecution*/ +void prepareNextSectorForPIO(int isReadMode, int isVerifyOnly) { + if (gbl$device0.sector_count 0) { + //check if increasing the sector would cross a head boundary + if ((gbl$device0.current_sector + 1) > NO_OF_SECTORS){ + //setting sector to first sector of the new head + gbl$device0.current_sector = 1; + //check if increasing the head would cross a cylinder boundary + if ((gbl$device0.current_head + 1) > NO_OF_HEADS){ + //go to the first head of the next cylinder + gbl$device0.current_head = 1; + gbl$device0.current_cylinder++; + //if going to the next cylinder reaches end of disk read must be aborted! + if (gbl$device0.current_cylinder > NO_OF_CYLINDERS) { +#ifdef DEBUG + printf("prepareNextSectorForPIO: Invalid address:\nCylinder: %x\nHead: %x\nSector: %x\n", + gbl$device0.current_cylinder, gbl$device0.current_head, + gbl$device0.current_sector); +#endif + //Setting ABRT (Command aborted) + writeRegister(ERROR_REGISTER, 0x04); + //in this case CHS-Registers should contain the address the access error occured at + writeRegister(SDH_REGISTER, (gbl$device0.current_head & 0xf) | + (readRegister(SDH_REGISTER) & 0xf0)); + writeRegister(SECTOR_NUMBER_REGISTER, gbl$device0.current_sector & 0xff); + writeRegister(CYLINDER_HIGH_REGISTER, (gbl$device0.current_cylinder >> 8) & 0xff); + writeRegister(CYLINDER_LOW_REGISTER, gbl$device0.current_cylinder & 0x00ff); + //Extended error code: invalid address + gbl$device0.extended_error_code=0x21; + //Setting DRDY, DSC and ERR and clearing BSY + writeRegister(STATUS_REGISTER, 0x51); + writeRegister(ALTERNATE_STATUS_REGISTER, 0x51); + error = TRUE; + } + } else { + gbl$device0.current_head++; + } + } else { + gbl$device0.current_sector++; + } + } //end of increasing sector + + if (! error) { + if (isReadMode) { + //fill read buffer with next sector + int i; + for (i=0; i> 8) & 0xff); + writeRegister(CYLINDER_LOW_REGISTER, gbl$device0.current_cylinder & 0x00ff); + writeRegister(STATUS_REGISTER, 0x50); + writeRegister(ALTERNATE_STATUS_REGISTER, 0x50); + } +} + + +unsigned int readDataRegister(){ + unsigned int returnCode; + if (! gbl$device0.pio_datain_in_progress) { + returnCode = readRegister(DATA_REGISTER); + } else { + //expecting 256 reads in a row. + if (gbl$device0.no_of_bytes_transfered < NO_OF_BYTES_PER_SECTOR && gbl$device0.buffer_filled) { + //write bytes to the Data Register before read. + writeRegister(DATA_REGISTER, (gbl$device0.buffer[gbl$device0.no_of_bytes_transfered+1]<<8) + | gbl$device0.buffer[gbl$device0.no_of_bytes_transfered]); + returnCode = readRegister(DATA_REGISTER); + //prepare for next read + gbl$device0.no_of_bytes_transfered += 2; +#ifdef DEBUG + printf("readDataRegister: Bytes transfered: %i\n", gbl$device0.no_of_bytes_transfered); +#endif + //check if last two bytes of the sector were transfered + if (gbl$device0.no_of_bytes_transfered==NO_OF_BYTES_PER_SECTOR) { + //prepare next sector + gbl$device0.sector_count+=1; + prepareNextSectorForPIO(READ_MODE, FALSE); + } + } else { + //should never happen + printf("readDataRegister: unexpected branch!\n"); + exit(-1); + } + } + return returnCode; +} + +void writeDataRegister(unsigned int value){ + if (! gbl$device0.pio_dataout_in_progress) { + writeRegister(DATA_REGISTER,value); + } else { + //expecting 256 writes in a row. + if (gbl$device0.no_of_bytes_transfered < NO_OF_BYTES_PER_SECTOR) { + //write bytes to the buffer + gbl$device0.buffer[gbl$device0.no_of_bytes_transfered]=value & 0xff; + gbl$device0.buffer[gbl$device0.no_of_bytes_transfered+1]= (value >> 8) & 0xff; + //prepare for next write + gbl$device0.no_of_bytes_transfered += 2; +#ifdef DEBUG + printf("writeDataRegister: Bytes transfered: %i\n", gbl$device0.no_of_bytes_transfered); +#endif + //check if last two bytes of the sector were transfered + if (gbl$device0.no_of_bytes_transfered==NO_OF_BYTES_PER_SECTOR) { + //write buffer to sector + int i; + for (i=0; i 0) ; + + /* If an error occured the number of unverified sectors have to be written to the Sector Count Register + * All other register have been filled correctly by error handling of the prepareNextSectorForPIO method. + * If no error occured all registers and the BSY flag have been set correctly by the prepareNextSectorForPIO method. + */ + if ((readRegister(STATUS_REGISTER) & 0x01) != 0) { + int unverifiedSectors = gbl$device0.no_sectors_to_access - gbl$device0.sector_count; +#ifdef DEBUG + printf("verifySectors: An access error has been detected during verify sectors at sector: %i\n%i sectors remain unverified:\n", + gbl$device0.sector_count, unverifiedSectors); +#endif + + //transform 256 unverified sectors to zero + if (unverifiedSectors==MAX_SECTORS_PER_ACCESS) + unverifiedSectors=0x00; + + writeRegister(SECTOR_COUNT_REGISTER, unverifiedSectors & 0xff); + } + +} + +/* +* For testing purposes only - will be removed at end of development +* Test to read two sectors from the device and check Status Register conditions*/ +void testMe() { + //Test write verify + writeIDEDeviceRegister(6,0xA2); + writeIDEDeviceRegister(5,0x00); + writeIDEDeviceRegister(4,0x80); + writeIDEDeviceRegister(3,0x02); + writeIDEDeviceRegister(2,0x02); + writeIDEDeviceRegister(7,0x41); + if (readIDEDeviceRegister(7) == 0x50) { + printf("\nRead Verify Command successful completed!\n"); + } + + + int i; + // writes two sectors starting from Cylinder:128, Head:2, Sector:2 + writeIDEDeviceRegister(6,0xA2); + writeIDEDeviceRegister(5,0x00); + writeIDEDeviceRegister(4,0x80); + writeIDEDeviceRegister(3,0x02); + writeIDEDeviceRegister(2,0x02); + writeIDEDeviceRegister(7,0x31); + + + if ((readIDEDeviceRegister(7)& 0x08) > 0) { + printf("Ready for write!\n"); + writeIDEDeviceRegister(0,('e' << 8) | 'T'); + writeIDEDeviceRegister(0,('t' << 8) | 's'); + for (i=0;i<254;i++){ + writeIDEDeviceRegister(0,0x0000); + } + if ((readIDEDeviceRegister(7)& 0x08) > 0) + printf("Still ready for write!\n"); + writeIDEDeviceRegister(0,('e' << 8) | 'M'); + int i; + for (i=0;i<255;i++){ + writeIDEDeviceRegister(0,0x0000); + } + } + + + + // reads two sectors starting from Cylinder:128, Head:2, Sector:2 + + writeIDEDeviceRegister(6,0xA2); + writeIDEDeviceRegister(5,0x00); + writeIDEDeviceRegister(4,0x80); + writeIDEDeviceRegister(3,0x02); + writeIDEDeviceRegister(2,0x02); + writeIDEDeviceRegister(7,0x21); + + if ((readIDEDeviceRegister(7)& 0x08) > 0) { + printf("Ready for read!\n"); + int sec[512], sec2[512]; + for (i=0; i<256; i++) { + int x = readIDEDeviceRegister(0); + sec [i*2] = x & 0xff; + sec [(i * 2) + 1] = (x >> 8) & 0xff; + } + for (i=0; i<256; i++) { + int x = readIDEDeviceRegister(0); + sec2[i*2] = x & 0xff; + sec2[(i * 2) + 1] = (x >> 8) & 0xff; + } + if (readIDEDeviceRegister(7) == 0x50) { + printf("\nRead Command successful completed!\n"); + printf("Read from IDE-Device: %c%c%c%c %c%c\n",sec[0],sec[1],sec[2],sec[3],sec2[0],sec[1]); + printf("Test %i and %i should be 0\n",sec[4],sec[511]); + } else { + printf("Read Command successful completed!\n"); + } + } else { + printf("Something is wrong!\n"); + } +} + diff --git a/emulator/ide_simulation.h b/emulator/ide_simulation.h new file mode 100755 index 00000000..776f9f05 --- /dev/null +++ b/emulator/ide_simulation.h @@ -0,0 +1,13 @@ +#ifndef TRUE +# define TRUE 1 +# define FALSE !TRUE +#endif + + +void writeIDEDeviceRegister(unsigned int address, unsigned int value); +unsigned int readIDEDeviceRegister(unsigned int address); +void initializeIDEDevice(); + +//For testing purposes only - will be removed at end of development +void testMe(); + diff --git a/emulator/make.bash b/emulator/make.bash new file mode 100755 index 00000000..8221f50d --- /dev/null +++ b/emulator/make.bash @@ -0,0 +1,2 @@ +#!/bin/bash +cc qnice.c ide_simulation.c uart_2681.c -o qnice diff --git a/emulator/qnice.c b/emulator/qnice.c new file mode 100644 index 00000000..1e27947e --- /dev/null +++ b/emulator/qnice.c @@ -0,0 +1,976 @@ +/* +** QNICE emulator -- this emulator was written as a proof of concept for the +** QNICE processor. In most cases Thomas' Perl based emulator will be used. :-) +** +** B. Ulmann, 16-AUG-2006...03-SEP-2006...04-NOV-2006...29-JUN-2007... +** 16-DEC-2007...03-JUN-2008...28-DEC-2014... +** xx-AUG-2015 +** +** Known bugs: +** +** 1) Executing the simple SUM.BIN yields to a statistics display with three more memory reads than expected. +** +*/ + +#define USE_UART + +#include +#include +#include +#include +#include "ide_simulation.h" + +#ifdef USE_UART +#include "uart_2681.h" + +unsigned int uart_read_register(uart_2681 *, int); +void uart_write_register(uart_2681 *, unsigned int, unsigned int); +void uart_hardware_initialization(uart_2681 *); +void uart_run_down(); +#endif + +/* +** Some preliminaries... +*/ + +#ifndef NULL +# define NULL 0 +#endif + +#ifndef TRUE +# define TRUE 1 +# define FALSE !TRUE +#endif + +#define STRING_LENGTH 132 +#define MEMORY_SIZE 65536 +#define REGMEM_SIZE 4096 + +/* The top most 1 kW of memory is reserverd for memory mapped IO devices */ +#define IO_AREA_START 0xfc00 +#define UART0_BASE_ADDRESS 0xfc00 +#define IDE_BASE_ADDRESS 0xfc10 + +#define NO_OF_INSTRUCTIONS 19 +#define NO_OF_ADDRESSING_MODES 4 +#define READ_MEMORY 0 /* This and the following constants are used to control the access_xxx functions */ +#define WRITE_MEMORY 1 + +/* The following constants form a bit mask to allow the exclusion of several bits */ +#define MODIFY_ALL 0x0 +#define DO_NOT_MODIFY_CARRY 0x1 +#define DO_NOT_MODIFY_X 0x2 +#define DO_NOT_MODIFY_OVERFLOW 0x4 + +#define GENERIC_BRANCH_OPCODE 0xf /* All branches share this common opcode */ + +typedef struct statistic_data +{ + unsigned int instruction_frequency[NO_OF_INSTRUCTIONS], /* Count the number of executions per instruction */ + addressing_modes[2][NO_OF_ADDRESSING_MODES], /* 0 -> read, 1 -> write */ + memory_accesses[2]; /* 0 -> read, 1 -> write */ +} statistic_data; + +int gbl$memory[MEMORY_SIZE], gbl$registers[REGMEM_SIZE], gbl$debug = FALSE, gbl$verbose = FALSE, + gbl$normal_operands[] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}, gbl$gather_statistics = FALSE, gbl$enable_uart = TRUE, + gbl$ctrl_c = FALSE; +char *gbl$normal_mnemonics[] = {"MOVE", "ADD", "ADDC", "SUB", "SUBC", "SHL", "SHR", "SWAP", + "NOT", "AND", "OR", "XOR", "CMP", "", "HALT"}, + *gbl$branch_mnemonics[] = {"ABRA", "ASUB", "RBRA", "RSUB"}, + *gbl$sr_bits = "1XCZNVIM", + *gbl$addressing_mnemonics[] = {"rx", "@rx", "@rx++", "@--rx"}; +statistic_data gbl$stat; + +#ifdef USE_UART +uart_2681 gbl$first_uart; +#endif + +/* +** +*/ +static void signal_handler_ctrl_c(int signo) +{ + gbl$ctrl_c = TRUE; +} + +/* +** upstr converts a string into upper case. +*/ +void upstr(char *string) +{ + while (*string) + { + if (*string >= 'a' && *string <= 'z') + *string += -'a' + 'A'; + string++; + } +} + +/* +** char_in returns TRUE if the character char is an element of string. +*/ +int char_in(char c, char *string) +{ + int i; + + for (i = 0; i < strlen(string); i++) + if (c == string[i]) + return TRUE; + + return FALSE; +} + +/* +** Local variant of strtok, just better. :-) The first call expects the string to be tokenized as its first argument. +** All subsequent calls only require the second argument to be set. If there is nothing left to be tokenized, a zero pointer +** will be returned. In contrast to strtok this routine will not alter the string to be tokenized since it +** operates on a local copy of this string. +*/ +char *tokenize(char *string, char *delimiters) +{ + static char local_copy[STRING_LENGTH], *position; + char *token; + + if (string) /* Initial call, create a copy of the string pointer */ + { + strcpy(local_copy, string); + position = local_copy; + } + else /* Subsequent call, scan local copy until a delimiter character will be found */ + { + while (*position && char_in(*position, delimiters)) /* Skip delimiters if there are any at the beginning of the string */ + position++; + + token = position; /* Now we are at the beginning of a token (or the end of the string :-) ) */ + while (*position) + { + position++; + if (!*position || char_in(*position, delimiters)) /* Delimiter found */ + { + if (*position) + *position++ = (char) 0; /* Split string copy */ + return token; + } + } + } + + return NULL; +} + +/* +** str2int converts a string in base 16 or base 10 notation to an unsigned integer value. +** Base 16 values require a prefix "0x" or "$" while base 10 value do not require any prefix. +*/ +unsigned int str2int(char *string) +{ + int value; + + if (!string || !*string) /* An empty string is treated as a zero */ + return 0; + + if (!strncmp(string, "0X", 2)) + sscanf(string + 2, "%x", &value); + else if (*string == '$') + sscanf(string + 1, "%x", &value); + else + sscanf(string, "%d", &value); + + return value; +} + +/* +** Does exactly what is expected. :-) +*/ +void chomp(char *string) +{ + if (string[strlen(string) - 1] == '\n') + string[strlen(string) - 1] = (char) 0; +} + +/* +** Return the content of a register addressed by its 4 bit register address. The routine takes care of the +** necessary bank switching logic. +*/ +unsigned int read_register(unsigned int address) +{ + address &= 0xf; + if (address & 0x8) /* Upper half -> always bank 0 */ + return gbl$registers[address] | (address == 0xe ? 1 : 0); /* The LSB of SR is always 1! */ + + return gbl$registers[address | ((read_register(14) >> 4) & 0xFF0)]; +} + +/* +** Change the contents of a register with provision for bank switching logic. +*/ +void write_register(unsigned int address, unsigned int value) +{ + address &= 0xf; + value &= 0xffff; + + if ((gbl$debug)) + printf("\twrite_register: address = %04X, value = %02X\n\r", address, value); + + if (address & 0x8) + gbl$registers[address] = value | (address == 14 ? 1 : 0); /* Ensure that LSB will always be set. */ + else /* Take bank switching into account! */ + gbl$registers[address | ((read_register(14) >> 4) & 0xFF0)] = value; +} + +/* +** The following function performs all memory access operations necessary for executing code in the +** emulator. Support routines like dump, etc. may access memory directly, but in this case be aware +** of the fact that no IO device emulation will take place! +** +*/ +unsigned int access_memory(unsigned int address, unsigned int operation, unsigned int value) +{ + address &= 0xffff; + if (gbl$gather_statistics) + gbl$stat.memory_accesses[operation]++; + + if (operation == READ_MEMORY) + { + if (address < IO_AREA_START) + value = gbl$memory[address]; + else /* IO area */ + { + /* TODO: Implement emulation of IO devices here! */ + value = 0; + if ((gbl$debug)) + printf("\tread_memory: IO-area access at 0x%04X: 0x%04X\n\r", address, value); + +#ifdef USE_UART + if (address >= UART0_BASE_ADDRESS && address < UART0_BASE_ADDRESS + 8 && gbl$enable_uart) /* Some UART0 operation */ + value = uart_read_register(&gbl$first_uart, address - UART0_BASE_ADDRESS); +#endif + + if (address >= IDE_BASE_ADDRESS && address < IDE_BASE_ADDRESS + 16) /* Some IDE operation */ + value = readIDEDeviceRegister(address - IDE_BASE_ADDRESS); + } + } + else if (operation == WRITE_MEMORY) + { + if (address < IO_AREA_START) + gbl$memory[address] = value; + else /* IO area */ + { + /* TODO: Implement IO-devices! */ + if ((gbl$debug)) + printf("\twrite_memory: IO-area access at 0x%04X: 0x%04X\n\r", address, value); + +#ifdef USE_UART + if (address >= UART0_BASE_ADDRESS && address < UART0_BASE_ADDRESS + 8 && gbl$enable_uart) /* Some UART0 operation */ + { + if ((gbl$debug)) + printf("\twrite uart register: %04X, %02X\n\t", address, value & 0xff); + uart_write_register(&gbl$first_uart, address - UART0_BASE_ADDRESS, value & 0xff); + } + else +#endif + + if (address >= IDE_BASE_ADDRESS && address < IDE_BASE_ADDRESS + 16) /* Some IDE operation */ + writeIDEDeviceRegister(address - IDE_BASE_ADDRESS, value); + } + } + else + { + printf("Illegal operation code in access_memory!\n"); + exit(-1); + } + + return value; +} + +/* +** reset the processor state, registers, memory. +*/ +void reset_machine() +{ + unsigned int i; + + /* Reset main memory and registers */ + for (i = 0; i < IO_AREA_START; access_memory(i++, WRITE_MEMORY, 0)); + for (i = 0; i < REGMEM_SIZE; gbl$registers[i++] = 0); + + /* Reset statistics counters */ + for (i = 0; i < NO_OF_INSTRUCTIONS; gbl$stat.instruction_frequency[i++] = 0); + for (i = 0; i < NO_OF_ADDRESSING_MODES; i++) + gbl$stat.addressing_modes[0][i] = gbl$stat.addressing_modes[1][i] = 0; + gbl$stat.memory_accesses[0] = gbl$stat.memory_accesses[1] = 0; + + if (gbl$debug || gbl$verbose) + printf("\treset_machine: done\n"); +} + +/* +** Decode an operand specified by a 6 bit mask. Returns TRUE if the next word will be a constant, so this can +** be skipped in the next disassemble step. +*/ +int decode_operand(unsigned int operand, char *string) +{ + int mode, regaddr; + + mode = operand & 0x3; + regaddr = (operand >> 2) & 0xf; + *string = (char) 0; + + if (!mode) + { + sprintf(string, "R%02d", regaddr); + return FALSE; + } + + if (mode == 1) /* @Rxx */ + sprintf(string, "@R%02d", regaddr); + else if (mode == 2) + { + sprintf(string, "@R%02d++", regaddr); + if (regaddr == 0xf) /* PC relative addressing */ + return TRUE; + } + else /* mode == 3 */ + sprintf(string, "@--R%02d", regaddr); + + return FALSE; +} + +/* +** Disassemble the contents of a memory region +*/ +void disassemble(unsigned int start, unsigned int stop) +{ + unsigned int i, opcode, instruction, j; + int skip_addresses; + char scratch[STRING_LENGTH], operands[STRING_LENGTH], mnemonic[STRING_LENGTH]; + + printf("Disassembled contents of memory locations %04x - %04x:\n", start, stop); + for (i = start, skip_addresses = 0; i <= stop || skip_addresses; i++) + { + opcode = (instruction = access_memory(i, READ_MEMORY, 0) & 0xffff) >> 12; + if (skip_addresses) /* Do not decode this machine word -- since it was used in @R15++! */ + { + skip_addresses--; + printf("%04X: %04X\n", i, instruction); + continue; + } + + *operands = (char) 0; + if (opcode < GENERIC_BRANCH_OPCODE) /* Normal instruction */ + { + if (opcode == 0xd) /* This one is reserved for future use! */ + { + strcpy(mnemonic, "RSRVD"); + *operands = (char) 0; + } + else + { + strcpy(mnemonic, gbl$normal_mnemonics[opcode]); + if (gbl$normal_operands[opcode]) /* At least one operand */ + { + if ((skip_addresses = decode_operand((instruction >> 6) & 0x3f, scratch))) /* Constant used! */ + sprintf(scratch, "0x%04X", access_memory(i + 1, READ_MEMORY, 0)); + strcpy(operands, scratch); + } + + if (gbl$normal_operands[opcode] == 2) /* Decode second operand */ + { + if ((j = decode_operand(instruction & 0x3f, scratch))) + sprintf(scratch, "0x%04X", access_memory(i + skip_addresses + j, READ_MEMORY, 0)); + skip_addresses += j; + strcat(operands, ", "); + strcat(operands, scratch); + } + } + } + else if (opcode == GENERIC_BRANCH_OPCODE) /* Branch or Subroutine call */ + { + strcpy(mnemonic, gbl$branch_mnemonics[(instruction >> 4) & 0x3]); + if ((skip_addresses = decode_operand((instruction >> 6) & 0x3f, scratch))) + sprintf(scratch, "0x%04X", access_memory(i + 1, READ_MEMORY, 0)); + sprintf(operands, "%s, %s%c", scratch, (instruction >> 3) & 1 ? "!" : "", gbl$sr_bits[instruction & 0x7]); + } + else + { + strcpy(mnemonic, "???"); + *operands = (char) 0; + } + + printf("%04X: %04X %-6s\t%s\n", i, instruction, mnemonic, operands); + } +} + +/* +** Read a source operand specified by mode and regaddr. If suppress_increment is set (all instructions with only +** one argument should do this!), the autoincrement will not be executed since this will be the task of +** the operand update step. If this is necessary, mode == 2 can be used as a condition for this. +** Predecrement will be executed always, postincrement only conditionally. +*/ +unsigned int read_source_operand(unsigned int mode, unsigned int regaddr, int suppress_increment) +{ + unsigned int source; + + if (gbl$debug) + printf("\tread_source_operand: mode=%01X, reg=%01X, skip_increment=%d\n\r", mode, regaddr, suppress_increment); + + switch (mode) /* Mode bits of source operand */ + { + case 0: /* Rxx */ + source = read_register(regaddr); + break; + case 1: /* @Rxx */ + source = access_memory(read_register(regaddr), READ_MEMORY, 0); + break; + case 2: /* @Rxx++ */ + source = access_memory(read_register(regaddr), READ_MEMORY, 0); + if (!suppress_increment) + write_register(regaddr, read_register(regaddr) + 1); + break; + case 3: /* @--Rxx */ + write_register(regaddr, read_register(regaddr) - 1); + source = access_memory(read_register(regaddr), READ_MEMORY, 0); + break; + default: + printf("Internal error, fetch operand!\n"); + exit(-1); + } + + if (gbl$gather_statistics) + gbl$stat.addressing_modes[0][mode]++; + + if (gbl$debug) + printf("\tread_source_operand: value=%04X, r15=%04X\n\r", source, read_register(15)); + return source; +} + +/* +** This is the counterpart function to read_source_operand. The major difference (apart from writing instead of reading :-) ) +** is that predecrements can be suppressed, autoincrements will be executed always. +*/ +void write_destination(unsigned int mode, unsigned int regaddr, unsigned int value, int suppress_decrement) +{ + if (gbl$debug) + printf("\twrite_operand: mode=%01X, reg=%01X, value=%04X, skip_increment=%d\n\r", mode, regaddr, value, suppress_decrement); + + value &= 0xffff; + switch (mode) + { + case 0: /* rxx */ + write_register(regaddr, value); + break; + case 1: /* @Rxx */ + access_memory(read_register(regaddr), WRITE_MEMORY, value); + break; + case 2: /* @Rxx++ */ + access_memory(read_register(regaddr), WRITE_MEMORY, value); + write_register(regaddr, read_register(regaddr) + 1); + break; + case 3: /* @--Rxx */ + if (!suppress_decrement) + write_register(regaddr, read_register(regaddr) - 1); + access_memory(read_register(regaddr), WRITE_MEMORY, value); + break; + default: + printf("Internal error, write operand!\n"); + exit(-1); + } + + if (gbl$gather_statistics) + gbl$stat.addressing_modes[1][mode]++; + + if (gbl$debug) + printf("\twrite_destination: r15=%04X\n\r", read_register(15)); +} + +/* +** The following function updates the lower six bits of the status register R14 according to +** the result of some machine instruction. Please keep in mind that the destination (result) +** parameter may occupy 17 bits (including the carry)! Do not truncate this parameter prior +** to calling this routine! +*/ +void update_status_bits(unsigned int destination, unsigned int source_0, unsigned int source_1, unsigned int control_bitmask) +{ + unsigned int sr_bits; + + sr_bits = 1; /* LSB is always set (for unconditional branches and subroutine calls) */ + if (((destination & 0xffff) == 0xffff) & !(control_bitmask & DO_NOT_MODIFY_X)) /* X */ + sr_bits |= 0x2; + if ((destination & 0x10000) && !(control_bitmask & DO_NOT_MODIFY_CARRY)) /* C */ + sr_bits |= 0x4; + if (!(destination & 0xffff)) /* Z */ + sr_bits |= 0x8; + if (destination & 0x8000) /* N */ + sr_bits |= 0x10; + if (((!(source_0 & 0x8000) && !(source_1 & 0x8000) && (destination & 0x8000)) || + ((source_0 & 0x8000) && (source_1 & 0x8000) && !(destination & 0x8000))) && !(control_bitmask & DO_NOT_MODIFY_OVERFLOW)) + sr_bits |= 0x20; + + write_register(14, (read_register(14) & 0xffc0) | (sr_bits & 0x3f)); +} + +/* +** The following function executes a single QNICE instruction. The return value will be TRUE if an illegal instruction is found. +*/ +int execute() +{ + unsigned int instruction, address, opcode, source_mode, source_regaddr, destination_mode, destination_regaddr, + source_0, source_1, destination, scratch, i, debug_address, temp_flag; + int condition; + + debug_address = address = read_register(15); /* Get current PC */ + opcode = ((instruction = access_memory(address++, READ_MEMORY, 0)) >> 12 & 0Xf); + write_register(15, address); /* Update program counter */ + + if (gbl$debug || gbl$verbose) + printf("execute: %04X %04X %s\n\r", debug_address, instruction, + opcode == GENERIC_BRANCH_OPCODE ? gbl$branch_mnemonics[(instruction >> 4) & 0x3] + : gbl$normal_mnemonics[opcode]); + + source_mode = (instruction >> 6) & 0x3; + source_regaddr = (instruction >> 8) & 0xf; + + destination_mode = instruction & 0x3; + destination_regaddr = (instruction >> 2) & 0xf; + + /* Update the statistics counters */ + if (opcode < GENERIC_BRANCH_OPCODE && gbl$gather_statistics) + gbl$stat.instruction_frequency[opcode]++; + else if (opcode == GENERIC_BRANCH_OPCODE && gbl$gather_statistics) + gbl$stat.instruction_frequency[opcode + ((instruction >> 4) & 0x3)]++; + + switch (opcode) + { + case 0: /* MOVE */ + destination = read_source_operand(source_mode, source_regaddr, FALSE); + update_status_bits(destination, destination, destination, DO_NOT_MODIFY_CARRY | DO_NOT_MODIFY_OVERFLOW); + write_destination(destination_mode, destination_regaddr, destination, FALSE); + break; + case 1: /* ADD */ + source_0 = read_source_operand(destination_mode, destination_regaddr, TRUE); + source_1 = read_source_operand(source_mode, source_regaddr, FALSE); + destination = source_0 + source_1; + update_status_bits(destination, source_0, source_1, MODIFY_ALL); + write_destination(destination_mode, destination_regaddr, destination, FALSE); + break; + case 2: /* ADDC */ + source_0 = read_source_operand(destination_mode, destination_regaddr, TRUE); + source_1 = read_source_operand(source_mode, source_regaddr, FALSE); + destination = source_0 + source_1 + ((read_register(14) >> 2) & 1); /* Take carry into account */ + update_status_bits(destination, source_0, source_1, MODIFY_ALL); + write_destination(destination_mode, destination_regaddr, destination, FALSE); + break; + case 3: /* SUB */ + source_0 = read_source_operand(destination_mode, destination_regaddr, TRUE); + source_1 = read_source_operand(source_mode, source_regaddr, FALSE); + destination = source_0 - source_1; + update_status_bits(destination, source_0, source_1, MODIFY_ALL); + write_destination(destination_mode, destination_regaddr, destination, FALSE); + break; + case 4: /* SUBC */ + source_0 = read_source_operand(destination_mode, destination_regaddr, TRUE); + source_1 = read_source_operand(source_mode, source_regaddr, FALSE); + destination = source_0 - source_1 - ((read_register(14) >> 2) & 1); /* Take carry into account */ + update_status_bits(destination, source_0, source_1, MODIFY_ALL); + write_destination(destination_mode, destination_regaddr, destination, FALSE); + break; + case 5: /* SHL */ + source_0 = read_source_operand(source_mode, source_regaddr, FALSE); + destination = read_source_operand(destination_mode, destination_regaddr, TRUE); + for (i = 0; i < source_0; i++) + { + temp_flag = (destination & 0x8000) >> 13; + destination = (destination << 1) | ((read_register(14) >> 1) & 1); /* Fill with X bit */ + } + write_register(14, (read_register(14) & 0xfffb) | temp_flag); /* Shift into C bit */ + write_destination(destination_mode, destination_regaddr, destination, FALSE); + break; + case 6: /* SHR */ + scratch = source_0 = read_source_operand(source_mode, source_regaddr, FALSE); + destination = read_source_operand(destination_mode, destination_regaddr, TRUE); + for (i = 0; i < source_0; i++) + { + temp_flag = (destination & 1) << 1; + destination = ((destination >> 1) & 0xffff) | ((read_register(14) & 4) << 13); /* Fill with C bit */ + } + write_register(14, (read_register(14) & 0xfffd) | temp_flag); /* Shift into X bit */ + write_destination(destination_mode, destination_regaddr, destination, FALSE); + break; + case 7: /* SWAP */ + source_0 = read_source_operand(source_mode, source_regaddr, FALSE); + destination = (source_0 >> 8) | ((source_0 << 8) & 0xff00); + update_status_bits(destination, source_0, source_0, DO_NOT_MODIFY_CARRY | DO_NOT_MODIFY_OVERFLOW); + write_destination(destination_mode, destination_regaddr, destination, FALSE); + break; + case 8: /* NOT */ + source_0 = read_source_operand(source_mode, source_regaddr, FALSE); + destination = ~source_0 & 0xffff; + update_status_bits(destination, source_0, source_0, DO_NOT_MODIFY_CARRY | DO_NOT_MODIFY_OVERFLOW); + write_destination(destination_mode, destination_regaddr, destination, FALSE); + break; + case 9: /* AND */ + source_0 = read_source_operand(destination_mode, destination_regaddr, TRUE); + source_1 = read_source_operand(source_mode, source_regaddr, FALSE); + destination = source_0 & source_1; + update_status_bits(destination, source_0, source_1, DO_NOT_MODIFY_CARRY | DO_NOT_MODIFY_OVERFLOW); + write_destination(destination_mode, destination_regaddr, destination, FALSE); + break; + case 10: /* OR */ + source_0 = read_source_operand(destination_mode, destination_regaddr, TRUE); + source_1 = read_source_operand(source_mode, source_regaddr, FALSE); + destination = source_0 | source_1; + update_status_bits(destination, source_0, source_1, DO_NOT_MODIFY_CARRY | DO_NOT_MODIFY_OVERFLOW); + write_destination(destination_mode, destination_regaddr, destination, FALSE); + break; + case 11: /* XOR */ + source_0 = read_source_operand(destination_mode, destination_regaddr, TRUE); + source_1 = read_source_operand(source_mode, source_regaddr, FALSE); + destination = source_0 ^ source_1; + update_status_bits(destination, source_0, source_1, DO_NOT_MODIFY_CARRY | DO_NOT_MODIFY_OVERFLOW); + write_destination(destination_mode, destination_regaddr, destination, FALSE); + break; + case 12: /* CMP */ + source_0 = read_source_operand(destination_mode, destination_regaddr, FALSE); + source_1 = read_source_operand(source_mode, source_regaddr, FALSE); + destination = source_0 - source_1; + update_status_bits(destination, source_0, source_1, MODIFY_ALL); + break; + case 13: /* This opcode intentionally left blank :-) */ + printf("Trying to execute opcode D -- this is reserved for future use!\n"); + return TRUE; + case 14: /* HALT */ + return TRUE; + case 15: /* Branch or subroutine call */ + /* Determine destination address in case the branch/subroutine instruction will be performed */ + destination = read_source_operand(source_mode, source_regaddr, FALSE); /* Perform autoincrement since no write back occurs! */ + + /* Determine which SR bit to use, etc. */ + condition = (read_register(14) >> (instruction & 0x7)) & 1; + if (instruction & 0x0008) /* Invert bit to be checked? */ + condition = 1 - condition; + + /* Now it is time to determine which branch resp. subroutine call type to execute if the condition is satisfied */ + if (condition) + { + switch((instruction >> 4) & 0x3) + { + case 0: /* ABRA */ + write_register(15, destination); + break; + case 1: /* ASUB */ + write_register(13, read_register(13) - 1); + access_memory(read_register(13), WRITE_MEMORY, read_register(15)); + write_register(15, destination); + break; + case 2: /* RBRA */ + write_register(15, (read_register(15) + destination) & 0xffff); + break; + case 3: /* RSUB */ + write_register(13, read_register(13) - 1); + access_memory(read_register(13), WRITE_MEMORY, read_register(15)); + write_register(15, (read_register(15) + destination) & 0xffff); + break; + } + } + /* We must increment the PC in case of a constant destination address even if the branch is not taken! */ +// NO, we must not since the PC has already been incremented during the fetch operation! +// else if (source_mode == 0x2 && source_regaddr == 0xf) /* This is mode @R15++ */ +// write_register(15, read_register(15) + 1); + + break; + default: + printf("PANIK: Illegal instruction found: Opcode %0X at address %04X.\n", opcode, address); + return TRUE; + } + +/* write_register(15, read_register(15) + 1); */ /* Update program counter */ + return FALSE; /* No HALT instruction executed */ +} + +void run() +{ + gbl$ctrl_c = FALSE; +#ifdef USE_UART + if (gbl$enable_uart) + uart_hardware_initialization(&gbl$first_uart); +#endif + gbl$gather_statistics = TRUE; + while (!execute() && !gbl$ctrl_c); + if (gbl$ctrl_c) + printf("\n\tAborted by CTRL-C!\n"); + gbl$gather_statistics = FALSE; +#ifdef USE_UART + if (gbl$enable_uart) + uart_run_down(); +#endif +} + +void print_statistics() +{ + unsigned int i, value; + + for (i = value = 0; i < NO_OF_INSTRUCTIONS; value += gbl$stat.instruction_frequency[i++]); + if (!value) + printf("No statistics have been gathered so far!\n"); + else + { + printf("\n%d memory reads, %d memory writes and\n%d instructions have been executed so far:\n\n\ +INSTR ABSOLUTE RELATIVE INSTR ABSOLUTE RELATIVE\n\ +-----------------------------------------------\n", + gbl$stat.memory_accesses[READ_MEMORY], gbl$stat.memory_accesses[WRITE_MEMORY], value); + for (i = 0; i < NO_OF_INSTRUCTIONS; i++) + printf("%s%-4s: %8d (%5.2f%%)\t", + !(i & 1) && i ? "\n" : "", /* New line every second round */ + i < GENERIC_BRANCH_OPCODE ? gbl$normal_mnemonics[i] + : gbl$branch_mnemonics[i - GENERIC_BRANCH_OPCODE], + gbl$stat.instruction_frequency[i], + (float) (100 * gbl$stat.instruction_frequency[i]) / (float) value); + + for (i = value = 0; i < NO_OF_ADDRESSING_MODES; i++) + value += gbl$stat.addressing_modes[0][i] + gbl$stat.addressing_modes[1][i]; + if (!value) + printf("\n\nThere have not been any memory references so far!\n"); + else + { + printf("\n\n READ ACCESSES WRITE ACCESSES\n\ +MODE ABSOLUTE RELATIVE MODE ABSOLUTE RELATIVE\n\ +-----------------------------------------------------------\n"); + for (i = 0; i < NO_OF_ADDRESSING_MODES; i++) + printf("%-5s: %8d (%5.2f%%) \t%-5s: %8d (%5.2f%%)\n", + gbl$addressing_mnemonics[i], gbl$stat.addressing_modes[0][i], + (float) (100 * gbl$stat.addressing_modes[0][i]) / (float) value, + gbl$addressing_mnemonics[i], gbl$stat.addressing_modes[1][i], + (float) (100 * gbl$stat.addressing_modes[1][i]) / (float) value); + } + printf("\n"); + } +} + +int load_binary_file(char *file_name) +{ + unsigned int address; + char scratch[STRING_LENGTH], *token; + FILE *handle; + + if (!(handle = fopen(file_name, "r"))) + { + printf("Unable to open file >>%s<<\n", file_name); + return -1; + } + else + { + fgets(scratch, STRING_LENGTH, handle); + upstr(scratch); + chomp(scratch); + while(!feof(handle)) + { + tokenize(scratch, NULL); + if (!(token = tokenize(NULL, " "))) + break; + address = str2int(token); + if (address >= MEMORY_SIZE) + { + printf("Address out of range in load file: >>%s<<\n", scratch); + return -1; + } + + if (!(token = tokenize(NULL, " "))) + { + printf("Illegal line in load file! Line: >>%s<<\n", scratch); + return -1; + } + access_memory(address, WRITE_MEMORY, str2int(token)); + + fgets(scratch, STRING_LENGTH, handle); + upstr(scratch); + chomp(scratch); + } + fclose(handle); + } + + return 0; +} + +void dump_registers() +{ + unsigned int i, value; + + printf("Register dump: BANK = %02x, SR = ", read_register(14) >> 8); + for (i = 7, value = read_register(14); i + 1; i--) + printf("%c", value & (1 << i) ? gbl$sr_bits[i] : '_'); + + printf("\n"); + for (i = 0; i < 0x10; i++) + { + if (!(i % 4)) /* New row */ + printf("\n\tR%02d-R%02d: ", i, i + 3); + + printf("%04x ", read_register(i)); + } + printf("\n\n"); +} + +int main(int argc, char **argv) +{ + char command[STRING_LENGTH], *token, *delimiters = " ,", scratch[STRING_LENGTH]; + unsigned int start, stop, i, address, value, last_command_was_step = 0; + FILE *handle; + + signal(SIGINT, signal_handler_ctrl_c); + reset_machine(); + initializeIDEDevice(); + + if (argc > 1) + { + if (!strcmp(argv[1], "-h")) + printf("\nUsage:\n\ + \"qnice\" without arguments will start an interactive session\n\ + \"qnice -h\" will print this help text\n\ + \"qnice \" will run in batch mode and print statistics\n\n"); + else /* Assume that the first argument is a file name */ + { + if (load_binary_file(argv[1])) + return -1; + + run(); + dump_registers(); + print_statistics(); + } + + return 0; + } + + for (;;) + { + printf("Q> "); + fgets(command, STRING_LENGTH, stdin); + chomp(command); + if (feof(stdin)) + return 0; + + if (last_command_was_step && !strlen(command)) /* If STEP was the last command and this is empty, perform the next step. */ + strcpy(command, "STEP"); + + upstr(command); + + last_command_was_step = 0; + tokenize(command, NULL); /* Initialize tokenizing */ + if ((token = tokenize(NULL, delimiters))) + { + if (!strcmp(token, "QUIT") || !strcmp(token, "EXIT")) + return 0; + else if (!strcmp(token, "DUMP")) + { + start = str2int(tokenize(NULL, delimiters)); + stop = str2int(tokenize(NULL, delimiters)); + for (i = start; i <= stop; i++) + { + if (!((i - start) % 8)) /* New row */ + printf("\n%04x: ", i); + + printf("%04x ", access_memory(i, READ_MEMORY, 0)); + } + printf("\n"); + } + else if (!strcmp(token, "SAVE")) /* Create a loadable binary file with data from memory */ + { + if (!(token = tokenize(NULL, delimiters))) + printf("SAVE expects at least a filename as its 1st parameter!\n"); + else + { + if (!(handle = fopen(token, "w"))) + printf("Unable to create file >>%s<<\n", token); + else + { + start = str2int(tokenize(NULL, delimiters)); + stop = str2int(tokenize(NULL, delimiters)); + for (i = start; i <= stop; i++) + fprintf(handle, "0x%04X 0x%04X\n", i, access_memory(i, READ_MEMORY, 0)); + + fclose(handle); + } + } + } + else if (!strcmp(token, "LOAD")) /* Load expects a file with a row format like " \n", etc. */ + { + if (!(token = tokenize(NULL, delimiters))) + printf("LOAD expects a filename as its 1st parameter!\n"); + else + load_binary_file(token); + } + else if (!strcmp(token, "RDUMP")) + dump_registers(); + else if (!strcmp(token, "SET")) + { + token = tokenize(NULL, delimiters); + value = str2int(tokenize(NULL, delimiters)); + if (*token == 'R') /* Set a register */ + write_register(str2int(token + 1), value); + else + access_memory(str2int(token), WRITE_MEMORY, value & 0xffff); + } + else if (!strcmp(token, "RESET")) + reset_machine(); + else if (!strcmp(token, "DEBUG")) + { + if ((gbl$debug = TRUE - gbl$debug)) + printf("New mode: verbose\n"); + else + printf("New mode: quiet\n"); + } + else if (!strcmp(token, "VERBOSE")) + { + if ((gbl$verbose = TRUE - gbl$verbose)) + printf("New mode: verbose\n"); + } + else if (!strcmp(token, "UART")) + { + gbl$enable_uart = TRUE - gbl$enable_uart; + printf("New UART-mode: %sabled\n", gbl$enable_uart ? "en" : "dis"); + } + else if (!strcmp(token, "DIS")) + { + start = str2int(tokenize(NULL, delimiters)); + disassemble(start, str2int(tokenize(NULL, delimiters))); + } + else if (!strcmp(token, "STAT")) + print_statistics(); + else if (!strcmp(token, "STEP")) + { + last_command_was_step = 1; + if ((token = tokenize(NULL, delimiters))) + write_register(15, str2int(token)); + execute(); + } + else if (!strcmp(token, "RUN")) + { + if ((token = tokenize(NULL, delimiters))) + write_register(15, str2int(token)); + run(); + } + else if (!strcmp(token, "HELP")) + printf("\n\ +DEBUG Toggle debug mode (for development only)\n\ +DIS , Disassemble a memory region\n\ +DUMP , Dump a memory area, START and STOP can be\n\ + hexadecimal or plain decimal\n\ +LOAD Loads a binary file into main memory\n\ +QUIT/EXIT Stop the emulator and return to the shell\n\ +RESET Reset the whole machine\n\ +RDUMP Print a register dump\n\ +RUN [] Run a program beginning at ADDR\n\ +SET Either set a register of a memory cell\n\ +SAVE Create a loadable binary file\n\ +STAT Displays some execution statistics\n\ +STEP [] Executes a single instruction at address\n\ + ADDR. If not address is specified the current\n\ + program counter will be used instead.\n\ + If the last command was step, an empty command\n\ + string will perform the next step!\n\ +UART Toggle UART disable/enable\n\ +VERBOSE Toggle verbosity mode\n\ +"); + else + printf("main: Unknown command >>%s<<\n", token); + } + } +} diff --git a/emulator/uart_2681.c b/emulator/uart_2681.c new file mode 100644 index 00000000..8faafb2d --- /dev/null +++ b/emulator/uart_2681.c @@ -0,0 +1,236 @@ +/* +** This module implements the emulation of a 2681 UART which will be eventually used in the actual hardware implementation +** of QNICE. +** +** 02-JUN-2008, B. Ulmann fecit. +** 03-AUG-2015, B. Ulmann Changed from curses to select-calls. +*/ + +#undef TEST /* Define to perform stand alone test */ +#define VERBOSE +#define DEBUG + +#include "uart_2681.h" +#include + +#include +#include +#include + +/* Ugly global variable to hold the original tty state in order to restore it during rundown */ +struct termios tty_state_old, tty_state; + +unsigned int uart_read_register(uart_2681 *state, unsigned int address) +{ + unsigned int value; + char last_character; + fd_set fd; + struct timeval tv; + int ret_val; + + /* Initialize data structures for following select calls */ + FD_ZERO(&fd); + FD_SET(STDIN_FILENO, &fd); + tv.tv_sec = 0; + tv.tv_usec = 1000; /* Wait 1 ms */ + + switch (address) + { + case MR1A: + /* case MR2A: */ + value = state->mr1a; + break; + case SRA: + /* Check if there is a character in the input buffer */ + if ((ret_val = select(1, &fd, NULL, NULL, &tv)) == -1) + { + /* Don't stop here as it might be caused by a catched CTRL-C signal! */ + } + else if (!ret_val) /* No data available */ + state->sra &= 0xfe; + else /* Data available */ + state->sra |= 1; + + value = state->sra; + break; + case BRG_TEST: + value = state->brg_test; + break; + case RHRA: + if ((ret_val = select(1, &fd, NULL, NULL, &tv)) == -1) + { + /* Don't stop here as it might be caused by a catched CTRL-C signal! */ + } + else if (!ret_val) /* No data available */ + state->rhra = 0; + else /* Data available */ + state->rhra = getchar() & 0xff; + + value = state->rhra; + break; + case IPCR: + value = state->ipcr; + break; + case ISR: + value = state->isr; + break; + case CTU: + value = state->ctu; + break; + case CTL: + value = state->ctl; + break; + case MR1B: + /* case MR2B: */ + value = state->mr1b; + break; + case SRB: + value = state->srb; + break; + case X_X_TEST: + value = state->x_x_test; + break; + case RHRB: + value = state->rhrb; + break; + case INPUT_PORTS: + value = state->input_ports; + break; + case START_COUNTER: + value = state->start_counter; + break; + case STOP_COUNTER: + value = state->stop_counter; + break; + default: +#ifdef VERBOSE + printf("uart_read_register: attempt to read illegal register %d!\n", address); +#endif + value = 0xff; + } + + return value & 0xff; +} + +void uart_write_register(uart_2681 *state, unsigned int address, unsigned int value) +{ + value &= 0xff; + switch (address) + { + case MR1A: + /* case MR1B: */ + state->mr1a = value; + break; + case CSRA: + state->csra = value; + break; + case CRA: + state->cra = value; + break; + case THRA: + state->thra = value; + putchar((int) value); + fflush(stdout); + break; + case ACR: + state->acr = value; + break; + case IMR: + state->imr = value; + break; + case CRUR: + state->crur = value; + break; + case CTLR: + state->ctlr = value; + break; + case CSRB: + state->csrb = value; + break; + case CRB: + state->crb = value; + break; + case THRB: + state->thrb = value; + break; + case OPCR: + state->opcr = value; + break; + case SET_OUTPUT_PORT: + state->set_output_port = value; + break; + case RESET_OUTPUT_PORT: + state->reset_output_port = value; + break; + default: +#ifdef VERBOSE + printf("uart_write_register: attempt to write into illegal register %d!", address); +#endif + } +} + +void uart_hardware_initialization(uart_2681 *state) +{ + /* Turn off buffering on STDIN and save original state for later */ + tcgetattr(STDIN_FILENO, &tty_state_old); + tty_state = tty_state_old; + tty_state.c_lflag &= ~ICANON; + tty_state.c_lflag &= ~ECHO; + tcsetattr(STDIN_FILENO, TCSANOW, &tty_state); + + /* + ** bit 1, 0: 11 -> 8 bits/character + ** bit 2 : 0 -> even parity + ** bit 4, 3: 10 -> no parity + ** bit 5 : 0 -> error mode char + ** bit 6 : 0 -> RxRDY + ** bit 7 : 0 -> no RxRTS control + */ + state->mr1a = state->mr1b = 0x13; + + state->sra = state->srb = state->brg_test = state->rhra = state->ipcr = state->isr = state->ctu = state->ctl = + state->x_x_test = state->rhrb = state->input_ports = state->start_counter = state->stop_counter = + state->csra = state->cra = state->thra = state->acr = state->imr = state->crur = state->ctlr = state->csrb = state->crb = + state->thrb = state->opcr = state->set_output_port = state->reset_output_port = (unsigned int) 0; +} + +void uart_run_down() +{ + /* Reset the terminal to its original settings */ + tcsetattr(STDIN_FILENO, TCSANOW, &tty_state_old); +} + +/* +** The main function serves test purposes only and will be invisible under normal circumstances. +*/ + +#ifdef TEST +int main() +{ + uart_2681 my_uart; + unsigned int sra, rhra = 0, i; + + /* Initialize UART (hardware reset) */ + uart_hardware_initialization(&my_uart); + + /* First simple test: Write the characters A to Z followed by CR/LF/LF to the first emulated serial line */ + for (i = 'A'; i <= 'Z'; i++) + uart_write_register(&my_uart, THRA, i); + uart_write_register(&my_uart, THRA, 10); + uart_write_register(&my_uart, THRA, 13); + + /* Second simple test: Read characters from the first simulated serial line and echo them, 'q' will quit */ + for (; rhra != 'q';) + { + do + { + sra = uart_read_register(&my_uart, SRA); + } while (!(sra & 1)); + rhra = uart_read_register(&my_uart, RHRA); + uart_write_register(&my_uart, THRA, rhra); + } + + uart_run_down(); + return 0; +} +#endif diff --git a/emulator/uart_2681.h b/emulator/uart_2681.h new file mode 100644 index 00000000..9a81bd06 --- /dev/null +++ b/emulator/uart_2681.h @@ -0,0 +1,48 @@ +/* +** This is the header file for the 2681 UART-emulation for the QNICE-emulator. +** +** 02-JUN-2008, B. Ulmann fecit +*/ + +/* This structure contains all register data of the emulated UART */ +typedef struct uart_2681 +{ + unsigned int mr1a, sra, brg_test, rhra, ipcr, isr, ctu, ctl, mr1b, srb, + x_x_test, rhrb, input_ports, start_counter, stop_counter, + csra, cra, thra, acr, imr, crur, ctlr, csrb, crb, thrb, opcr, + set_output_port, reset_output_port; +} uart_2681; + +/* The following registers can be read from and partially written to the UART */ +#define MR1A 0 /* Read/write */ +#define MR2A 0 /* Read/write */ +#define SRA 1 +#define BRG_TEST 2 +#define RHRA 3 +#define IPCR 4 +#define ISR 5 +#define CTU 6 +#define CTL 7 +#define MR1B 8 /* Read/write */ +#define MR2B 8 /* Read/write */ +#define SRB 9 +#define X_X_TEST 10 +#define RHRB 11 +#define INPUT_PORTS 13 +#define START_COUNTER 14 +#define STOP_COUNTER 15 + +/* The following registers can be written to */ +#define CSRA 1 +#define CRA 2 +#define THRA 3 +#define ACR 4 +#define IMR 5 +#define CRUR 6 +#define CTLR 7 +#define CSRB 9 +#define CRB 10 +#define THRB 11 +#define OPCR 13 +#define SET_OUTPUT_PORT 14 +#define RESET_OUTPUT_PORT 15 diff --git a/monitor/io_library.asm b/monitor/io_library.asm new file mode 100644 index 00000000..b6e0dbcb --- /dev/null +++ b/monitor/io_library.asm @@ -0,0 +1,216 @@ +; +;;======================================================================================= +;; The collection of input/output related function starts here +;;======================================================================================= +; +; Define io system specific constants and memory areas etc. It is expected that the +; basic definitions from sysdef.asm have been included somewhere before! +; +;*************************************************************************************** +;* IO$DUMP_MEMORY prints a hexadecimal memory dump of a specified memory region. +;* +;* R8: Contains the start address +;* R9: Contains the end address (inclusive) +;* +;* The contents of R8 and R9 are preserved during the run of this function. +;*************************************************************************************** +; +IO$DUMP_MEMORY INCRB ; Get a new register page + MOVE R8, R0 ; R0 will be the loop counter + MOVE R8, R1 ; This will be needed to restore R8 later + MOVE R9, R3 + ADD 0x0001, R3 ; That is necessary since we want the last + ; address printed, too + MOVE 0xFFFF, R4 ; Set R4 - this is the column counter - to -1 +_IO$DUMP_MEMORY_LOOP MOVE R0, R2 ; Have we reached the end of the memory area? + SUB R3, R2 + RBRA _IO$DUMP_MEMORY_EXIT, Z ; Yes - that is it, so exit this routine + ADD 0x0001, R4 ; Next column + AND 0x0007, R4 ; We compute mod 8 + RBRA _IO$DUMP_MEMORY_CONTENT, !Z; if the result is not equal 0 we do not + ; need an address printed + RSUB IO$PUT_CRLF, 1 ; Print a CR/LF pair + MOVE R0, R8 ; Print address + RSUB IO$PUT_W_HEX, 1 + MOVE IO$COLON_DELIMITER, R8 ; Print a colon followed by a space + RSUB IO$PUTS, 1 +_IO$DUMP_MEMORY_CONTENT MOVE @R0++, R8 ; Print the memory contents of this location + RSUB IO$PUT_W_HEX, 1 + MOVE ' ', R8 ; Print a space + RSUB IO$PUTCHAR, 1 + RBRA _IO$DUMP_MEMORY_LOOP, 1 ; Continue the print loop +_IO$DUMP_MEMORY_EXIT RSUB IO$PUT_CRLF, 1 ; Print a last CR/LF pair + MOVE R1, R8 ; Restore R8, + DECRB ; switch back to the correct register page + RET +; +;*************************************************************************************** +;* IO$GET_W_HEX reads four hex nibbles from stdin and returns the corresponding +;* value in R8 +;* +;* Illegal characters (not 1-9A-F or a-f) will generate a bell signal. The only +;* exception to this behaviour is the character 'x' which will erase any input +;* up to this point. This has the positive effect that a hexadecimal value can +;* be entered as 0x.... or just as .... +;*************************************************************************************** +; +IO$GET_W_HEX INCRB ; Get a new register page +_IO$GET_W_HEX_REDO XOR R0, R0 ; Clear R0 + MOVE 4, R1 ; We need four characters + MOVE IO$HEX_NIBBLES, R9 ; Pointer to list of valid chars +_IO$GET_W_HEX_INPUT RSUB IO$GETCHAR, 1 ; Read a character into R8 + RBRA QMON$WARMSTART, Z + RSUB CHR$TO_UPPER, 1 ; Convert to upper case + CMP 'X', R8 ; Was it an 'X'? + RBRA _IO$GET_W_HEX_REDO, Z ; Yes - redo from start :-) + RSUB STR$STRCHR, 1 ; Is it a valid character? + MOVE R10, R10 ; Result equal zero? + RBRA _IO$GET_W_HEX_VALID, !Z ; No + MOVE 7, R8 ; Yes - generate a beep :-) + RSUB IO$PUTCHAR, 1 + RBRA _IO$GET_W_HEX_INPUT, 1 ; Retry +_IO$GET_W_HEX_VALID RSUB IO$PUTCHAR, 1 ; Echo character + SUB IO$HEX_NIBBLES, R10 ; Get number of character + SHL 4, R0 + ADD R10, R0 + SUB 0x0001, R1 + RBRA _IO$GET_W_HEX_INPUT, !Z ; Read next character + MOVE R0, R8 + DECRB ; Restore previous register page + RET +; +;*************************************************************************************** +;* IO$PUT_W_HEX prints a machine word in hexadecimal notation. +;* +;* R8: Contains the machine word to be printed in hex notation. +;* +;* The contents of R8 are being preserved during the run of this function. +;*************************************************************************************** +; +IO$PUT_W_HEX INCRB ; Get a new register page + MOVE 0x0004, R0 ; Save constant for nibble shifting + MOVE R0, R4 ; Set loop counter to four + MOVE R8, R5 ; Copy contents of R8 for later restore + MOVE IO$HEX_NIBBLES, R1 ; Create a pointer to the list of nibbles + ; Push four ASCII characters to the stack +_IO$PWH_SCAN MOVE R1, R2 ; and create a scratch copy of this pointer + MOVE R8, R3 ; Create a local copy of the machine word + AND 0x000f, R3 ; Only the four LSBs are of interest + ADD R3, R2 ; Adjust pointer to the desired nibble + MOVE @R2, @--SP ; and save the ASCII character to the stack + SHR 4, R8 ; Shift R8 four places right + SUB 0x0001, R4 ; Decrement loop counter + RBRA _IO$PWH_SCAN, !Z ; and continue with the next nibble + ; Now read these characters back and print them + MOVE R0, R4 ; Initialize loop counter +_IO$PWH_PRINT MOVE @SP++, R8 ; Fetch a character from the stack + RSUB IO$PUTCHAR, 1 ; and print it + SUB 0x0001, R4 ; Decrement loop counter + RBRA _IO$PWH_PRINT, !Z ; and continue with the next character + ; That is all... + MOVE R5, R8 ; Restore contents of R8 + DECRB ; Restore correct register page + RET +; +;*************************************************************************************** +;* IO$GETCHAR reads a character from the first UART in the system. +;* +;* R8 will contain the character read in its lower eight bits +;*************************************************************************************** +; +IO$GETCHAR INCRB + MOVE IO$UART0_BASE, R0 + MOVE R0, R1 + ADD IO$UART_SRA, R0 ; R0 contains the address of the status register + ADD IO$UART_RHRA, R1 ; R1 contains the address of the receiver reg. +_IO$GETC_LOOP MOVE @R0, R2 ; Read status register + AND 0x0001, R2 ; Only bit 0 is of interest + RBRA _IO$GETC_LOOP, Z ; Loop until a character has been received + MOVE @R1, R8 ; Get the character from the receiver register + DECRB + CMP 0x0005, R8 ; CTRL-E? + RBRA QMON$WARMSTART, Z + RET +; +;*************************************************************************************** +;* IO$GETS reads a CR/LF terminated string from the serial line +;* +;* R8 has to point to a preallocated memory area to store the input line +;*************************************************************************************** +; +IO$GETS INCRB ; Get a new register page + MOVE R8, R0 ; Save parameters + MOVE R8, R1 +_IO$GETS_LOOP RSUB IO$GETCHAR, 1 ; Get a single character from the serial line + MOVE R8, @R0++ ; Store it into the buffer area + RSUB IO$PUTCHAR, 1 ; Echo the character + SUB 0x000A, R8 ; Was it a LF character? + RBRA _IO$GETS_LOOP, !Z ; No -> continue reading characters + MOVE 0x000D, @R0++ ; Extend the string with a CR and + MOVE 0x0000, @R0 ; terminate it with a null word + MOVE R1, R8 ; Restore R8 which will now point to the string + DECRB ; Restore the register page + RET +; +;*************************************************************************************** +;* IO$PUTS prints a null terminated string. +;* +;* R8: Pointer to the string to be printed. Of each word only the lower eight bits +;* will be printed. The terminating word has to be zero. +;* +;* The contents of R8 are being preserved during the run of this function. +;*************************************************************************************** +; +IO$PUTS INCRB ; Get a new register page + MOVE R8, R1 ; Save contents of R8 + MOVE R8, R0 ; Local copy of the string pointer +_IO$PUTS_LOOP MOVE @R0++, R8 ; Get a character from the string + AND 0x00FF, R8 ; Only the lower eight bits are relevant + RBRA _IO$PUTS_END, Z ; Return when the string end has been reached + RSUB IO$PUTCHAR, 1 ; Print this character + RBRA _IO$PUTS_LOOP, 1 ; Continue with the next character +_IO$PUTS_END MOVE R1, R8 ; Restore contents of R8 + DECRB ; Restore correct register page + RET +; +;*************************************************************************************** +;* IO$PUT_CRLF prints actually a LF/CR (the reason for this is that curses on the +;* MAC, where the emulation currently runs, has problems with CR/LF, but +;* not with LF/CR) +;*************************************************************************************** +; +IO$PUT_CRLF INCRB ; Get a new register page + MOVE R8, R0 ; Save contents of R8 + MOVE 0x0A, R8 + RSUB IO$PUTCHAR, 1 + MOVE 0x0D, R8 + RSUB IO$PUTCHAR, 1 + MOVE R0, R8 ; Restore contents of R8 + DECRB ; Return to previous register page + RET +; +;*************************************************************************************** +;* IO$PUTCHAR prints a single character. +;* +;* R8: Contains the character to be printed +; +;* The contents of R8 are being preserved during the run of this function. +;* +;* TODO: This routine is way too simple and only works with the simple +;* UART emulation. To use a real 16550 this routine will require a complete +;* rewrite! +;*************************************************************************************** +; +IO$PUTCHAR INCRB ; Get a new register page + MOVE IO$UART0_BASE, R0 + ADD IO$UART_THRA, R0 ; R0 now points to the THRA register + MOVE R8, @R0 ; Print character + DECRB ; Restore the old page + RET +; +;*************************************************************************************** +; Constants, etc. +;*************************************************************************************** +; +IO$HEX_NIBBLES .ASCII_W "0123456789ABCDEF" +IO$COLON_DELIMITER .ASCII_W ": " diff --git a/monitor/math_library.asm b/monitor/math_library.asm new file mode 100644 index 00000000..d98e2188 --- /dev/null +++ b/monitor/math_library.asm @@ -0,0 +1,50 @@ +; +;;============================================================================= +;; The collection of math related functions starts here +;;============================================================================= +; +;****************************************************************************** +;* MTH$MUL performs a highly unelegant signed 16 x 16 multiplication of the +;* form R11(H)/R10(L) = R8 * R9. +;* +;* (R2 = XL, R3 = XH) = (R8 = |A|), initally +;* R4 = |B| +;* R10 = RL, R11 = RH (result) +;* R5 = counter +;****************************************************************************** +MTH$MUL INCRB + XOR R0, R0 ; Clear negative flags + XOR R1, R1 + XOR R3, R3 ; Clear XH + MOVE R8, R2 ; Negative A? + RBRA _MTH$MUL_A_POS, !N ; No + XOR R2, R2 ; A is negative + SUB R8, R2 ; R2 = |R8| + MOVE 1, R0 ; Remember that A was negative +_MTH$MUL_A_POS MOVE R9, R4 ; Negative B? + RBRA _MTH$MUL_B_POS, !N ; No + XOR R4, R4 ; B is negative + SUB R9, R4 ; R4 = |R9| + MOVE 1, R1 ; Remember that B was negative +_MTH$MUL_B_POS XOR R10, R10 ; Clear the two result registers + XOR R11, R11 + MOVE 0x0010, R5 ; Initialize counter to 16 +_MTH$MUL_LOOP SHR 1, R4 ; Determine LSB of B + RBRA _MTH$MUL_LAST, !X ; Nothing to add, LSB was 0 + ADD R2, R10 ; Add to low result word + ADDC R3, R11 ; Add to high result word + C +_MTH$MUL_LAST SHL 1, R2 ; Shift XL/XH one bit left + RBRA _MTH$MUL_ZERO, !C ; No carry shifted out + OR 0x0002, SR ; There was a carry, set X bit +_MTH$MUL_ZERO SHL 1, R3 ; No shift XH one to the left + SUB 1, R5 ; Decrement counter + RBRA _MTH$MUL_LOOP, !Z ; Still not done? + CMP R0, R1 ; Are the negative flags equal? + RBRA _MTH$MUL_EXIT, Z ; Yes, nothing further to do + NOT R10, R10 ; 2s-complement of the result + NOT R11, R11 + ADD 1, R10 + ADDC 0, R11 +_MTH$MUL_EXIT DECRB + RET + diff --git a/monitor/mem_library.asm b/monitor/mem_library.asm new file mode 100644 index 00000000..13201bfb --- /dev/null +++ b/monitor/mem_library.asm @@ -0,0 +1,38 @@ +; +;;============================================================================= +;; The collection of memory related functions starts here +;;============================================================================= +; +;****************************************************************************** +;* MEM$FILL fills a block of memory running from the address stored in R8. +;* R9 contains the number of words to be written. R10 contains the value to +;* be stored in the memory area. +;****************************************************************************** +MEM$FILL INCRB + MOVE R8, R0 + MOVE R9, R1 +_MEM$FILL_LOOP MOVE R1, R1 ; Zero length left? + RBRA _MEM$FILL_EXIT, Z ; Yes, done... + MOVE R10, @R0++ + SUB 0x0001, R1 + RBRA _MEM$FILL_LOOP, 1 +_MEM$FILL_EXIT DECRB + RET +; +;****************************************************************************** +;* MEM$MOVE moves the memory area starting at the address contained in R8 +;* to the area starting at the address contained in R9. R10 contains the +;* number of words to be moved. +;****************************************************************************** +; +MEM$MOVE INCRB + MOVE R8, R0 + MOVE R9, R1 + MOVE R10, R2 +_MEM$MOVE_LOOP MOVE R2, R2 ; Zero length left? + RBRA _MEM$MOVE_EXIT, Z ; Yes, done... + MOVE @R0++, @R1++ + SUB 0x0001, R2 + RBRA _MEM$MOVE_LOOP, 1 +_MEM$MOVE_EXIT DECRB + RET diff --git a/monitor/monitor.asm b/monitor/monitor.asm new file mode 100644 index 00000000..12e539d0 --- /dev/null +++ b/monitor/monitor.asm @@ -0,0 +1,8 @@ +#include "sysdef.asm" +#include "qmon.asm" +#include "io_library.asm" +#include "string_library.asm" +#include "mem_library.asm" +;; +QMON$LAST_ADDR HALT + diff --git a/monitor/monitor.lis b/monitor/monitor.lis new file mode 100644 index 00000000..ba67c6c7 --- /dev/null +++ b/monitor/monitor.lis @@ -0,0 +1,1796 @@ +000001 +000002 ; +000003 ; This file contains the necessary definitions for the simple QNICE-monitor. +000004 ; +000005 +000006 ; +000007 ; Some assembler macros which make life much easier: +000008 ; +000009 +000010 +000011 +000012 +000013 ; +000014 ; Some register short names: +000015 ; +000016 +000017 +000018 +000019 +000020 ; +000021 ; IO-page addresses: +000022 ; +000023 IO$BASE .EQU 0xFC00 +000024 IO$UART0_BASE .EQU 0xFC00 +000025 +000026 ; +000027 ; UART-registers: +000028 ; +000029 IO$UART_SRA .EQU 0x0001 ; Status register (relative to base address) +000030 IO$UART_RHRA .EQU 0x0003 ; Receiving register (relative to base address) +000031 IO$UART_THRA .EQU 0x0003 ; Transmitting register (relative to base address) +000032 +000033 +000034 ;; +000035 ;; QMON - a simple monitor for the QNICE processor +000036 ;; +000037 ;; The labels and constants of each subsystem are prefixed with a short name denoting the particular +000038 ;; subsystem, followed by a dollar sign. Examples for this are IO$BASE or STR$STRMP etc. Labels +000039 ;; within a routine follow this prefix style but have an additional underscore following the dollar +000040 ;; sign to denote that these labels should normally not be the target of a branch or subroutine call +000041 ;; from outside code areas. +000042 ;; +000043 ;; B. Ulmann fecit +000044 ;; +000045 ;; 17-DEC-2007: Begin of coding +000046 ;; 03-AUG-2015: After upgrading the emulator and fixing some (serious) bugs the work on the +000047 ;; monitor continues +000048 ;; 06-AUG-2015: Basic monitor functions implemented +000049 ;; +000050 ;; Known bugs: +000051 ;; +000052 ;; Bits and pieces: +000053 ;; - All functions expect their input parameters in the registers R8, R9 and maybe R10. +000054 ;; - The result of a function is returned in the first non-used high numbered register, so if a +000055 ;; function expects its parameters in R8 and R9, it will return its result in R10. If it only +000056 ;; expects one parameter, the result will be +000057 ;; returned in R9 respectively. +000058 ;; - Every function name starts with its subsection name followed by a dollar sign, so all string +000059 ;; routines have names starting with "STR$". +000060 ;; - Labels within a function always have an underscore following the subsystem name, so a label +000061 ;; within the routine STR$CMP would have the form "STR$_CMP...". So never jump to a label of the +000062 ;; form "$_..." since this will be a label buried inside a function. +000063 ;; - Every subsystem (string routines, IO routines etc.) has its own constants which are always +000064 ;; located after the code for the routines. +000065 ;; - To assemble this monitor just call the "asm" shell script which will use the C preprocessor +000066 ;; to include the necessary library files. +000067 ;; +000068 .ORG 0x0000 ; The monitor begins at address 0x0000, so the lower +000069 ; address EPROMs should be mapped into memory by hardware +000070 ; default on power up. +000071 ; +000072 ; Some useful constants +000073 ; +000074 +000075 ; +000076 ; Main program +000077 ; +000078 0000 0FB4 FC00 QMON$COLDSTART MOVE IO$BASE, R13 ; Set up stack pointer +000079 0002 0FA0 0119 MOVE QMON$WELCOME, R8 ; Print welcome message +000080 0004 FFB0 0601 RSUB IO$PUTS, 1 +000081 0006 0FA0 06E7 MOVE QMON$LAST_ADDR, R8 ; Clear memory after the monitor +000082 0008 1FA0 0001 ADD 0x0001, R8 ; Start address +000083 000A 0FA4 FC00 MOVE IO$BASE, R9 ; Determine length of memory area +000084 000C 3824 SUB R8, R9 ; to be cleared +000085 000D 3FA4 0001 SUB 0x0001, R9 ; We need one stack cell for the following call +000086 000F BA28 XOR R10, R10 ; Clear with zero words +000087 0010 FFB0 06B6 RSUB MEM$FILL, 1 ; Clear +000088 ;;TODO: Clear registers +000089 0012 0FB4 FC00 QMON$WARMSTART MOVE IO$BASE, R13 ; Set up stack pointer +000090 0014 9FB8 00FF AND 0x00FF, R14 ; Reset register bank to zero +000091 0016 FFB0 0600 RSUB IO$PUT_CRLF, 1 +000092 0018 0FA0 01A0 QMON$MAIN_LOOP MOVE QMON$PROMPT, R8 ; Print monitor prompt +000093 001A FFB0 05EB RSUB IO$PUTS, 1 +000094 001C FFB0 05BE RSUB IO$GETCHAR, 1 ; Wait for a key being pressed +000095 001E FFB0 0625 RSUB CHR$TO_UPPER, 1 ; Convert it into an uppercase letter +000096 0020 FFB0 0605 RSUB IO$PUTCHAR, 1 ; Echo the character +000097 0022 CFA0 0043 CMP 'C', R8 ; Control group? +000098 0024 FFAB 002E RBRA QMON$MAYBE_M, !Z ; No +000099 ; Control group +000100 0026 0FA0 035A MOVE QMON$CG_C, R8 +000101 0028 FFB0 05DD RSUB IO$PUTS, 1 +000102 002A FFB0 05B0 RSUB IO$GETCHAR, 1 ; Get command character +000103 002C FFB0 0617 RSUB CHR$TO_UPPER, 1 +000104 002E CFA0 0043 CMP 'C', R8 ; Cold start? +000105 0030 FFAB 0006 RBRA QMON$C_MAYBE_H, !Z ; No... +000106 ; CONTROL/COLDSTART: +000107 0032 0FA0 0362 MOVE QMON$CG_C_C, R8 +000108 0034 FFB0 05D1 RSUB IO$PUTS, 1 +000109 0036 FFA0 FFC8 RBRA QMON$COLDSTART, 1 ; Yes! +000110 0038 CFA0 0048 QMON$C_MAYBE_H CMP 'H', R8 ; Halt? +000111 003A FFAB 0005 RBRA QMON$MAYBE_R, !Z +000112 ; CONTROL/HALT: +000113 003C 0FA0 036D MOVE QMON$CG_C_H, R8 +000114 003E FFB0 05C7 RSUB IO$PUTS, 1 +000115 0040 E000 HALT +000116 0041 CFA0 0052 QMON$MAYBE_R CMP 'R', R8 ; Run? +000117 0043 FFAB 0009 RBRA QMON$C_ILLEGAL, !Z ; No +000118 ; CONTROL/RUN: +000119 0045 0FA0 0376 MOVE QMON$CG_C_R, R8 +000120 0047 FFB0 05BE RSUB IO$PUTS, 1 +000121 0049 FFB0 0546 RSUB IO$GET_W_HEX, 1 ; Get address +000122 004B FFB0 05CB RSUB IO$PUT_CRLF, 1 +000123 004D F800 ABRA R8, 1 ; Jump to address specified +000124 004E 0FA0 01C8 QMON$C_ILLEGAL MOVE QMON$ILLCMD, R8 ; Control group C, illegal command +000125 0050 FFB0 05B5 RSUB IO$PUTS, 1 +000126 0052 FFA0 FFC4 RBRA QMON$MAIN_LOOP, 1 +000127 0054 CFA0 004D QMON$MAYBE_M CMP 'M', R8 ; Compare with 'M' +000128 0056 FFAB 00B1 RBRA QMON$MAYBE_H, !Z ; No M, try next... +000129 ; Memory control group: +000130 0058 0FA0 0383 MOVE QMON$CG_M, R8 ; Print control group name +000131 005A FFB0 05AB RSUB IO$PUTS, 1 +000132 005C FFB0 057E RSUB IO$GETCHAR, 1 ; Get command character +000133 005E FFB0 05E5 RSUB CHR$TO_UPPER, 1 ; ...convert it to upper case +000134 0060 CFA0 0043 CMP 'C', R8 ; 'Change'? +000135 0062 FFAB 0019 RBRA QMON$M_MAYBE_D, !Z +000136 ; MEMORY/CHANGE: +000137 0064 0FA0 038A MOVE QMON$CG_M_C, R8 ; Print prompt for address +000138 0066 FFB0 059F RSUB IO$PUTS, 1 +000139 0068 FFB0 0527 RSUB IO$GET_W_HEX, 1 ; Read in address +000140 006A 0800 MOVE R8, R0 +000141 006B 0FA0 039A MOVE QMON$CG_M_C1, R8 ; Prepare output of current value +000142 006D FFB0 0598 RSUB IO$PUTS, 1 +000143 006F 0060 MOVE @R0, R8 ; Get current value +000144 0070 FFB0 054A RSUB IO$PUT_W_HEX, 1 ; Print current value +000145 0072 0FA0 03AA MOVE QMON$CG_M_C2, R8 ; Prompt for new value +000146 0074 FFB0 0591 RSUB IO$PUTS, 1 +000147 0076 FFB0 0519 RSUB IO$GET_W_HEX, 1 +000148 0078 0801 MOVE R8, @R0 +000149 0079 FFB0 059D RSUB IO$PUT_CRLF, 1 +000150 007B FFA0 FF9B RBRA QMON$MAIN_LOOP, 1 +000151 007D CFA0 0044 QMON$M_MAYBE_D CMP 'D', R8 +000152 007F FFAB 0017 RBRA QMON$M_MAYBE_E, !Z ; No D, try next... +000153 ; MEMORY/DUMP: +000154 0081 0FA0 03B6 MOVE QMON$CG_M_D, R8 ; Print prompt for start address +000155 0083 FFB0 0582 RSUB IO$PUTS, 1 +000156 0085 FFB0 050A RSUB IO$GET_W_HEX, 1 ; Get start address +000157 0087 0800 MOVE R8, R0 ; Remember start address in R8 +000158 0088 0FA0 03CA MOVE QMON$CG_M_D2, R8 ; Print prompt for end address +000159 008A FFB0 057B RSUB IO$PUTS, 1 +000160 008C FFB0 0503 RSUB IO$GET_W_HEX, 1 ; Get end address +000161 008E FFB0 0588 RSUB IO$PUT_CRLF, 1 +000162 0090 0824 MOVE R8, R9 +000163 0091 0020 MOVE R0, R8 +000164 0092 FFB0 04D2 RSUB IO$DUMP_MEMORY, 1 ; Dump memory contents +000165 0094 FFB0 0582 RSUB IO$PUT_CRLF, 1 +000166 0096 FFA0 FF80 RBRA QMON$MAIN_LOOP, 1 +000167 0098 CFA0 0045 QMON$M_MAYBE_E CMP 'E', R8 ; Is it an 'E'? +000168 009A FFAB 0012 RBRA QMON$M_MAYBE_F, !Z ; No... +000169 ; MEMORY/EXAMINE: +000170 009C 0FA0 03D8 MOVE QMON$CG_M_E, R8 ; Print prompt for address +000171 009E FFB0 0567 RSUB IO$PUTS, 1 +000172 00A0 FFB0 04EF RSUB IO$GET_W_HEX, 1 ; Read address +000173 00A2 0800 MOVE R8, R0 +000174 00A3 0FA0 0020 MOVE ' ', R8 +000175 00A5 FFB0 0580 RSUB IO$PUTCHAR, 1 +000176 00A7 0060 MOVE @R0, R8 +000177 00A8 FFB0 0512 RSUB IO$PUT_W_HEX, 1 +000178 00AA FFB0 056C RSUB IO$PUT_CRLF, 1 +000179 00AC FFA0 FF6A RBRA QMON$MAIN_LOOP, 1 +000180 00AE CFA0 0046 QMON$M_MAYBE_F CMP 'F', R8 +000181 00B0 FFAB 0020 RBRA QMON$M_MAYBE_L, !Z +000182 ; MEMORY/FILL: +000183 00B2 0FA0 03E9 MOVE QMON$CG_M_F, R8 +000184 00B4 FFB0 0551 RSUB IO$PUTS, 1 +000185 00B6 FFB0 04D9 RSUB IO$GET_W_HEX, 1 +000186 00B8 0800 MOVE R8, R0 +000187 00B9 0FA0 03FD MOVE QMON$CG_M_F2, R8 +000188 00BB FFB0 054A RSUB IO$PUTS, 1 +000189 00BD FFB0 04D2 RSUB IO$GET_W_HEX, 1 +000190 00BF 0804 MOVE R8, R1 +000191 00C0 0FA0 040B MOVE QMON$CG_M_F3, R8 +000192 00C2 FFB0 0543 RSUB IO$PUTS, 1 +000193 00C4 FFB0 04CB RSUB IO$GET_W_HEX, 1 +000194 00C6 0828 MOVE R8, R10 +000195 00C7 0020 MOVE R0, R8 +000196 00C8 3004 SUB R0, R1 +000197 00C9 1F84 0001 ADD 0x0001, R1 +000198 00CB 0124 MOVE R1, R9 +000199 00CC FFB0 05FA RSUB MEM$FILL, 1 +000200 00CE FFB0 0548 RSUB IO$PUT_CRLF, 1 +000201 00D0 FFA0 FF46 RBRA QMON$MAIN_LOOP, 1 +000202 00D2 CFA0 004C QMON$M_MAYBE_L CMP 'L', R8 +000203 00D4 FFAB 000C RBRA QMON$M_MAYBE_M, !Z +000204 ; MEMORY/LOAD: +000205 00D6 0FA0 0413 MOVE QMON$CG_M_L, R8 +000206 00D8 FFB0 052D RSUB IO$PUTS, 1 +000207 00DA FFB0 04B5 _QMON$ML_LOOP RSUB IO$GET_W_HEX, 1 ; Get address +000208 00DC 0800 MOVE R8, R0 +000209 00DD FFB0 04B2 RSUB IO$GET_W_HEX, 1 ; Get value +000210 00DF 0801 MOVE R8, @R0 +000211 00E0 FFA0 FFF8 RBRA _QMON$ML_LOOP, 1 +000212 00E2 CFA0 004D QMON$M_MAYBE_M CMP 'M', R8 +000213 00E4 FFAB 001D RBRA QMON$M_ILLEGAL, !Z +000214 ; MEMORY/MOVE: +000215 00E6 0FA0 044D MOVE QMON$CG_M_M, R8 +000216 00E8 FFB0 051D RSUB IO$PUTS, 1 +000217 00EA FFB0 04A5 RSUB IO$GET_W_HEX, 1 +000218 00EC 0800 MOVE R8, R0 +000219 00ED 0FA0 0458 MOVE QMON$CG_M_M2, R8 +000220 00EF FFB0 0516 RSUB IO$PUTS, 1 +000221 00F1 FFB0 049E RSUB IO$GET_W_HEX, 1 +000222 00F3 0804 MOVE R8, R1 +000223 00F4 0FA0 045D MOVE QMON$CG_M_M3, R8 +000224 00F6 FFB0 050F RSUB IO$PUTS, 1 +000225 00F8 FFB0 0497 RSUB IO$GET_W_HEX, 1 +000226 00FA 0828 MOVE R8, R10 +000227 00FB 0020 MOVE R0, R8 +000228 00FC 0124 MOVE R1, R9 +000229 00FD FFB0 05D8 RSUB MEM$MOVE, 1 +000230 00FF FFB0 0517 RSUB IO$PUT_CRLF, 1 +000231 0101 FFA0 FF15 RBRA QMON$MAIN_LOOP, 1 +000232 0103 0FA0 01C8 QMON$M_ILLEGAL MOVE QMON$ILLCMD, R8 +000233 0105 FFB0 0500 RSUB IO$PUTS, 1 +000234 0107 FFA0 FF0F RBRA QMON$MAIN_LOOP, 1 +000235 0109 CFA0 0048 QMON$MAYBE_H CMP 'H', R8 +000236 010B FFAB 0006 RBRA QMON$NOT_H, !Z ; No H, try next... +000237 ; HELP: +000238 010D 0FA0 01E3 MOVE QMON$HELP, R8 ; H(elp) - print help text +000239 010F FFB0 04F6 RSUB IO$PUTS, 1 +000240 0111 FFA0 FF05 RBRA QMON$MAIN_LOOP, 1 +000241 0113 0FA0 01A7 QMON$NOT_H MOVE QMON$ILLCMDGRP, R8A ; Illegal command group +000242 0115 FFB0 04F0 RSUB IO$PUTS, 1 +000243 0117 FFA0 FEFF RBRA QMON$MAIN_LOOP, 1 +000244 +000245 0119 000D QMON$WELCOME .ASCII_P "\n\nSimple QNICE-monitor - Version 0.2 (Bernd Ulmann, August 2015)\n" + 011A 000A + 011B 000D + 011C 000A + 011D 0053 + 011E 0069 + 011F 006D + 0120 0070 + 0121 006C + 0122 0065 + 0123 0020 + 0124 0051 + 0125 004E + 0126 0049 + 0127 0043 + 0128 0045 + 0129 002D + 012A 006D + 012B 006F + 012C 006E + 012D 0069 + 012E 0074 + 012F 006F + 0130 0072 + 0131 0020 + 0132 002D + 0133 0020 + 0134 0056 + 0135 0065 + 0136 0072 + 0137 0073 + 0138 0069 + 0139 006F + 013A 006E + 013B 0020 + 013C 0030 + 013D 002E + 013E 0032 + 013F 0020 + 0140 0028 + 0141 0042 + 0142 0065 + 0143 0072 + 0144 006E + 0145 0064 + 0146 0020 + 0147 0055 + 0148 006C + 0149 006D + 014A 0061 + 014B 006E + 014C 006E + 014D 002C + 014E 0020 + 014F 0041 + 0150 0075 + 0151 0067 + 0152 0075 + 0153 0073 + 0154 0074 + 0155 0020 + 0156 0032 + 0157 0030 + 0158 0031 + 0159 0035 + 015A 0029 + 015B 000D + 015C 000A +000246 015D 002D .ASCII_W "--------------------------------------------------------------\n\n" + 015E 002D + 015F 002D + 0160 002D + 0161 002D + 0162 002D + 0163 002D + 0164 002D + 0165 002D + 0166 002D + 0167 002D + 0168 002D + 0169 002D + 016A 002D + 016B 002D + 016C 002D + 016D 002D + 016E 002D + 016F 002D + 0170 002D + 0171 002D + 0172 002D + 0173 002D + 0174 002D + 0175 002D + 0176 002D + 0177 002D + 0178 002D + 0179 002D + 017A 002D + 017B 002D + 017C 002D + 017D 002D + 017E 002D + 017F 002D + 0180 002D + 0181 002D + 0182 002D + 0183 002D + 0184 002D + 0185 002D + 0186 002D + 0187 002D + 0188 002D + 0189 002D + 018A 002D + 018B 002D + 018C 002D + 018D 002D + 018E 002D + 018F 002D + 0190 002D + 0191 002D + 0192 002D + 0193 002D + 0194 002D + 0195 002D + 0196 002D + 0197 002D + 0198 002D + 0199 002D + 019A 002D + 019B 000D + 019C 000A + 019D 000D + 019E 000A + 019F 0000 +000247 01A0 0051 QMON$PROMPT .ASCII_W "QMON> " + 01A1 004D + 01A2 004F + 01A3 004E + 01A4 003E + 01A5 0020 + 01A6 0000 +000248 01A7 0020 QMON$ILLCMDGRP .ASCII_W " *** Illegal command group ***\n" + 01A8 002A + 01A9 002A + 01AA 002A + 01AB 0020 + 01AC 0049 + 01AD 006C + 01AE 006C + 01AF 0065 + 01B0 0067 + 01B1 0061 + 01B2 006C + 01B3 0020 + 01B4 0063 + 01B5 006F + 01B6 006D + 01B7 006D + 01B8 0061 + 01B9 006E + 01BA 0064 + 01BB 0020 + 01BC 0067 + 01BD 0072 + 01BE 006F + 01BF 0075 + 01C0 0070 + 01C1 0020 + 01C2 002A + 01C3 002A + 01C4 002A + 01C5 000D + 01C6 000A + 01C7 0000 +000249 01C8 0020 QMON$ILLCMD .ASCII_W " *** Illegal command ***\n" + 01C9 002A + 01CA 002A + 01CB 002A + 01CC 0020 + 01CD 0049 + 01CE 006C + 01CF 006C + 01D0 0065 + 01D1 0067 + 01D2 0061 + 01D3 006C + 01D4 0020 + 01D5 0063 + 01D6 006F + 01D7 006D + 01D8 006D + 01D9 0061 + 01DA 006E + 01DB 0064 + 01DC 0020 + 01DD 002A + 01DE 002A + 01DF 002A + 01E0 000D + 01E1 000A + 01E2 0000 +000250 01E3 0045 QMON$HELP .ASCII_P "ELP:\n\n" + 01E4 004C + 01E5 0050 + 01E6 003A + 01E7 000D + 01E8 000A + 01E9 000D + 01EA 000A +000251 01EB 0020 .ASCII_P " C(control group):\n" + 01EC 0020 + 01ED 0020 + 01EE 0020 + 01EF 0043 + 01F0 0028 + 01F1 0063 + 01F2 006F + 01F3 006E + 01F4 0074 + 01F5 0072 + 01F6 006F + 01F7 006C + 01F8 0020 + 01F9 0067 + 01FA 0072 + 01FB 006F + 01FC 0075 + 01FD 0070 + 01FE 0029 + 01FF 003A + 0200 000D + 0201 000A +000252 0202 0020 .ASCII_P " C(old start) H(alt) R(un)\n" + 0203 0020 + 0204 0020 + 0205 0020 + 0206 0020 + 0207 0020 + 0208 0020 + 0209 0020 + 020A 0043 + 020B 0028 + 020C 006F + 020D 006C + 020E 0064 + 020F 0020 + 0210 0073 + 0211 0074 + 0212 0061 + 0213 0072 + 0214 0074 + 0215 0029 + 0216 0020 + 0217 0048 + 0218 0028 + 0219 0061 + 021A 006C + 021B 0074 + 021C 0029 + 021D 0020 + 021E 0052 + 021F 0028 + 0220 0075 + 0221 006E + 0222 0029 + 0223 000D + 0224 000A +000253 0225 0020 .ASCII_P " H(elp)\n" + 0226 0020 + 0227 0020 + 0228 0020 + 0229 0048 + 022A 0028 + 022B 0065 + 022C 006C + 022D 0070 + 022E 0029 + 022F 000D + 0230 000A +000254 0231 0020 .ASCII_P " M(emory group):\n" + 0232 0020 + 0233 0020 + 0234 0020 + 0235 004D + 0236 0028 + 0237 0065 + 0238 006D + 0239 006F + 023A 0072 + 023B 0079 + 023C 0020 + 023D 0067 + 023E 0072 + 023F 006F + 0240 0075 + 0241 0070 + 0242 0029 + 0243 003A + 0244 000D + 0245 000A +000255 0246 0020 .ASCII_P " C(hange) D(ump) E(xamine) F(ill) L(oad) M(ove)\n" + 0247 0020 + 0248 0020 + 0249 0020 + 024A 0020 + 024B 0020 + 024C 0020 + 024D 0020 + 024E 0043 + 024F 0028 + 0250 0068 + 0251 0061 + 0252 006E + 0253 0067 + 0254 0065 + 0255 0029 + 0256 0020 + 0257 0044 + 0258 0028 + 0259 0075 + 025A 006D + 025B 0070 + 025C 0029 + 025D 0020 + 025E 0045 + 025F 0028 + 0260 0078 + 0261 0061 + 0262 006D + 0263 0069 + 0264 006E + 0265 0065 + 0266 0029 + 0267 0020 + 0268 0046 + 0269 0028 + 026A 0069 + 026B 006C + 026C 006C + 026D 0029 + 026E 0020 + 026F 004C + 0270 0028 + 0271 006F + 0272 0061 + 0273 0064 + 0274 0029 + 0275 0020 + 0276 004D + 0277 0028 + 0278 006F + 0279 0076 + 027A 0065 + 027B 0029 + 027C 000D + 027D 000A +000256 027E 000D .ASCII_P "\n General: CTRL-E performs a warm start whenever an\n" + 027F 000A + 0280 0020 + 0281 0020 + 0282 0020 + 0283 0020 + 0284 0047 + 0285 0065 + 0286 006E + 0287 0065 + 0288 0072 + 0289 0061 + 028A 006C + 028B 003A + 028C 0020 + 028D 0043 + 028E 0054 + 028F 0052 + 0290 004C + 0291 002D + 0292 0045 + 0293 0020 + 0294 0070 + 0295 0065 + 0296 0072 + 0297 0066 + 0298 006F + 0299 0072 + 029A 006D + 029B 0073 + 029C 0020 + 029D 0061 + 029E 0020 + 029F 0077 + 02A0 0061 + 02A1 0072 + 02A2 006D + 02A3 0020 + 02A4 0073 + 02A5 0074 + 02A6 0061 + 02A7 0072 + 02A8 0074 + 02A9 0020 + 02AA 0077 + 02AB 0068 + 02AC 0065 + 02AD 006E + 02AE 0065 + 02AF 0076 + 02B0 0065 + 02B1 0072 + 02B2 0020 + 02B3 0061 + 02B4 006E + 02B5 000D + 02B6 000A +000257 02B7 0020 .ASCII_P " input from keyboard is expected.\n" + 02B8 0020 + 02B9 0020 + 02BA 0020 + 02BB 0020 + 02BC 0020 + 02BD 0020 + 02BE 0020 + 02BF 0069 + 02C0 006E + 02C1 0070 + 02C2 0075 + 02C3 0074 + 02C4 0020 + 02C5 0066 + 02C6 0072 + 02C7 006F + 02C8 006D + 02C9 0020 + 02CA 006B + 02CB 0065 + 02CC 0079 + 02CD 0062 + 02CE 006F + 02CF 0061 + 02D0 0072 + 02D1 0064 + 02D2 0020 + 02D3 0069 + 02D4 0073 + 02D5 0020 + 02D6 0065 + 02D7 0078 + 02D8 0070 + 02D9 0065 + 02DA 0063 + 02DB 0074 + 02DC 0065 + 02DD 0064 + 02DE 002E + 02DF 000D + 02E0 000A +000258 02E1 000D .ASCII_P "\n M(emory)L(oad) can be used to load assembler output\n" + 02E2 000A + 02E3 0020 + 02E4 0020 + 02E5 0020 + 02E6 0020 + 02E7 004D + 02E8 0028 + 02E9 0065 + 02EA 006D + 02EB 006F + 02EC 0072 + 02ED 0079 + 02EE 0029 + 02EF 004C + 02F0 0028 + 02F1 006F + 02F2 0061 + 02F3 0064 + 02F4 0029 + 02F5 0020 + 02F6 0063 + 02F7 0061 + 02F8 006E + 02F9 0020 + 02FA 0062 + 02FB 0065 + 02FC 0020 + 02FD 0075 + 02FE 0073 + 02FF 0065 + 0300 0064 + 0301 0020 + 0302 0074 + 0303 006F + 0304 0020 + 0305 006C + 0306 006F + 0307 0061 + 0308 0064 + 0309 0020 + 030A 0061 + 030B 0073 + 030C 0073 + 030D 0065 + 030E 006D + 030F 0062 + 0310 006C + 0311 0065 + 0312 0072 + 0313 0020 + 0314 006F + 0315 0075 + 0316 0074 + 0317 0070 + 0318 0075 + 0319 0074 + 031A 000D + 031B 000A +000259 031C 0020 .ASCII_P " by pasting it to the terminal. CTRL-E terminates.\n" + 031D 0020 + 031E 0020 + 031F 0020 + 0320 0020 + 0321 0020 + 0322 0020 + 0323 0020 + 0324 0062 + 0325 0079 + 0326 0020 + 0327 0070 + 0328 0061 + 0329 0073 + 032A 0074 + 032B 0069 + 032C 006E + 032D 0067 + 032E 0020 + 032F 0069 + 0330 0074 + 0331 0020 + 0332 0074 + 0333 006F + 0334 0020 + 0335 0074 + 0336 0068 + 0337 0065 + 0338 0020 + 0339 0074 + 033A 0065 + 033B 0072 + 033C 006D + 033D 0069 + 033E 006E + 033F 0061 + 0340 006C + 0341 002E + 0342 0020 + 0343 0043 + 0344 0054 + 0345 0052 + 0346 004C + 0347 002D + 0348 0045 + 0349 0020 + 034A 0074 + 034B 0065 + 034C 0072 + 034D 006D + 034E 0069 + 034F 006E + 0350 0061 + 0351 0074 + 0352 0065 + 0353 0073 + 0354 002E + 0355 000D + 0356 000A +000260 0357 000D .ASCII_W "\n" + 0358 000A + 0359 0000 +000261 035A 004F QMON$CG_C .ASCII_W "ONTROL/" + 035B 004E + 035C 0054 + 035D 0052 + 035E 004F + 035F 004C + 0360 002F + 0361 0000 +000262 0362 0043 QMON$CG_C_C .ASCII_W "COLD START" + 0363 004F + 0364 004C + 0365 0044 + 0366 0020 + 0367 0053 + 0368 0054 + 0369 0041 + 036A 0052 + 036B 0054 + 036C 0000 +000263 036D 0048 QMON$CG_C_H .ASCII_W "HALT\n\n" + 036E 0041 + 036F 004C + 0370 0054 + 0371 000D + 0372 000A + 0373 000D + 0374 000A + 0375 0000 +000264 0376 0052 QMON$CG_C_R .ASCII_W "RUN ADDRESS=" + 0377 0055 + 0378 004E + 0379 0020 + 037A 0041 + 037B 0044 + 037C 0044 + 037D 0052 + 037E 0045 + 037F 0053 + 0380 0053 + 0381 003D + 0382 0000 +000265 0383 0045 QMON$CG_M .ASCII_W "EMORY/" + 0384 004D + 0385 004F + 0386 0052 + 0387 0059 + 0388 002F + 0389 0000 +000266 038A 0043 QMON$CG_M_C .ASCII_W "CHANGE ADDRESS=" + 038B 0048 + 038C 0041 + 038D 004E + 038E 0047 + 038F 0045 + 0390 0020 + 0391 0041 + 0392 0044 + 0393 0044 + 0394 0052 + 0395 0045 + 0396 0053 + 0397 0053 + 0398 003D + 0399 0000 +000267 039A 0020 QMON$CG_M_C1 .ASCII_W " CURRENT VALUE=" + 039B 0043 + 039C 0055 + 039D 0052 + 039E 0052 + 039F 0045 + 03A0 004E + 03A1 0054 + 03A2 0020 + 03A3 0056 + 03A4 0041 + 03A5 004C + 03A6 0055 + 03A7 0045 + 03A8 003D + 03A9 0000 +000268 03AA 0020 QMON$CG_M_C2 .ASCII_W " NEW VALUE=" + 03AB 004E + 03AC 0045 + 03AD 0057 + 03AE 0020 + 03AF 0056 + 03B0 0041 + 03B1 004C + 03B2 0055 + 03B3 0045 + 03B4 003D + 03B5 0000 +000269 03B6 0044 QMON$CG_M_D .ASCII_W "DUMP START ADDRESS=" + 03B7 0055 + 03B8 004D + 03B9 0050 + 03BA 0020 + 03BB 0053 + 03BC 0054 + 03BD 0041 + 03BE 0052 + 03BF 0054 + 03C0 0020 + 03C1 0041 + 03C2 0044 + 03C3 0044 + 03C4 0052 + 03C5 0045 + 03C6 0053 + 03C7 0053 + 03C8 003D + 03C9 0000 +000270 03CA 0020 QMON$CG_M_D2 .ASCII_W " END ADDRESS=" + 03CB 0045 + 03CC 004E + 03CD 0044 + 03CE 0020 + 03CF 0041 + 03D0 0044 + 03D1 0044 + 03D2 0052 + 03D3 0045 + 03D4 0053 + 03D5 0053 + 03D6 003D + 03D7 0000 +000271 03D8 0045 QMON$CG_M_E .ASCII_W "EXAMINE ADDRESS=" + 03D9 0058 + 03DA 0041 + 03DB 004D + 03DC 0049 + 03DD 004E + 03DE 0045 + 03DF 0020 + 03E0 0041 + 03E1 0044 + 03E2 0044 + 03E3 0052 + 03E4 0045 + 03E5 0053 + 03E6 0053 + 03E7 003D + 03E8 0000 +000272 03E9 0046 QMON$CG_M_F .ASCII_W "FILL START ADDRESS=" + 03EA 0049 + 03EB 004C + 03EC 004C + 03ED 0020 + 03EE 0053 + 03EF 0054 + 03F0 0041 + 03F1 0052 + 03F2 0054 + 03F3 0020 + 03F4 0041 + 03F5 0044 + 03F6 0044 + 03F7 0052 + 03F8 0045 + 03F9 0053 + 03FA 0053 + 03FB 003D + 03FC 0000 +000273 03FD 0020 QMON$CG_M_F2 .ASCII_W " END ADDRESS=" + 03FE 0045 + 03FF 004E + 0400 0044 + 0401 0020 + 0402 0041 + 0403 0044 + 0404 0044 + 0405 0052 + 0406 0045 + 0407 0053 + 0408 0053 + 0409 003D + 040A 0000 +000274 040B 0020 QMON$CG_M_F3 .ASCII_W " VALUE=" + 040C 0056 + 040D 0041 + 040E 004C + 040F 0055 + 0410 0045 + 0411 003D + 0412 0000 +000275 0413 004C QMON$CG_M_L .ASCII_W "LOAD - ENTER ADDRESS/VALUE PAIRS, TERMINATE WITH CTRL-E\n" + 0414 004F + 0415 0041 + 0416 0044 + 0417 0020 + 0418 002D + 0419 0020 + 041A 0045 + 041B 004E + 041C 0054 + 041D 0045 + 041E 0052 + 041F 0020 + 0420 0041 + 0421 0044 + 0422 0044 + 0423 0052 + 0424 0045 + 0425 0053 + 0426 0053 + 0427 002F + 0428 0056 + 0429 0041 + 042A 004C + 042B 0055 + 042C 0045 + 042D 0020 + 042E 0050 + 042F 0041 + 0430 0049 + 0431 0052 + 0432 0053 + 0433 002C + 0434 0020 + 0435 0054 + 0436 0045 + 0437 0052 + 0438 004D + 0439 0049 + 043A 004E + 043B 0041 + 043C 0054 + 043D 0045 + 043E 0020 + 043F 0057 + 0440 0049 + 0441 0054 + 0442 0048 + 0443 0020 + 0444 0043 + 0445 0054 + 0446 0052 + 0447 004C + 0448 002D + 0449 0045 + 044A 000D + 044B 000A + 044C 0000 +000276 044D 004D QMON$CG_M_M .ASCII_W "MOVE FROM=" + 044E 004F + 044F 0056 + 0450 0045 + 0451 0020 + 0452 0046 + 0453 0052 + 0454 004F + 0455 004D + 0456 003D + 0457 0000 +000277 0458 0020 QMON$CG_M_M2 .ASCII_W " TO=" + 0459 0054 + 045A 004F + 045B 003D + 045C 0000 +000278 045D 0020 QMON$CG_M_M3 .ASCII_W " LENGTH=" + 045E 004C + 045F 0045 + 0460 004E + 0461 0047 + 0462 0054 + 0463 0048 + 0464 003D + 0465 0000 +000279 ; +000280 0466 0000 QMON$COMMAND .BLOCK 0x0100 ; Reserve some memory for holding a command line + 0467 0000 + 0468 0000 + 0469 0000 + 046A 0000 + 046B 0000 + 046C 0000 + 046D 0000 + 046E 0000 + 046F 0000 + 0470 0000 + 0471 0000 + 0472 0000 + 0473 0000 + 0474 0000 + 0475 0000 + 0476 0000 + 0477 0000 + 0478 0000 + 0479 0000 + 047A 0000 + 047B 0000 + 047C 0000 + 047D 0000 + 047E 0000 + 047F 0000 + 0480 0000 + 0481 0000 + 0482 0000 + 0483 0000 + 0484 0000 + 0485 0000 + 0486 0000 + 0487 0000 + 0488 0000 + 0489 0000 + 048A 0000 + 048B 0000 + 048C 0000 + 048D 0000 + 048E 0000 + 048F 0000 + 0490 0000 + 0491 0000 + 0492 0000 + 0493 0000 + 0494 0000 + 0495 0000 + 0496 0000 + 0497 0000 + 0498 0000 + 0499 0000 + 049A 0000 + 049B 0000 + 049C 0000 + 049D 0000 + 049E 0000 + 049F 0000 + 04A0 0000 + 04A1 0000 + 04A2 0000 + 04A3 0000 + 04A4 0000 + 04A5 0000 + 04A6 0000 + 04A7 0000 + 04A8 0000 + 04A9 0000 + 04AA 0000 + 04AB 0000 + 04AC 0000 + 04AD 0000 + 04AE 0000 + 04AF 0000 + 04B0 0000 + 04B1 0000 + 04B2 0000 + 04B3 0000 + 04B4 0000 + 04B5 0000 + 04B6 0000 + 04B7 0000 + 04B8 0000 + 04B9 0000 + 04BA 0000 + 04BB 0000 + 04BC 0000 + 04BD 0000 + 04BE 0000 + 04BF 0000 + 04C0 0000 + 04C1 0000 + 04C2 0000 + 04C3 0000 + 04C4 0000 + 04C5 0000 + 04C6 0000 + 04C7 0000 + 04C8 0000 + 04C9 0000 + 04CA 0000 + 04CB 0000 + 04CC 0000 + 04CD 0000 + 04CE 0000 + 04CF 0000 + 04D0 0000 + 04D1 0000 + 04D2 0000 + 04D3 0000 + 04D4 0000 + 04D5 0000 + 04D6 0000 + 04D7 0000 + 04D8 0000 + 04D9 0000 + 04DA 0000 + 04DB 0000 + 04DC 0000 + 04DD 0000 + 04DE 0000 + 04DF 0000 + 04E0 0000 + 04E1 0000 + 04E2 0000 + 04E3 0000 + 04E4 0000 + 04E5 0000 + 04E6 0000 + 04E7 0000 + 04E8 0000 + 04E9 0000 + 04EA 0000 + 04EB 0000 + 04EC 0000 + 04ED 0000 + 04EE 0000 + 04EF 0000 + 04F0 0000 + 04F1 0000 + 04F2 0000 + 04F3 0000 + 04F4 0000 + 04F5 0000 + 04F6 0000 + 04F7 0000 + 04F8 0000 + 04F9 0000 + 04FA 0000 + 04FB 0000 + 04FC 0000 + 04FD 0000 + 04FE 0000 + 04FF 0000 + 0500 0000 + 0501 0000 + 0502 0000 + 0503 0000 + 0504 0000 + 0505 0000 + 0506 0000 + 0507 0000 + 0508 0000 + 0509 0000 + 050A 0000 + 050B 0000 + 050C 0000 + 050D 0000 + 050E 0000 + 050F 0000 + 0510 0000 + 0511 0000 + 0512 0000 + 0513 0000 + 0514 0000 + 0515 0000 + 0516 0000 + 0517 0000 + 0518 0000 + 0519 0000 + 051A 0000 + 051B 0000 + 051C 0000 + 051D 0000 + 051E 0000 + 051F 0000 + 0520 0000 + 0521 0000 + 0522 0000 + 0523 0000 + 0524 0000 + 0525 0000 + 0526 0000 + 0527 0000 + 0528 0000 + 0529 0000 + 052A 0000 + 052B 0000 + 052C 0000 + 052D 0000 + 052E 0000 + 052F 0000 + 0530 0000 + 0531 0000 + 0532 0000 + 0533 0000 + 0534 0000 + 0535 0000 + 0536 0000 + 0537 0000 + 0538 0000 + 0539 0000 + 053A 0000 + 053B 0000 + 053C 0000 + 053D 0000 + 053E 0000 + 053F 0000 + 0540 0000 + 0541 0000 + 0542 0000 + 0543 0000 + 0544 0000 + 0545 0000 + 0546 0000 + 0547 0000 + 0548 0000 + 0549 0000 + 054A 0000 + 054B 0000 + 054C 0000 + 054D 0000 + 054E 0000 + 054F 0000 + 0550 0000 + 0551 0000 + 0552 0000 + 0553 0000 + 0554 0000 + 0555 0000 + 0556 0000 + 0557 0000 + 0558 0000 + 0559 0000 + 055A 0000 + 055B 0000 + 055C 0000 + 055D 0000 + 055E 0000 + 055F 0000 + 0560 0000 + 0561 0000 + 0562 0000 + 0563 0000 + 0564 0000 + 0565 0000 +000281 +000282 ; +000283 ;;======================================================================================= +000284 ;; The collection of input/output related function starts here +000285 ;;======================================================================================= +000286 ; +000287 ; Define io system specific constants and memory areas etc. It is expected that the +000288 ; basic definitions from sysdef.asm have been included somewhere before! +000289 ; +000290 ;*************************************************************************************** +000291 ;* IO$DUMP_MEMORY prints a hexadecimal memory dump of a specified memory region. +000292 ;* +000293 ;* R8: Contains the start address +000294 ;* R9: Contains the end address (inclusive) +000295 ;* +000296 ;* The contents of R8 and R9 are preserved during the run of this function. +000297 ;*************************************************************************************** +000298 ; +000299 0566 1FB8 0100 IO$DUMP_MEMORY ADD 0x0100, R14 ; Get a new register page +000300 0568 0800 MOVE R8, R0 ; R0 will be the loop counter +000301 0569 0804 MOVE R8, R1 ; This will be needed to restore R8 later +000302 056A 090C MOVE R9, R3 +000303 056B 1F8C 0001 ADD 0x0001, R3 ; That is necessary since we want the last +000304 ; address printed, too +000305 056D 0F90 FFFF MOVE 0xFFFF, R4 ; Set R4 - this is the column counter - to -1 +000306 056F 0008 _IO$DUMP_MEMORY_LOOP MOVE R0, R2 ; Have we reached the end of the memory area? +000307 0570 3308 SUB R3, R2 +000308 0571 FFA3 0018 RBRA _IO$DUMP_MEMORY_EXIT, Z ; Yes - that is it, so exit this routine +000309 0573 1F90 0001 ADD 0x0001, R4 ; Next column +000310 0575 9F90 0007 AND 0x0007, R4 ; We compute mod 8 +000311 0577 FFAB 0009 RBRA _IO$DUMP_MEMORY_CONTENT, !Z; if the result is not equal 0 we do not +000312 ; need an address printed +000313 0579 FFB0 009D RSUB IO$PUT_CRLF, 1 ; Print a CR/LF pair +000314 057B 0020 MOVE R0, R8 ; Print address +000315 057C FFB0 003E RSUB IO$PUT_W_HEX, 1 +000316 057E 0FA0 0642 MOVE IO$COLON_DELIMITER, R8 ; Print a colon followed by a space +000317 0580 FFB0 0085 RSUB IO$PUTS, 1 +000318 0582 00A0 _IO$DUMP_MEMORY_CONTENT MOVE @R0++, R8 ; Print the memory contents of this location +000319 0583 FFB0 0037 RSUB IO$PUT_W_HEX, 1 +000320 0585 0FA0 0020 MOVE ' ', R8 ; Print a space +000321 0587 FFB0 009E RSUB IO$PUTCHAR, 1 +000322 0589 FFA0 FFE4 RBRA _IO$DUMP_MEMORY_LOOP, 1 ; Continue the print loop +000323 058B FFB0 008B _IO$DUMP_MEMORY_EXIT RSUB IO$PUT_CRLF, 1 ; Print a last CR/LF pair +000324 058D 0120 MOVE R1, R8 ; Restore R8, +000325 058E 3FB8 0100 SUB 0x0100, R14 ; switch back to the correct register page +000326 0590 0DBC MOVE @R13++, R15 +000327 ; +000328 ;*************************************************************************************** +000329 ;* IO$GET_W_HEX reads four hex nibbles from stdin and returns the corresponding +000330 ;* value in R8 +000331 ;* +000332 ;* Illegal characters (not 1-9A-F or a-f) will generate a bell signal. The only +000333 ;* exception to this behaviour is the character 'x' which will erase any input +000334 ;* up to this point. This has the positive effect that a hexadecimal value can +000335 ;* be entered as 0x.... or just as .... +000336 ;*************************************************************************************** +000337 ; +000338 0591 1FB8 0100 IO$GET_W_HEX ADD 0x0100, R14 ; Get a new register page +000339 0593 B000 _IO$GET_W_HEX_REDO XOR R0, R0 ; Clear R0 +000340 0594 0F84 0004 MOVE 4, R1 ; We need four characters +000341 0596 0FA4 0631 MOVE IO$HEX_NIBBLES, R9 ; Pointer to list of valid chars +000342 0598 FFB0 0042 _IO$GET_W_HEX_INPUT RSUB IO$GETCHAR, 1 ; Read a character into R8 +000343 059A FFA3 FA76 RBRA QMON$WARMSTART, Z +000344 059C FFB0 00A7 RSUB CHR$TO_UPPER, 1 ; Convert to upper case +000345 059E CFA0 0058 CMP 'X', R8 ; Was it an 'X'? +000346 05A0 FFA3 FFF1 RBRA _IO$GET_W_HEX_REDO, Z ; Yes - redo from start :-) +000347 05A2 FFB0 010F RSUB STR$STRCHR, 1 ; Is it a valid character? +000348 05A4 0A28 MOVE R10, R10 ; Result equal zero? +000349 05A5 FFAB 0006 RBRA _IO$GET_W_HEX_VALID, !Z ; No +000350 05A7 0FA0 0007 MOVE 7, R8 ; Yes - generate a beep :-) +000351 05A9 FFB0 007C RSUB IO$PUTCHAR, 1 +000352 05AB FFA0 FFEB RBRA _IO$GET_W_HEX_INPUT, 1 ; Retry +000353 05AD FFB0 0078 _IO$GET_W_HEX_VALID RSUB IO$PUTCHAR, 1 ; Echo character +000354 05AF 3FA8 0631 SUB IO$HEX_NIBBLES, R10 ; Get number of character +000355 05B1 5F80 0004 SHL 4, R0 +000356 05B3 1A00 ADD R10, R0 +000357 05B4 3F84 0001 SUB 0x0001, R1 +000358 05B6 FFAB FFE0 RBRA _IO$GET_W_HEX_INPUT, !Z ; Read next character +000359 05B8 0020 MOVE R0, R8 +000360 05B9 3FB8 0100 SUB 0x0100, R14 ; Restore previous register page +000361 05BB 0DBC MOVE @R13++, R15 +000362 ; +000363 ;*************************************************************************************** +000364 ;* IO$PUT_W_HEX prints a machine word in hexadecimal notation. +000365 ;* +000366 ;* R8: Contains the machine word to be printed in hex notation. +000367 ;* +000368 ;* The contents of R8 are being preserved during the run of this function. +000369 ;*************************************************************************************** +000370 ; +000371 05BC 1FB8 0100 IO$PUT_W_HEX ADD 0x0100, R14 ; Get a new register page +000372 05BE 0F80 0004 MOVE 0x0004, R0 ; Save constant for nibble shifting +000373 05C0 0010 MOVE R0, R4 ; Set loop counter to four +000374 05C1 0814 MOVE R8, R5 ; Copy contents of R8 for later restore +000375 05C2 0F84 0631 MOVE IO$HEX_NIBBLES, R1 ; Create a pointer to the list of nibbles +000376 ; Push four ASCII characters to the stack +000377 05C4 0108 _IO$PWH_SCAN MOVE R1, R2 ; and create a scratch copy of this pointer +000378 05C5 080C MOVE R8, R3 ; Create a local copy of the machine word +000379 05C6 9F8C 000F AND 0x000f, R3 ; Only the four LSBs are of interest +000380 05C8 1308 ADD R3, R2 ; Adjust pointer to the desired nibble +000381 05C9 0277 MOVE @R2, @--R13 ; and save the ASCII character to the stack +000382 05CA 6FA0 0004 SHR 4, R8 ; Shift R8 four places right +000383 05CC 3F90 0001 SUB 0x0001, R4 ; Decrement loop counter +000384 05CE FFAB FFF4 RBRA _IO$PWH_SCAN, !Z ; and continue with the next nibble +000385 ; Now read these characters back and print them +000386 05D0 0010 MOVE R0, R4 ; Initialize loop counter +000387 05D1 0DA0 _IO$PWH_PRINT MOVE @R13++, R8 ; Fetch a character from the stack +000388 05D2 FFB0 0053 RSUB IO$PUTCHAR, 1 ; and print it +000389 05D4 3F90 0001 SUB 0x0001, R4 ; Decrement loop counter +000390 05D6 FFAB FFF9 RBRA _IO$PWH_PRINT, !Z ; and continue with the next character +000391 ; That is all... +000392 05D8 0520 MOVE R5, R8 ; Restore contents of R8 +000393 05D9 3FB8 0100 SUB 0x0100, R14 ; Restore correct register page +000394 05DB 0DBC MOVE @R13++, R15 +000395 ; +000396 ;*************************************************************************************** +000397 ;* IO$GETCHAR reads a character from the first UART in the system. +000398 ;* +000399 ;* R8 will contain the character read in its lower eight bits +000400 ;*************************************************************************************** +000401 ; +000402 05DC 1FB8 0100 IO$GETCHAR ADD 0x0100, R14 +000403 05DE 0F80 FC00 MOVE IO$UART0_BASE, R0 +000404 05E0 0004 MOVE R0, R1 +000405 05E1 1F80 0001 ADD IO$UART_SRA, R0 ; R0 contains the address of the status register +000406 05E3 1F84 0003 ADD IO$UART_RHRA, R1 ; R1 contains the address of the receiver reg. +000407 05E5 0048 _IO$GETC_LOOP MOVE @R0, R2 ; Read status register +000408 05E6 9F88 0001 AND 0x0001, R2 ; Only bit 0 is of interest +000409 05E8 FFA3 FFFB RBRA _IO$GETC_LOOP, Z ; Loop until a character has been received +000410 05EA 0160 MOVE @R1, R8 ; Get the character from the receiver register +000411 05EB 3FB8 0100 SUB 0x0100, R14 +000412 05ED CFA0 0005 CMP 0x0005, R8 ; CTRL-E? +000413 05EF FFA3 FA21 RBRA QMON$WARMSTART, Z +000414 05F1 0DBC MOVE @R13++, R15 +000415 ; +000416 ;*************************************************************************************** +000417 ;* IO$GETS reads a CR/LF terminated string from the serial line +000418 ;* +000419 ;* R8 has to point to a preallocated memory area to store the input line +000420 ;*************************************************************************************** +000421 ; +000422 05F2 1FB8 0100 IO$GETS ADD 0x0100, R14 ; Get a new register page +000423 05F4 0800 MOVE R8, R0 ; Save parameters +000424 05F5 0804 MOVE R8, R1 +000425 05F6 FFB0 FFE4 _IO$GETS_LOOP RSUB IO$GETCHAR, 1 ; Get a single character from the serial line +000426 05F8 0802 MOVE R8, @R0++ ; Store it into the buffer area +000427 05F9 FFB0 002C RSUB IO$PUTCHAR, 1 ; Echo the character +000428 05FB 3FA0 000A SUB 0x000A, R8 ; Was it a LF character? +000429 05FD FFAB FFF7 RBRA _IO$GETS_LOOP, !Z ; No -> continue reading characters +000430 05FF 0F82 000D MOVE 0x000D, @R0++ ; Extend the string with a CR and +000431 0601 0F81 0000 MOVE 0x0000, @R0 ; terminate it with a null word +000432 0603 0120 MOVE R1, R8 ; Restore R8 which will now point to the string +000433 0604 3FB8 0100 SUB 0x0100, R14 ; Restore the register page +000434 0606 0DBC MOVE @R13++, R15 +000435 ; +000436 ;*************************************************************************************** +000437 ;* IO$PUTS prints a null terminated string. +000438 ;* +000439 ;* R8: Pointer to the string to be printed. Of each word only the lower eight bits +000440 ;* will be printed. The terminating word has to be zero. +000441 ;* +000442 ;* The contents of R8 are being preserved during the run of this function. +000443 ;*************************************************************************************** +000444 ; +000445 0607 1FB8 0100 IO$PUTS ADD 0x0100, R14 ; Get a new register page +000446 0609 0804 MOVE R8, R1 ; Save contents of R8 +000447 060A 0800 MOVE R8, R0 ; Local copy of the string pointer +000448 060B 00A0 _IO$PUTS_LOOP MOVE @R0++, R8 ; Get a character from the string +000449 060C 9FA0 00FF AND 0x00FF, R8 ; Only the lower eight bits are relevant +000450 060E FFA3 0004 RBRA _IO$PUTS_END, Z ; Return when the string end has been reached +000451 0610 FFB0 0015 RSUB IO$PUTCHAR, 1 ; Print this character +000452 0612 FFA0 FFF7 RBRA _IO$PUTS_LOOP, 1 ; Continue with the next character +000453 0614 0120 _IO$PUTS_END MOVE R1, R8 ; Restore contents of R8 +000454 0615 3FB8 0100 SUB 0x0100, R14 ; Restore correct register page +000455 0617 0DBC MOVE @R13++, R15 +000456 ; +000457 ;*************************************************************************************** +000458 ;* IO$PUT_CRLF prints actually a LF/CR (the reason for this is that curses on the +000459 ;* MAC, where the emulation currently runs, has problems with CR/LF, but +000460 ;* not with LF/CR) +000461 ;*************************************************************************************** +000462 ; +000463 0618 1FB8 0100 IO$PUT_CRLF ADD 0x0100, R14 ; Get a new register page +000464 061A 0800 MOVE R8, R0 ; Save contents of R8 +000465 061B 0FA0 000A MOVE 0x0A, R8 +000466 061D FFB0 0008 RSUB IO$PUTCHAR, 1 +000467 061F 0FA0 000D MOVE 0x0D, R8 +000468 0621 FFB0 0004 RSUB IO$PUTCHAR, 1 +000469 0623 0020 MOVE R0, R8 ; Restore contents of R8 +000470 0624 3FB8 0100 SUB 0x0100, R14 ; Return to previous register page +000471 0626 0DBC MOVE @R13++, R15 +000472 ; +000473 ;*************************************************************************************** +000474 ;* IO$PUTCHAR prints a single character. +000475 ;* +000476 ;* R8: Contains the character to be printed +000477 ; +000478 ;* The contents of R8 are being preserved during the run of this function. +000479 ;* +000480 ;* TODO: This routine is way too simple and only works with the simple +000481 ;* UART emulation. To use a real 16550 this routine will require a complete +000482 ;* rewrite! +000483 ;*************************************************************************************** +000484 ; +000485 0627 1FB8 0100 IO$PUTCHAR ADD 0x0100, R14 ; Get a new register page +000486 0629 0F80 FC00 MOVE IO$UART0_BASE, R0 +000487 062B 1F80 0003 ADD IO$UART_THRA, R0 ; R0 now points to the THRA register +000488 062D 0801 MOVE R8, @R0 ; Print character +000489 062E 3FB8 0100 SUB 0x0100, R14 ; Restore the old page +000490 0630 0DBC MOVE @R13++, R15 +000491 ; +000492 ;*************************************************************************************** +000493 ; Constants, etc. +000494 ;*************************************************************************************** +000495 ; +000496 0631 0030 IO$HEX_NIBBLES .ASCII_W "0123456789ABCDEF" + 0632 0031 + 0633 0032 + 0634 0033 + 0635 0034 + 0636 0035 + 0637 0036 + 0638 0037 + 0639 0038 + 063A 0039 + 063B 0041 + 063C 0042 + 063D 0043 + 063E 0044 + 063F 0045 + 0640 0046 + 0641 0000 +000497 0642 003A IO$COLON_DELIMITER .ASCII_W ": " + 0643 0020 + 0644 0000 +000498 +000499 ; +000500 ;;======================================================================================= +000501 ;; The collection of string related functions starts here +000502 ;;======================================================================================= +000503 ; +000504 ;*************************************************************************************** +000505 ;* CHR$TO_UPPER expects a character to be converted to upper case in R8 +000506 ;*************************************************************************************** +000507 ; +000508 0645 1FB8 0100 CHR$TO_UPPER ADD 0x0100, R14 +000509 0647 0800 MOVE R8, R0 ; Save character +000510 0648 3F80 0061 SUB 'a', R0 ; Less than 'a'? +000511 064A FFA4 0009 RBRA _CHR$TO_UPPER_EXIT, N ; Yes - nothing to do +000512 064C 0F80 007A MOVE 'z', R0 ; Check if greater than 'z' +000513 064E 3800 SUB R8, R0 +000514 064F FFA4 0004 RBRA _CHR$TO_UPPER_EXIT, N ; Yes - nothing to do +000515 0651 3FA0 0061 SUB 'a', R8 ; Perform the conversion +000516 0653 1FA0 0041 ADD 'A', R8 +000517 0655 3FB8 0100 _CHR$TO_UPPER_EXIT SUB 0x0100, R14 +000518 0657 0DBC MOVE @R13++, R15 +000519 ; +000520 ;*************************************************************************************** +000521 ;* STR$TO_UPPER expects the address of a string to be converted to upper case in R8 +000522 ;*************************************************************************************** +000523 ; +000524 0658 1FB8 0100 STR$TO_UPPER ADD 0x0100, R14 ; Get a new scratch register page +000525 065A 0800 MOVE R8, R0 ; Do not destroy parameters +000526 065B 0044 _STR$TO_UPPER_LOOP MOVE @R0, R1 ; Null terminator found? +000527 065C FFA3 0013 RBRA _STR$TO_UPPER_END, Z ; Yes - that is it +000528 065E 0108 MOVE R1, R2 +000529 065F 3F88 0061 SUB 'a', R2 ; Less than 'a'? +000530 0661 FFA4 000A RBRA _STR$TO_UPPER_NEXT, N ; Yes +000531 0663 0F88 007A MOVE 'z', R2 ; Greater than 'z'? +000532 0665 3108 SUB R1, R2 +000533 0666 FFA4 0005 RBRA _STR$TO_UPPER_NEXT, N ; Yes +000534 0668 3F84 0061 SUB 'a', R1 ; Now convert the LC char to UC +000535 066A 1F84 0041 ADD 'A', R1 +000536 066C 0101 MOVE R1, @R0 ; Store it back into the string +000537 066D 1F80 0001 _STR$TO_UPPER_NEXT ADD 0x001, R0 +000538 066F FFA0 FFEA RBRA _STR$TO_UPPER_LOOP, 1 ; Process next character +000539 0671 3FB8 0100 _STR$TO_UPPER_END SUB 0x0100, R14 ; Restore old register page +000540 0673 0DBC MOVE @R13++, R15 +000541 ; +000542 ;*************************************************************************************** +000543 ;* STR$LEN expects the address of a string in R8 and returns its length in R9 +000544 ;*************************************************************************************** +000545 ; +000546 0674 1FB8 0100 STR$LEN ADD 0x0100, R14 ; Get a new scratch register page +000547 0676 0800 MOVE R8, R0 ; Do not work with the original pointer +000548 0677 0FA4 FFFF MOVE 0xFFFF, R9 ; R9 = -1 +000549 0679 1FA4 0001 _STR$LEN_LOOP ADD 0x0001, R9 ; One character found +000550 067B 0084 MOVE @R0++, R1 ; Was it the terminating null word? +000551 067C FFAB FFFB RBRA _STR$LEN_LOOP, !Z ; No? +000552 067E 3FB8 0100 SUB 0x0100, R14 +000553 0680 0DBC MOVE @R13++, R15 +000554 ; +000555 ;*************************************************************************************** +000556 ;* STR$CHOMP removes a trailing LF/CR from a string pointed to by R8 +000557 ;*************************************************************************************** +000558 ; +000559 0681 1FB8 0100 STR$CHOMP ADD 0x0100, R14 ; Get a new register page +000560 0683 0800 MOVE R8, R0 ; Save the start address of the string +000561 0684 0904 MOVE R9, R1 ; R9 will be used later +000562 0685 0808 MOVE R8, R2 ; R2 will be used as a working pointer +000563 0686 FFB0 FFEC RSUB STR$LEN, 1 ; Determine the length of the string +000564 0688 0924 MOVE R9, R9 ; Is the string empty? +000565 0689 FFA3 0011 RBRA _STR$CHOMP_EXIT, Z ; Yes +000566 068B 1908 ADD R9, R2 ; R2 now points to the last string character +000567 068C 02CC MOVE @--R2, R3 ; Get a character +000568 068D 3F8C 000D SUB 0x000D, R3 ; Is it a CR (we are working from right!) +000569 068F FFAB 0004 RBRA _STR$CHOMP_1, !Z ; No, so nothing to do so far +000570 0691 0F89 0000 MOVE 0x0000, @R2 ; Yes, replace it with a null word +000571 0693 3F88 0001 SUB 0x0001, R2 ; Proceed to second last character +000572 0695 024C _STR$CHOMP_1 MOVE @R2, R3 ; Now test for a line feed +000573 0696 3F8C 000A SUB 0x000A, R3 +000574 0698 FFAB 0002 RBRA _STR$CHOMP_EXIT, !Z ; Nothing to do +000575 069A 0F89 0000 MOVE 0x0000, @R2 ; Replace the LF with a null word +000576 069C 0124 _STR$CHOMP_EXIT MOVE R1, R9 ; Restore R9 +000577 069D 3FB8 0100 SUB 0x0100, R14 ; Restore register bank +000578 069F 0DBC MOVE @R13++, R15 +000579 ; +000580 ;*************************************************************************************** +000581 ;* STR$CMP compares two strings +000582 ;* +000583 ;* R8: Pointer to the first string (S0), +000584 ;* R9: Pointer to the second string (S1), +000585 ;* +000586 ;* R10: negative if (S0 < S1), zero if (S0 == S1), positive if (S0 > S1) +000587 ; +000588 ;* The contents of R8 and R9 are being preserved during the run of this function +000589 ;*************************************************************************************** +000590 ; +000591 06A0 1FB8 0100 STR$CMP ADD 0x0100, R14 ; Get a new register page +000592 06A2 0800 MOVE R8, R0 ; Save arguments +000593 06A3 0904 MOVE R9, R1 +000594 06A4 0068 _STR$CMP_LOOP MOVE @R0, R10 ; while (*s1 == *s2++) +000595 06A5 0188 MOVE @R1++, R2 +000596 06A6 3A08 SUB R10, R2 +000597 06A7 FFAB 0005 RBRA _STR$CMP_END, !Z +000598 06A9 00A8 MOVE @R0++, R10 ; if (*s1++ == 0) +000599 06AA FFA3 0004 RBRA _STR$CMP_EXIT, Z ; return 0; +000600 06AC FFA0 FFF6 RBRA _STR$CMP_LOOP, 1 ; end-of-while-loop +000601 06AE 01C8 _STR$CMP_END MOVE @--R1, R2 ; return (*s1 - (--*s2)); +000602 06AF 3228 SUB R2, R10 +000603 06B0 3FB8 0100 _STR$CMP_EXIT SUB 0x0100, R14 ; Restore previous register page +000604 06B2 0DBC MOVE @R13++, R15 +000605 ; +000606 ;*************************************************************************************** +000607 ;* STR$STRCHR seaches for the first occurrence of the character stored in R8 in a +000608 ;* string pointed to by R9. +000609 ;* +000610 ;* R8: Pointer to the string +000611 ;* R9: Character to be searched +000612 ;* +000613 ;* R10: Zero if the character has not been found, otherwise it contains a pointer +000614 ;* to the first occurrence of the character in the string +000615 ; +000616 ;* The contents of R8 and R9 are being preserved during the run of this function +000617 ;*************************************************************************************** +000618 ; +000619 06B3 1FB8 0100 STR$STRCHR ADD 0x0100, R14 +000620 06B5 0900 MOVE R9, R0 +000621 06B6 BA28 XOR R10, R10 +000622 06B7 CF81 0000 _STR$STRCHR_LOOP CMP 0x0000, @R0 ; while (*string) +000623 06B9 FFA3 000A RBRA _STR$STRCHR_EXIT, Z +000624 06BB C801 CMP R8, @R0 ; if (*string == R8) +000625 06BC FFAB 0003 RBRA _STR$STRCHR_NEXT, !Z +000626 06BE 0028 MOVE R0, R10 +000627 06BF FFA0 0004 RBRA _STR$STRCHR_EXIT, 1 +000628 06C1 1F80 0001 _STR$STRCHR_NEXT ADD 0x0001, R0 ; string++ +000629 06C3 FFA0 FFF2 RBRA _STR$STRCHR_LOOP, 1 +000630 06C5 3FB8 0100 _STR$STRCHR_EXIT SUB 0x0100, R14 +000631 06C7 0DBC MOVE @R13++, R15 +000632 +000633 ; +000634 ;;============================================================================= +000635 ;; The collection of memory related functions starts here +000636 ;;============================================================================= +000637 ; +000638 ;****************************************************************************** +000639 ;* MEM$FILL fills a block of memory running from the address stored in R8. +000640 ;* R9 contains the number of words to be written. R10 contains the value to +000641 ;* be stored in the memory area. +000642 ;****************************************************************************** +000643 06C8 1FB8 0100 MEM$FILL ADD 0x0100, R14 +000644 06CA 0800 MOVE R8, R0 +000645 06CB 0904 MOVE R9, R1 +000646 06CC 0104 _MEM$FILL_LOOP MOVE R1, R1 ; Zero length left? +000647 06CD FFA3 0005 RBRA _MEM$FILL_EXIT, Z ; Yes, done... +000648 06CF 0A02 MOVE R10, @R0++ +000649 06D0 3F84 0001 SUB 0x0001, R1 +000650 06D2 FFA0 FFF8 RBRA _MEM$FILL_LOOP, 1 +000651 06D4 3FB8 0100 _MEM$FILL_EXIT SUB 0x0100, R14 +000652 06D6 0DBC MOVE @R13++, R15 +000653 ; +000654 ;****************************************************************************** +000655 ;* MEM$MOVE moves the memory area starting at the address contained in R8 +000656 ;* to the area starting at the address contained in R9. R10 contains the +000657 ;* number of words to be moved. +000658 ;****************************************************************************** +000659 ; +000660 06D7 1FB8 0100 MEM$MOVE ADD 0x0100, R14 +000661 06D9 0800 MOVE R8, R0 +000662 06DA 0904 MOVE R9, R1 +000663 06DB 0A08 MOVE R10, R2 +000664 06DC 0208 _MEM$MOVE_LOOP MOVE R2, R2 ; Zero length left? +000665 06DD FFA3 0005 RBRA _MEM$MOVE_EXIT, Z ; Yes, done... +000666 06DF 0086 MOVE @R0++, @R1++ +000667 06E0 3F88 0001 SUB 0x0001, R2 +000668 06E2 FFA0 FFF8 RBRA _MEM$MOVE_LOOP, 1 +000669 06E4 3FB8 0100 _MEM$MOVE_EXIT SUB 0x0100, R14 +000670 06E6 0DBC MOVE @R13++, R15 +000671 ;; +000672 06E7 E000 QMON$LAST_ADDR HALT +000673 + + +EQU-list: +-------------------------------------------------------------------------------------------------------- +IO$BASE : 0xFC00 IO$UART0_BASE : 0xFC00 IO$UART_SRA : 0x0001 +IO$UART_RHRA : 0x0003 IO$UART_THRA : 0x0003 + +Label-list: +-------------------------------------------------------------------------------------------------------- +QMON$COLDSTART : 0x0000 QMON$WARMSTART : 0x0012 QMON$MAIN_LOOP : 0x0018 +QMON$C_MAYBE_H : 0x0038 QMON$MAYBE_R : 0x0041 QMON$C_ILLEGAL : 0x004E +QMON$MAYBE_M : 0x0054 QMON$M_MAYBE_D : 0x007D QMON$M_MAYBE_E : 0x0098 +QMON$M_MAYBE_F : 0x00AE QMON$M_MAYBE_L : 0x00D2 _QMON$ML_LOOP : 0x00DA +QMON$M_MAYBE_M : 0x00E2 QMON$M_ILLEGAL : 0x0103 QMON$MAYBE_H : 0x0109 +QMON$NOT_H : 0x0113 QMON$WELCOME : 0x0119 QMON$PROMPT : 0x01A0 +QMON$ILLCMDGRP : 0x01A7 QMON$ILLCMD : 0x01C8 QMON$HELP : 0x01E3 +QMON$CG_C : 0x035A QMON$CG_C_C : 0x0362 QMON$CG_C_H : 0x036D +QMON$CG_C_R : 0x0376 QMON$CG_M : 0x0383 QMON$CG_M_C : 0x038A +QMON$CG_M_C1 : 0x039A QMON$CG_M_C2 : 0x03AA QMON$CG_M_D : 0x03B6 +QMON$CG_M_D2 : 0x03CA QMON$CG_M_E : 0x03D8 QMON$CG_M_F : 0x03E9 +QMON$CG_M_F2 : 0x03FD QMON$CG_M_F3 : 0x040B QMON$CG_M_L : 0x0413 +QMON$CG_M_M : 0x044D QMON$CG_M_M2 : 0x0458 QMON$CG_M_M3 : 0x045D +QMON$COMMAND : 0x0466 IO$DUMP_MEMORY : 0x0566 _IO$DUMP_MEMORY_LOOP : 0x056F +_IO$DUMP_MEMORY_CONTENT : 0x0582 _IO$DUMP_MEMORY_EXIT : 0x058B IO$GET_W_HEX : 0x0591 +_IO$GET_W_HEX_REDO : 0x0593 _IO$GET_W_HEX_INPUT : 0x0598 _IO$GET_W_HEX_VALID : 0x05AD +IO$PUT_W_HEX : 0x05BC _IO$PWH_SCAN : 0x05C4 _IO$PWH_PRINT : 0x05D1 +IO$GETCHAR : 0x05DC _IO$GETC_LOOP : 0x05E5 IO$GETS : 0x05F2 +_IO$GETS_LOOP : 0x05F6 IO$PUTS : 0x0607 _IO$PUTS_LOOP : 0x060B +_IO$PUTS_END : 0x0614 IO$PUT_CRLF : 0x0618 IO$PUTCHAR : 0x0627 +IO$HEX_NIBBLES : 0x0631 IO$COLON_DELIMITER : 0x0642 CHR$TO_UPPER : 0x0645 +_CHR$TO_UPPER_EXIT : 0x0655 STR$TO_UPPER : 0x0658 _STR$TO_UPPER_LOOP : 0x065B +_STR$TO_UPPER_NEXT : 0x066D _STR$TO_UPPER_END : 0x0671 STR$LEN : 0x0674 +_STR$LEN_LOOP : 0x0679 STR$CHOMP : 0x0681 _STR$CHOMP_1 : 0x0695 +_STR$CHOMP_EXIT : 0x069C STR$CMP : 0x06A0 _STR$CMP_LOOP : 0x06A4 +_STR$CMP_END : 0x06AE _STR$CMP_EXIT : 0x06B0 STR$STRCHR : 0x06B3 +_STR$STRCHR_LOOP : 0x06B7 _STR$STRCHR_NEXT : 0x06C1 _STR$STRCHR_EXIT : 0x06C5 +MEM$FILL : 0x06C8 _MEM$FILL_LOOP : 0x06CC _MEM$FILL_EXIT : 0x06D4 +MEM$MOVE : 0x06D7 _MEM$MOVE_LOOP : 0x06DC _MEM$MOVE_EXIT : 0x06E4 +QMON$LAST_ADDR : 0x06E7 diff --git a/monitor/monitor.out b/monitor/monitor.out new file mode 100644 index 00000000..18b6df47 --- /dev/null +++ b/monitor/monitor.out @@ -0,0 +1,1768 @@ +0x0000 0x0FB4 +0x0001 0xFC00 +0x0002 0x0FA0 +0x0003 0x0119 +0x0004 0xFFB0 +0x0005 0x0601 +0x0006 0x0FA0 +0x0007 0x06E7 +0x0008 0x1FA0 +0x0009 0x0001 +0x000A 0x0FA4 +0x000B 0xFC00 +0x000C 0x3824 +0x000D 0x3FA4 +0x000E 0x0001 +0x000F 0xBA28 +0x0010 0xFFB0 +0x0011 0x06B6 +0x0012 0x0FB4 +0x0013 0xFC00 +0x0014 0x9FB8 +0x0015 0x00FF +0x0016 0xFFB0 +0x0017 0x0600 +0x0018 0x0FA0 +0x0019 0x01A0 +0x001A 0xFFB0 +0x001B 0x05EB +0x001C 0xFFB0 +0x001D 0x05BE +0x001E 0xFFB0 +0x001F 0x0625 +0x0020 0xFFB0 +0x0021 0x0605 +0x0022 0xCFA0 +0x0023 0x0043 +0x0024 0xFFAB +0x0025 0x002E +0x0026 0x0FA0 +0x0027 0x035A +0x0028 0xFFB0 +0x0029 0x05DD +0x002A 0xFFB0 +0x002B 0x05B0 +0x002C 0xFFB0 +0x002D 0x0617 +0x002E 0xCFA0 +0x002F 0x0043 +0x0030 0xFFAB +0x0031 0x0006 +0x0032 0x0FA0 +0x0033 0x0362 +0x0034 0xFFB0 +0x0035 0x05D1 +0x0036 0xFFA0 +0x0037 0xFFC8 +0x0038 0xCFA0 +0x0039 0x0048 +0x003A 0xFFAB +0x003B 0x0005 +0x003C 0x0FA0 +0x003D 0x036D +0x003E 0xFFB0 +0x003F 0x05C7 +0x0040 0xE000 +0x0041 0xCFA0 +0x0042 0x0052 +0x0043 0xFFAB +0x0044 0x0009 +0x0045 0x0FA0 +0x0046 0x0376 +0x0047 0xFFB0 +0x0048 0x05BE +0x0049 0xFFB0 +0x004A 0x0546 +0x004B 0xFFB0 +0x004C 0x05CB +0x004D 0xF800 +0x004E 0x0FA0 +0x004F 0x01C8 +0x0050 0xFFB0 +0x0051 0x05B5 +0x0052 0xFFA0 +0x0053 0xFFC4 +0x0054 0xCFA0 +0x0055 0x004D +0x0056 0xFFAB +0x0057 0x00B1 +0x0058 0x0FA0 +0x0059 0x0383 +0x005A 0xFFB0 +0x005B 0x05AB +0x005C 0xFFB0 +0x005D 0x057E +0x005E 0xFFB0 +0x005F 0x05E5 +0x0060 0xCFA0 +0x0061 0x0043 +0x0062 0xFFAB +0x0063 0x0019 +0x0064 0x0FA0 +0x0065 0x038A +0x0066 0xFFB0 +0x0067 0x059F +0x0068 0xFFB0 +0x0069 0x0527 +0x006A 0x0800 +0x006B 0x0FA0 +0x006C 0x039A +0x006D 0xFFB0 +0x006E 0x0598 +0x006F 0x0060 +0x0070 0xFFB0 +0x0071 0x054A +0x0072 0x0FA0 +0x0073 0x03AA +0x0074 0xFFB0 +0x0075 0x0591 +0x0076 0xFFB0 +0x0077 0x0519 +0x0078 0x0801 +0x0079 0xFFB0 +0x007A 0x059D +0x007B 0xFFA0 +0x007C 0xFF9B +0x007D 0xCFA0 +0x007E 0x0044 +0x007F 0xFFAB +0x0080 0x0017 +0x0081 0x0FA0 +0x0082 0x03B6 +0x0083 0xFFB0 +0x0084 0x0582 +0x0085 0xFFB0 +0x0086 0x050A +0x0087 0x0800 +0x0088 0x0FA0 +0x0089 0x03CA +0x008A 0xFFB0 +0x008B 0x057B +0x008C 0xFFB0 +0x008D 0x0503 +0x008E 0xFFB0 +0x008F 0x0588 +0x0090 0x0824 +0x0091 0x0020 +0x0092 0xFFB0 +0x0093 0x04D2 +0x0094 0xFFB0 +0x0095 0x0582 +0x0096 0xFFA0 +0x0097 0xFF80 +0x0098 0xCFA0 +0x0099 0x0045 +0x009A 0xFFAB +0x009B 0x0012 +0x009C 0x0FA0 +0x009D 0x03D8 +0x009E 0xFFB0 +0x009F 0x0567 +0x00A0 0xFFB0 +0x00A1 0x04EF +0x00A2 0x0800 +0x00A3 0x0FA0 +0x00A4 0x0020 +0x00A5 0xFFB0 +0x00A6 0x0580 +0x00A7 0x0060 +0x00A8 0xFFB0 +0x00A9 0x0512 +0x00AA 0xFFB0 +0x00AB 0x056C +0x00AC 0xFFA0 +0x00AD 0xFF6A +0x00AE 0xCFA0 +0x00AF 0x0046 +0x00B0 0xFFAB +0x00B1 0x0020 +0x00B2 0x0FA0 +0x00B3 0x03E9 +0x00B4 0xFFB0 +0x00B5 0x0551 +0x00B6 0xFFB0 +0x00B7 0x04D9 +0x00B8 0x0800 +0x00B9 0x0FA0 +0x00BA 0x03FD +0x00BB 0xFFB0 +0x00BC 0x054A +0x00BD 0xFFB0 +0x00BE 0x04D2 +0x00BF 0x0804 +0x00C0 0x0FA0 +0x00C1 0x040B +0x00C2 0xFFB0 +0x00C3 0x0543 +0x00C4 0xFFB0 +0x00C5 0x04CB +0x00C6 0x0828 +0x00C7 0x0020 +0x00C8 0x3004 +0x00C9 0x1F84 +0x00CA 0x0001 +0x00CB 0x0124 +0x00CC 0xFFB0 +0x00CD 0x05FA +0x00CE 0xFFB0 +0x00CF 0x0548 +0x00D0 0xFFA0 +0x00D1 0xFF46 +0x00D2 0xCFA0 +0x00D3 0x004C +0x00D4 0xFFAB +0x00D5 0x000C +0x00D6 0x0FA0 +0x00D7 0x0413 +0x00D8 0xFFB0 +0x00D9 0x052D +0x00DA 0xFFB0 +0x00DB 0x04B5 +0x00DC 0x0800 +0x00DD 0xFFB0 +0x00DE 0x04B2 +0x00DF 0x0801 +0x00E0 0xFFA0 +0x00E1 0xFFF8 +0x00E2 0xCFA0 +0x00E3 0x004D +0x00E4 0xFFAB +0x00E5 0x001D +0x00E6 0x0FA0 +0x00E7 0x044D +0x00E8 0xFFB0 +0x00E9 0x051D +0x00EA 0xFFB0 +0x00EB 0x04A5 +0x00EC 0x0800 +0x00ED 0x0FA0 +0x00EE 0x0458 +0x00EF 0xFFB0 +0x00F0 0x0516 +0x00F1 0xFFB0 +0x00F2 0x049E +0x00F3 0x0804 +0x00F4 0x0FA0 +0x00F5 0x045D +0x00F6 0xFFB0 +0x00F7 0x050F +0x00F8 0xFFB0 +0x00F9 0x0497 +0x00FA 0x0828 +0x00FB 0x0020 +0x00FC 0x0124 +0x00FD 0xFFB0 +0x00FE 0x05D8 +0x00FF 0xFFB0 +0x0100 0x0517 +0x0101 0xFFA0 +0x0102 0xFF15 +0x0103 0x0FA0 +0x0104 0x01C8 +0x0105 0xFFB0 +0x0106 0x0500 +0x0107 0xFFA0 +0x0108 0xFF0F +0x0109 0xCFA0 +0x010A 0x0048 +0x010B 0xFFAB +0x010C 0x0006 +0x010D 0x0FA0 +0x010E 0x01E3 +0x010F 0xFFB0 +0x0110 0x04F6 +0x0111 0xFFA0 +0x0112 0xFF05 +0x0113 0x0FA0 +0x0114 0x01A7 +0x0115 0xFFB0 +0x0116 0x04F0 +0x0117 0xFFA0 +0x0118 0xFEFF +0x0119 0x000D +0x011A 0x000A +0x011B 0x000D +0x011C 0x000A +0x011D 0x0053 +0x011E 0x0069 +0x011F 0x006D +0x0120 0x0070 +0x0121 0x006C +0x0122 0x0065 +0x0123 0x0020 +0x0124 0x0051 +0x0125 0x004E +0x0126 0x0049 +0x0127 0x0043 +0x0128 0x0045 +0x0129 0x002D +0x012A 0x006D +0x012B 0x006F +0x012C 0x006E +0x012D 0x0069 +0x012E 0x0074 +0x012F 0x006F +0x0130 0x0072 +0x0131 0x0020 +0x0132 0x002D +0x0133 0x0020 +0x0134 0x0056 +0x0135 0x0065 +0x0136 0x0072 +0x0137 0x0073 +0x0138 0x0069 +0x0139 0x006F +0x013A 0x006E +0x013B 0x0020 +0x013C 0x0030 +0x013D 0x002E +0x013E 0x0032 +0x013F 0x0020 +0x0140 0x0028 +0x0141 0x0042 +0x0142 0x0065 +0x0143 0x0072 +0x0144 0x006E +0x0145 0x0064 +0x0146 0x0020 +0x0147 0x0055 +0x0148 0x006C +0x0149 0x006D +0x014A 0x0061 +0x014B 0x006E +0x014C 0x006E +0x014D 0x002C +0x014E 0x0020 +0x014F 0x0041 +0x0150 0x0075 +0x0151 0x0067 +0x0152 0x0075 +0x0153 0x0073 +0x0154 0x0074 +0x0155 0x0020 +0x0156 0x0032 +0x0157 0x0030 +0x0158 0x0031 +0x0159 0x0035 +0x015A 0x0029 +0x015B 0x000D +0x015C 0x000A +0x015D 0x002D +0x015E 0x002D +0x015F 0x002D +0x0160 0x002D +0x0161 0x002D +0x0162 0x002D +0x0163 0x002D +0x0164 0x002D +0x0165 0x002D +0x0166 0x002D +0x0167 0x002D +0x0168 0x002D +0x0169 0x002D +0x016A 0x002D +0x016B 0x002D +0x016C 0x002D +0x016D 0x002D +0x016E 0x002D +0x016F 0x002D +0x0170 0x002D +0x0171 0x002D +0x0172 0x002D +0x0173 0x002D +0x0174 0x002D +0x0175 0x002D +0x0176 0x002D +0x0177 0x002D +0x0178 0x002D +0x0179 0x002D +0x017A 0x002D +0x017B 0x002D +0x017C 0x002D +0x017D 0x002D +0x017E 0x002D +0x017F 0x002D +0x0180 0x002D +0x0181 0x002D +0x0182 0x002D +0x0183 0x002D +0x0184 0x002D +0x0185 0x002D +0x0186 0x002D +0x0187 0x002D +0x0188 0x002D +0x0189 0x002D +0x018A 0x002D +0x018B 0x002D +0x018C 0x002D +0x018D 0x002D +0x018E 0x002D +0x018F 0x002D +0x0190 0x002D +0x0191 0x002D +0x0192 0x002D +0x0193 0x002D +0x0194 0x002D +0x0195 0x002D +0x0196 0x002D +0x0197 0x002D +0x0198 0x002D +0x0199 0x002D +0x019A 0x002D +0x019B 0x000D +0x019C 0x000A +0x019D 0x000D +0x019E 0x000A +0x019F 0x0000 +0x01A0 0x0051 +0x01A1 0x004D +0x01A2 0x004F +0x01A3 0x004E +0x01A4 0x003E +0x01A5 0x0020 +0x01A6 0x0000 +0x01A7 0x0020 +0x01A8 0x002A +0x01A9 0x002A +0x01AA 0x002A +0x01AB 0x0020 +0x01AC 0x0049 +0x01AD 0x006C +0x01AE 0x006C +0x01AF 0x0065 +0x01B0 0x0067 +0x01B1 0x0061 +0x01B2 0x006C +0x01B3 0x0020 +0x01B4 0x0063 +0x01B5 0x006F +0x01B6 0x006D +0x01B7 0x006D +0x01B8 0x0061 +0x01B9 0x006E +0x01BA 0x0064 +0x01BB 0x0020 +0x01BC 0x0067 +0x01BD 0x0072 +0x01BE 0x006F +0x01BF 0x0075 +0x01C0 0x0070 +0x01C1 0x0020 +0x01C2 0x002A +0x01C3 0x002A +0x01C4 0x002A +0x01C5 0x000D +0x01C6 0x000A +0x01C7 0x0000 +0x01C8 0x0020 +0x01C9 0x002A +0x01CA 0x002A +0x01CB 0x002A +0x01CC 0x0020 +0x01CD 0x0049 +0x01CE 0x006C +0x01CF 0x006C +0x01D0 0x0065 +0x01D1 0x0067 +0x01D2 0x0061 +0x01D3 0x006C +0x01D4 0x0020 +0x01D5 0x0063 +0x01D6 0x006F +0x01D7 0x006D +0x01D8 0x006D +0x01D9 0x0061 +0x01DA 0x006E +0x01DB 0x0064 +0x01DC 0x0020 +0x01DD 0x002A +0x01DE 0x002A +0x01DF 0x002A +0x01E0 0x000D +0x01E1 0x000A +0x01E2 0x0000 +0x01E3 0x0045 +0x01E4 0x004C +0x01E5 0x0050 +0x01E6 0x003A +0x01E7 0x000D +0x01E8 0x000A +0x01E9 0x000D +0x01EA 0x000A +0x01EB 0x0020 +0x01EC 0x0020 +0x01ED 0x0020 +0x01EE 0x0020 +0x01EF 0x0043 +0x01F0 0x0028 +0x01F1 0x0063 +0x01F2 0x006F +0x01F3 0x006E +0x01F4 0x0074 +0x01F5 0x0072 +0x01F6 0x006F +0x01F7 0x006C +0x01F8 0x0020 +0x01F9 0x0067 +0x01FA 0x0072 +0x01FB 0x006F +0x01FC 0x0075 +0x01FD 0x0070 +0x01FE 0x0029 +0x01FF 0x003A +0x0200 0x000D +0x0201 0x000A +0x0202 0x0020 +0x0203 0x0020 +0x0204 0x0020 +0x0205 0x0020 +0x0206 0x0020 +0x0207 0x0020 +0x0208 0x0020 +0x0209 0x0020 +0x020A 0x0043 +0x020B 0x0028 +0x020C 0x006F +0x020D 0x006C +0x020E 0x0064 +0x020F 0x0020 +0x0210 0x0073 +0x0211 0x0074 +0x0212 0x0061 +0x0213 0x0072 +0x0214 0x0074 +0x0215 0x0029 +0x0216 0x0020 +0x0217 0x0048 +0x0218 0x0028 +0x0219 0x0061 +0x021A 0x006C +0x021B 0x0074 +0x021C 0x0029 +0x021D 0x0020 +0x021E 0x0052 +0x021F 0x0028 +0x0220 0x0075 +0x0221 0x006E +0x0222 0x0029 +0x0223 0x000D +0x0224 0x000A +0x0225 0x0020 +0x0226 0x0020 +0x0227 0x0020 +0x0228 0x0020 +0x0229 0x0048 +0x022A 0x0028 +0x022B 0x0065 +0x022C 0x006C +0x022D 0x0070 +0x022E 0x0029 +0x022F 0x000D +0x0230 0x000A +0x0231 0x0020 +0x0232 0x0020 +0x0233 0x0020 +0x0234 0x0020 +0x0235 0x004D +0x0236 0x0028 +0x0237 0x0065 +0x0238 0x006D +0x0239 0x006F +0x023A 0x0072 +0x023B 0x0079 +0x023C 0x0020 +0x023D 0x0067 +0x023E 0x0072 +0x023F 0x006F +0x0240 0x0075 +0x0241 0x0070 +0x0242 0x0029 +0x0243 0x003A +0x0244 0x000D +0x0245 0x000A +0x0246 0x0020 +0x0247 0x0020 +0x0248 0x0020 +0x0249 0x0020 +0x024A 0x0020 +0x024B 0x0020 +0x024C 0x0020 +0x024D 0x0020 +0x024E 0x0043 +0x024F 0x0028 +0x0250 0x0068 +0x0251 0x0061 +0x0252 0x006E +0x0253 0x0067 +0x0254 0x0065 +0x0255 0x0029 +0x0256 0x0020 +0x0257 0x0044 +0x0258 0x0028 +0x0259 0x0075 +0x025A 0x006D +0x025B 0x0070 +0x025C 0x0029 +0x025D 0x0020 +0x025E 0x0045 +0x025F 0x0028 +0x0260 0x0078 +0x0261 0x0061 +0x0262 0x006D +0x0263 0x0069 +0x0264 0x006E +0x0265 0x0065 +0x0266 0x0029 +0x0267 0x0020 +0x0268 0x0046 +0x0269 0x0028 +0x026A 0x0069 +0x026B 0x006C +0x026C 0x006C +0x026D 0x0029 +0x026E 0x0020 +0x026F 0x004C +0x0270 0x0028 +0x0271 0x006F +0x0272 0x0061 +0x0273 0x0064 +0x0274 0x0029 +0x0275 0x0020 +0x0276 0x004D +0x0277 0x0028 +0x0278 0x006F +0x0279 0x0076 +0x027A 0x0065 +0x027B 0x0029 +0x027C 0x000D +0x027D 0x000A +0x027E 0x000D +0x027F 0x000A +0x0280 0x0020 +0x0281 0x0020 +0x0282 0x0020 +0x0283 0x0020 +0x0284 0x0047 +0x0285 0x0065 +0x0286 0x006E +0x0287 0x0065 +0x0288 0x0072 +0x0289 0x0061 +0x028A 0x006C +0x028B 0x003A +0x028C 0x0020 +0x028D 0x0043 +0x028E 0x0054 +0x028F 0x0052 +0x0290 0x004C +0x0291 0x002D +0x0292 0x0045 +0x0293 0x0020 +0x0294 0x0070 +0x0295 0x0065 +0x0296 0x0072 +0x0297 0x0066 +0x0298 0x006F +0x0299 0x0072 +0x029A 0x006D +0x029B 0x0073 +0x029C 0x0020 +0x029D 0x0061 +0x029E 0x0020 +0x029F 0x0077 +0x02A0 0x0061 +0x02A1 0x0072 +0x02A2 0x006D +0x02A3 0x0020 +0x02A4 0x0073 +0x02A5 0x0074 +0x02A6 0x0061 +0x02A7 0x0072 +0x02A8 0x0074 +0x02A9 0x0020 +0x02AA 0x0077 +0x02AB 0x0068 +0x02AC 0x0065 +0x02AD 0x006E +0x02AE 0x0065 +0x02AF 0x0076 +0x02B0 0x0065 +0x02B1 0x0072 +0x02B2 0x0020 +0x02B3 0x0061 +0x02B4 0x006E +0x02B5 0x000D +0x02B6 0x000A +0x02B7 0x0020 +0x02B8 0x0020 +0x02B9 0x0020 +0x02BA 0x0020 +0x02BB 0x0020 +0x02BC 0x0020 +0x02BD 0x0020 +0x02BE 0x0020 +0x02BF 0x0069 +0x02C0 0x006E +0x02C1 0x0070 +0x02C2 0x0075 +0x02C3 0x0074 +0x02C4 0x0020 +0x02C5 0x0066 +0x02C6 0x0072 +0x02C7 0x006F +0x02C8 0x006D +0x02C9 0x0020 +0x02CA 0x006B +0x02CB 0x0065 +0x02CC 0x0079 +0x02CD 0x0062 +0x02CE 0x006F +0x02CF 0x0061 +0x02D0 0x0072 +0x02D1 0x0064 +0x02D2 0x0020 +0x02D3 0x0069 +0x02D4 0x0073 +0x02D5 0x0020 +0x02D6 0x0065 +0x02D7 0x0078 +0x02D8 0x0070 +0x02D9 0x0065 +0x02DA 0x0063 +0x02DB 0x0074 +0x02DC 0x0065 +0x02DD 0x0064 +0x02DE 0x002E +0x02DF 0x000D +0x02E0 0x000A +0x02E1 0x000D +0x02E2 0x000A +0x02E3 0x0020 +0x02E4 0x0020 +0x02E5 0x0020 +0x02E6 0x0020 +0x02E7 0x004D +0x02E8 0x0028 +0x02E9 0x0065 +0x02EA 0x006D +0x02EB 0x006F +0x02EC 0x0072 +0x02ED 0x0079 +0x02EE 0x0029 +0x02EF 0x004C +0x02F0 0x0028 +0x02F1 0x006F +0x02F2 0x0061 +0x02F3 0x0064 +0x02F4 0x0029 +0x02F5 0x0020 +0x02F6 0x0063 +0x02F7 0x0061 +0x02F8 0x006E +0x02F9 0x0020 +0x02FA 0x0062 +0x02FB 0x0065 +0x02FC 0x0020 +0x02FD 0x0075 +0x02FE 0x0073 +0x02FF 0x0065 +0x0300 0x0064 +0x0301 0x0020 +0x0302 0x0074 +0x0303 0x006F +0x0304 0x0020 +0x0305 0x006C +0x0306 0x006F +0x0307 0x0061 +0x0308 0x0064 +0x0309 0x0020 +0x030A 0x0061 +0x030B 0x0073 +0x030C 0x0073 +0x030D 0x0065 +0x030E 0x006D +0x030F 0x0062 +0x0310 0x006C +0x0311 0x0065 +0x0312 0x0072 +0x0313 0x0020 +0x0314 0x006F +0x0315 0x0075 +0x0316 0x0074 +0x0317 0x0070 +0x0318 0x0075 +0x0319 0x0074 +0x031A 0x000D +0x031B 0x000A +0x031C 0x0020 +0x031D 0x0020 +0x031E 0x0020 +0x031F 0x0020 +0x0320 0x0020 +0x0321 0x0020 +0x0322 0x0020 +0x0323 0x0020 +0x0324 0x0062 +0x0325 0x0079 +0x0326 0x0020 +0x0327 0x0070 +0x0328 0x0061 +0x0329 0x0073 +0x032A 0x0074 +0x032B 0x0069 +0x032C 0x006E +0x032D 0x0067 +0x032E 0x0020 +0x032F 0x0069 +0x0330 0x0074 +0x0331 0x0020 +0x0332 0x0074 +0x0333 0x006F +0x0334 0x0020 +0x0335 0x0074 +0x0336 0x0068 +0x0337 0x0065 +0x0338 0x0020 +0x0339 0x0074 +0x033A 0x0065 +0x033B 0x0072 +0x033C 0x006D +0x033D 0x0069 +0x033E 0x006E +0x033F 0x0061 +0x0340 0x006C +0x0341 0x002E +0x0342 0x0020 +0x0343 0x0043 +0x0344 0x0054 +0x0345 0x0052 +0x0346 0x004C +0x0347 0x002D +0x0348 0x0045 +0x0349 0x0020 +0x034A 0x0074 +0x034B 0x0065 +0x034C 0x0072 +0x034D 0x006D +0x034E 0x0069 +0x034F 0x006E +0x0350 0x0061 +0x0351 0x0074 +0x0352 0x0065 +0x0353 0x0073 +0x0354 0x002E +0x0355 0x000D +0x0356 0x000A +0x0357 0x000D +0x0358 0x000A +0x0359 0x0000 +0x035A 0x004F +0x035B 0x004E +0x035C 0x0054 +0x035D 0x0052 +0x035E 0x004F +0x035F 0x004C +0x0360 0x002F +0x0361 0x0000 +0x0362 0x0043 +0x0363 0x004F +0x0364 0x004C +0x0365 0x0044 +0x0366 0x0020 +0x0367 0x0053 +0x0368 0x0054 +0x0369 0x0041 +0x036A 0x0052 +0x036B 0x0054 +0x036C 0x0000 +0x036D 0x0048 +0x036E 0x0041 +0x036F 0x004C +0x0370 0x0054 +0x0371 0x000D +0x0372 0x000A +0x0373 0x000D +0x0374 0x000A +0x0375 0x0000 +0x0376 0x0052 +0x0377 0x0055 +0x0378 0x004E +0x0379 0x0020 +0x037A 0x0041 +0x037B 0x0044 +0x037C 0x0044 +0x037D 0x0052 +0x037E 0x0045 +0x037F 0x0053 +0x0380 0x0053 +0x0381 0x003D +0x0382 0x0000 +0x0383 0x0045 +0x0384 0x004D +0x0385 0x004F +0x0386 0x0052 +0x0387 0x0059 +0x0388 0x002F +0x0389 0x0000 +0x038A 0x0043 +0x038B 0x0048 +0x038C 0x0041 +0x038D 0x004E +0x038E 0x0047 +0x038F 0x0045 +0x0390 0x0020 +0x0391 0x0041 +0x0392 0x0044 +0x0393 0x0044 +0x0394 0x0052 +0x0395 0x0045 +0x0396 0x0053 +0x0397 0x0053 +0x0398 0x003D +0x0399 0x0000 +0x039A 0x0020 +0x039B 0x0043 +0x039C 0x0055 +0x039D 0x0052 +0x039E 0x0052 +0x039F 0x0045 +0x03A0 0x004E +0x03A1 0x0054 +0x03A2 0x0020 +0x03A3 0x0056 +0x03A4 0x0041 +0x03A5 0x004C +0x03A6 0x0055 +0x03A7 0x0045 +0x03A8 0x003D +0x03A9 0x0000 +0x03AA 0x0020 +0x03AB 0x004E +0x03AC 0x0045 +0x03AD 0x0057 +0x03AE 0x0020 +0x03AF 0x0056 +0x03B0 0x0041 +0x03B1 0x004C +0x03B2 0x0055 +0x03B3 0x0045 +0x03B4 0x003D +0x03B5 0x0000 +0x03B6 0x0044 +0x03B7 0x0055 +0x03B8 0x004D +0x03B9 0x0050 +0x03BA 0x0020 +0x03BB 0x0053 +0x03BC 0x0054 +0x03BD 0x0041 +0x03BE 0x0052 +0x03BF 0x0054 +0x03C0 0x0020 +0x03C1 0x0041 +0x03C2 0x0044 +0x03C3 0x0044 +0x03C4 0x0052 +0x03C5 0x0045 +0x03C6 0x0053 +0x03C7 0x0053 +0x03C8 0x003D +0x03C9 0x0000 +0x03CA 0x0020 +0x03CB 0x0045 +0x03CC 0x004E +0x03CD 0x0044 +0x03CE 0x0020 +0x03CF 0x0041 +0x03D0 0x0044 +0x03D1 0x0044 +0x03D2 0x0052 +0x03D3 0x0045 +0x03D4 0x0053 +0x03D5 0x0053 +0x03D6 0x003D +0x03D7 0x0000 +0x03D8 0x0045 +0x03D9 0x0058 +0x03DA 0x0041 +0x03DB 0x004D +0x03DC 0x0049 +0x03DD 0x004E +0x03DE 0x0045 +0x03DF 0x0020 +0x03E0 0x0041 +0x03E1 0x0044 +0x03E2 0x0044 +0x03E3 0x0052 +0x03E4 0x0045 +0x03E5 0x0053 +0x03E6 0x0053 +0x03E7 0x003D +0x03E8 0x0000 +0x03E9 0x0046 +0x03EA 0x0049 +0x03EB 0x004C +0x03EC 0x004C +0x03ED 0x0020 +0x03EE 0x0053 +0x03EF 0x0054 +0x03F0 0x0041 +0x03F1 0x0052 +0x03F2 0x0054 +0x03F3 0x0020 +0x03F4 0x0041 +0x03F5 0x0044 +0x03F6 0x0044 +0x03F7 0x0052 +0x03F8 0x0045 +0x03F9 0x0053 +0x03FA 0x0053 +0x03FB 0x003D +0x03FC 0x0000 +0x03FD 0x0020 +0x03FE 0x0045 +0x03FF 0x004E +0x0400 0x0044 +0x0401 0x0020 +0x0402 0x0041 +0x0403 0x0044 +0x0404 0x0044 +0x0405 0x0052 +0x0406 0x0045 +0x0407 0x0053 +0x0408 0x0053 +0x0409 0x003D +0x040A 0x0000 +0x040B 0x0020 +0x040C 0x0056 +0x040D 0x0041 +0x040E 0x004C +0x040F 0x0055 +0x0410 0x0045 +0x0411 0x003D +0x0412 0x0000 +0x0413 0x004C +0x0414 0x004F +0x0415 0x0041 +0x0416 0x0044 +0x0417 0x0020 +0x0418 0x002D +0x0419 0x0020 +0x041A 0x0045 +0x041B 0x004E +0x041C 0x0054 +0x041D 0x0045 +0x041E 0x0052 +0x041F 0x0020 +0x0420 0x0041 +0x0421 0x0044 +0x0422 0x0044 +0x0423 0x0052 +0x0424 0x0045 +0x0425 0x0053 +0x0426 0x0053 +0x0427 0x002F +0x0428 0x0056 +0x0429 0x0041 +0x042A 0x004C +0x042B 0x0055 +0x042C 0x0045 +0x042D 0x0020 +0x042E 0x0050 +0x042F 0x0041 +0x0430 0x0049 +0x0431 0x0052 +0x0432 0x0053 +0x0433 0x002C +0x0434 0x0020 +0x0435 0x0054 +0x0436 0x0045 +0x0437 0x0052 +0x0438 0x004D +0x0439 0x0049 +0x043A 0x004E +0x043B 0x0041 +0x043C 0x0054 +0x043D 0x0045 +0x043E 0x0020 +0x043F 0x0057 +0x0440 0x0049 +0x0441 0x0054 +0x0442 0x0048 +0x0443 0x0020 +0x0444 0x0043 +0x0445 0x0054 +0x0446 0x0052 +0x0447 0x004C +0x0448 0x002D +0x0449 0x0045 +0x044A 0x000D +0x044B 0x000A +0x044C 0x0000 +0x044D 0x004D +0x044E 0x004F +0x044F 0x0056 +0x0450 0x0045 +0x0451 0x0020 +0x0452 0x0046 +0x0453 0x0052 +0x0454 0x004F +0x0455 0x004D +0x0456 0x003D +0x0457 0x0000 +0x0458 0x0020 +0x0459 0x0054 +0x045A 0x004F +0x045B 0x003D +0x045C 0x0000 +0x045D 0x0020 +0x045E 0x004C +0x045F 0x0045 +0x0460 0x004E +0x0461 0x0047 +0x0462 0x0054 +0x0463 0x0048 +0x0464 0x003D +0x0465 0x0000 +0x0466 0x0000 +0x0467 0x0000 +0x0468 0x0000 +0x0469 0x0000 +0x046A 0x0000 +0x046B 0x0000 +0x046C 0x0000 +0x046D 0x0000 +0x046E 0x0000 +0x046F 0x0000 +0x0470 0x0000 +0x0471 0x0000 +0x0472 0x0000 +0x0473 0x0000 +0x0474 0x0000 +0x0475 0x0000 +0x0476 0x0000 +0x0477 0x0000 +0x0478 0x0000 +0x0479 0x0000 +0x047A 0x0000 +0x047B 0x0000 +0x047C 0x0000 +0x047D 0x0000 +0x047E 0x0000 +0x047F 0x0000 +0x0480 0x0000 +0x0481 0x0000 +0x0482 0x0000 +0x0483 0x0000 +0x0484 0x0000 +0x0485 0x0000 +0x0486 0x0000 +0x0487 0x0000 +0x0488 0x0000 +0x0489 0x0000 +0x048A 0x0000 +0x048B 0x0000 +0x048C 0x0000 +0x048D 0x0000 +0x048E 0x0000 +0x048F 0x0000 +0x0490 0x0000 +0x0491 0x0000 +0x0492 0x0000 +0x0493 0x0000 +0x0494 0x0000 +0x0495 0x0000 +0x0496 0x0000 +0x0497 0x0000 +0x0498 0x0000 +0x0499 0x0000 +0x049A 0x0000 +0x049B 0x0000 +0x049C 0x0000 +0x049D 0x0000 +0x049E 0x0000 +0x049F 0x0000 +0x04A0 0x0000 +0x04A1 0x0000 +0x04A2 0x0000 +0x04A3 0x0000 +0x04A4 0x0000 +0x04A5 0x0000 +0x04A6 0x0000 +0x04A7 0x0000 +0x04A8 0x0000 +0x04A9 0x0000 +0x04AA 0x0000 +0x04AB 0x0000 +0x04AC 0x0000 +0x04AD 0x0000 +0x04AE 0x0000 +0x04AF 0x0000 +0x04B0 0x0000 +0x04B1 0x0000 +0x04B2 0x0000 +0x04B3 0x0000 +0x04B4 0x0000 +0x04B5 0x0000 +0x04B6 0x0000 +0x04B7 0x0000 +0x04B8 0x0000 +0x04B9 0x0000 +0x04BA 0x0000 +0x04BB 0x0000 +0x04BC 0x0000 +0x04BD 0x0000 +0x04BE 0x0000 +0x04BF 0x0000 +0x04C0 0x0000 +0x04C1 0x0000 +0x04C2 0x0000 +0x04C3 0x0000 +0x04C4 0x0000 +0x04C5 0x0000 +0x04C6 0x0000 +0x04C7 0x0000 +0x04C8 0x0000 +0x04C9 0x0000 +0x04CA 0x0000 +0x04CB 0x0000 +0x04CC 0x0000 +0x04CD 0x0000 +0x04CE 0x0000 +0x04CF 0x0000 +0x04D0 0x0000 +0x04D1 0x0000 +0x04D2 0x0000 +0x04D3 0x0000 +0x04D4 0x0000 +0x04D5 0x0000 +0x04D6 0x0000 +0x04D7 0x0000 +0x04D8 0x0000 +0x04D9 0x0000 +0x04DA 0x0000 +0x04DB 0x0000 +0x04DC 0x0000 +0x04DD 0x0000 +0x04DE 0x0000 +0x04DF 0x0000 +0x04E0 0x0000 +0x04E1 0x0000 +0x04E2 0x0000 +0x04E3 0x0000 +0x04E4 0x0000 +0x04E5 0x0000 +0x04E6 0x0000 +0x04E7 0x0000 +0x04E8 0x0000 +0x04E9 0x0000 +0x04EA 0x0000 +0x04EB 0x0000 +0x04EC 0x0000 +0x04ED 0x0000 +0x04EE 0x0000 +0x04EF 0x0000 +0x04F0 0x0000 +0x04F1 0x0000 +0x04F2 0x0000 +0x04F3 0x0000 +0x04F4 0x0000 +0x04F5 0x0000 +0x04F6 0x0000 +0x04F7 0x0000 +0x04F8 0x0000 +0x04F9 0x0000 +0x04FA 0x0000 +0x04FB 0x0000 +0x04FC 0x0000 +0x04FD 0x0000 +0x04FE 0x0000 +0x04FF 0x0000 +0x0500 0x0000 +0x0501 0x0000 +0x0502 0x0000 +0x0503 0x0000 +0x0504 0x0000 +0x0505 0x0000 +0x0506 0x0000 +0x0507 0x0000 +0x0508 0x0000 +0x0509 0x0000 +0x050A 0x0000 +0x050B 0x0000 +0x050C 0x0000 +0x050D 0x0000 +0x050E 0x0000 +0x050F 0x0000 +0x0510 0x0000 +0x0511 0x0000 +0x0512 0x0000 +0x0513 0x0000 +0x0514 0x0000 +0x0515 0x0000 +0x0516 0x0000 +0x0517 0x0000 +0x0518 0x0000 +0x0519 0x0000 +0x051A 0x0000 +0x051B 0x0000 +0x051C 0x0000 +0x051D 0x0000 +0x051E 0x0000 +0x051F 0x0000 +0x0520 0x0000 +0x0521 0x0000 +0x0522 0x0000 +0x0523 0x0000 +0x0524 0x0000 +0x0525 0x0000 +0x0526 0x0000 +0x0527 0x0000 +0x0528 0x0000 +0x0529 0x0000 +0x052A 0x0000 +0x052B 0x0000 +0x052C 0x0000 +0x052D 0x0000 +0x052E 0x0000 +0x052F 0x0000 +0x0530 0x0000 +0x0531 0x0000 +0x0532 0x0000 +0x0533 0x0000 +0x0534 0x0000 +0x0535 0x0000 +0x0536 0x0000 +0x0537 0x0000 +0x0538 0x0000 +0x0539 0x0000 +0x053A 0x0000 +0x053B 0x0000 +0x053C 0x0000 +0x053D 0x0000 +0x053E 0x0000 +0x053F 0x0000 +0x0540 0x0000 +0x0541 0x0000 +0x0542 0x0000 +0x0543 0x0000 +0x0544 0x0000 +0x0545 0x0000 +0x0546 0x0000 +0x0547 0x0000 +0x0548 0x0000 +0x0549 0x0000 +0x054A 0x0000 +0x054B 0x0000 +0x054C 0x0000 +0x054D 0x0000 +0x054E 0x0000 +0x054F 0x0000 +0x0550 0x0000 +0x0551 0x0000 +0x0552 0x0000 +0x0553 0x0000 +0x0554 0x0000 +0x0555 0x0000 +0x0556 0x0000 +0x0557 0x0000 +0x0558 0x0000 +0x0559 0x0000 +0x055A 0x0000 +0x055B 0x0000 +0x055C 0x0000 +0x055D 0x0000 +0x055E 0x0000 +0x055F 0x0000 +0x0560 0x0000 +0x0561 0x0000 +0x0562 0x0000 +0x0563 0x0000 +0x0564 0x0000 +0x0565 0x0000 +0x0566 0x1FB8 +0x0567 0x0100 +0x0568 0x0800 +0x0569 0x0804 +0x056A 0x090C +0x056B 0x1F8C +0x056C 0x0001 +0x056D 0x0F90 +0x056E 0xFFFF +0x056F 0x0008 +0x0570 0x3308 +0x0571 0xFFA3 +0x0572 0x0018 +0x0573 0x1F90 +0x0574 0x0001 +0x0575 0x9F90 +0x0576 0x0007 +0x0577 0xFFAB +0x0578 0x0009 +0x0579 0xFFB0 +0x057A 0x009D +0x057B 0x0020 +0x057C 0xFFB0 +0x057D 0x003E +0x057E 0x0FA0 +0x057F 0x0642 +0x0580 0xFFB0 +0x0581 0x0085 +0x0582 0x00A0 +0x0583 0xFFB0 +0x0584 0x0037 +0x0585 0x0FA0 +0x0586 0x0020 +0x0587 0xFFB0 +0x0588 0x009E +0x0589 0xFFA0 +0x058A 0xFFE4 +0x058B 0xFFB0 +0x058C 0x008B +0x058D 0x0120 +0x058E 0x3FB8 +0x058F 0x0100 +0x0590 0x0DBC +0x0591 0x1FB8 +0x0592 0x0100 +0x0593 0xB000 +0x0594 0x0F84 +0x0595 0x0004 +0x0596 0x0FA4 +0x0597 0x0631 +0x0598 0xFFB0 +0x0599 0x0042 +0x059A 0xFFA3 +0x059B 0xFA76 +0x059C 0xFFB0 +0x059D 0x00A7 +0x059E 0xCFA0 +0x059F 0x0058 +0x05A0 0xFFA3 +0x05A1 0xFFF1 +0x05A2 0xFFB0 +0x05A3 0x010F +0x05A4 0x0A28 +0x05A5 0xFFAB +0x05A6 0x0006 +0x05A7 0x0FA0 +0x05A8 0x0007 +0x05A9 0xFFB0 +0x05AA 0x007C +0x05AB 0xFFA0 +0x05AC 0xFFEB +0x05AD 0xFFB0 +0x05AE 0x0078 +0x05AF 0x3FA8 +0x05B0 0x0631 +0x05B1 0x5F80 +0x05B2 0x0004 +0x05B3 0x1A00 +0x05B4 0x3F84 +0x05B5 0x0001 +0x05B6 0xFFAB +0x05B7 0xFFE0 +0x05B8 0x0020 +0x05B9 0x3FB8 +0x05BA 0x0100 +0x05BB 0x0DBC +0x05BC 0x1FB8 +0x05BD 0x0100 +0x05BE 0x0F80 +0x05BF 0x0004 +0x05C0 0x0010 +0x05C1 0x0814 +0x05C2 0x0F84 +0x05C3 0x0631 +0x05C4 0x0108 +0x05C5 0x080C +0x05C6 0x9F8C +0x05C7 0x000F +0x05C8 0x1308 +0x05C9 0x0277 +0x05CA 0x6FA0 +0x05CB 0x0004 +0x05CC 0x3F90 +0x05CD 0x0001 +0x05CE 0xFFAB +0x05CF 0xFFF4 +0x05D0 0x0010 +0x05D1 0x0DA0 +0x05D2 0xFFB0 +0x05D3 0x0053 +0x05D4 0x3F90 +0x05D5 0x0001 +0x05D6 0xFFAB +0x05D7 0xFFF9 +0x05D8 0x0520 +0x05D9 0x3FB8 +0x05DA 0x0100 +0x05DB 0x0DBC +0x05DC 0x1FB8 +0x05DD 0x0100 +0x05DE 0x0F80 +0x05DF 0xFC00 +0x05E0 0x0004 +0x05E1 0x1F80 +0x05E2 0x0001 +0x05E3 0x1F84 +0x05E4 0x0003 +0x05E5 0x0048 +0x05E6 0x9F88 +0x05E7 0x0001 +0x05E8 0xFFA3 +0x05E9 0xFFFB +0x05EA 0x0160 +0x05EB 0x3FB8 +0x05EC 0x0100 +0x05ED 0xCFA0 +0x05EE 0x0005 +0x05EF 0xFFA3 +0x05F0 0xFA21 +0x05F1 0x0DBC +0x05F2 0x1FB8 +0x05F3 0x0100 +0x05F4 0x0800 +0x05F5 0x0804 +0x05F6 0xFFB0 +0x05F7 0xFFE4 +0x05F8 0x0802 +0x05F9 0xFFB0 +0x05FA 0x002C +0x05FB 0x3FA0 +0x05FC 0x000A +0x05FD 0xFFAB +0x05FE 0xFFF7 +0x05FF 0x0F82 +0x0600 0x000D +0x0601 0x0F81 +0x0602 0x0000 +0x0603 0x0120 +0x0604 0x3FB8 +0x0605 0x0100 +0x0606 0x0DBC +0x0607 0x1FB8 +0x0608 0x0100 +0x0609 0x0804 +0x060A 0x0800 +0x060B 0x00A0 +0x060C 0x9FA0 +0x060D 0x00FF +0x060E 0xFFA3 +0x060F 0x0004 +0x0610 0xFFB0 +0x0611 0x0015 +0x0612 0xFFA0 +0x0613 0xFFF7 +0x0614 0x0120 +0x0615 0x3FB8 +0x0616 0x0100 +0x0617 0x0DBC +0x0618 0x1FB8 +0x0619 0x0100 +0x061A 0x0800 +0x061B 0x0FA0 +0x061C 0x000A +0x061D 0xFFB0 +0x061E 0x0008 +0x061F 0x0FA0 +0x0620 0x000D +0x0621 0xFFB0 +0x0622 0x0004 +0x0623 0x0020 +0x0624 0x3FB8 +0x0625 0x0100 +0x0626 0x0DBC +0x0627 0x1FB8 +0x0628 0x0100 +0x0629 0x0F80 +0x062A 0xFC00 +0x062B 0x1F80 +0x062C 0x0003 +0x062D 0x0801 +0x062E 0x3FB8 +0x062F 0x0100 +0x0630 0x0DBC +0x0631 0x0030 +0x0632 0x0031 +0x0633 0x0032 +0x0634 0x0033 +0x0635 0x0034 +0x0636 0x0035 +0x0637 0x0036 +0x0638 0x0037 +0x0639 0x0038 +0x063A 0x0039 +0x063B 0x0041 +0x063C 0x0042 +0x063D 0x0043 +0x063E 0x0044 +0x063F 0x0045 +0x0640 0x0046 +0x0641 0x0000 +0x0642 0x003A +0x0643 0x0020 +0x0644 0x0000 +0x0645 0x1FB8 +0x0646 0x0100 +0x0647 0x0800 +0x0648 0x3F80 +0x0649 0x0061 +0x064A 0xFFA4 +0x064B 0x0009 +0x064C 0x0F80 +0x064D 0x007A +0x064E 0x3800 +0x064F 0xFFA4 +0x0650 0x0004 +0x0651 0x3FA0 +0x0652 0x0061 +0x0653 0x1FA0 +0x0654 0x0041 +0x0655 0x3FB8 +0x0656 0x0100 +0x0657 0x0DBC +0x0658 0x1FB8 +0x0659 0x0100 +0x065A 0x0800 +0x065B 0x0044 +0x065C 0xFFA3 +0x065D 0x0013 +0x065E 0x0108 +0x065F 0x3F88 +0x0660 0x0061 +0x0661 0xFFA4 +0x0662 0x000A +0x0663 0x0F88 +0x0664 0x007A +0x0665 0x3108 +0x0666 0xFFA4 +0x0667 0x0005 +0x0668 0x3F84 +0x0669 0x0061 +0x066A 0x1F84 +0x066B 0x0041 +0x066C 0x0101 +0x066D 0x1F80 +0x066E 0x0001 +0x066F 0xFFA0 +0x0670 0xFFEA +0x0671 0x3FB8 +0x0672 0x0100 +0x0673 0x0DBC +0x0674 0x1FB8 +0x0675 0x0100 +0x0676 0x0800 +0x0677 0x0FA4 +0x0678 0xFFFF +0x0679 0x1FA4 +0x067A 0x0001 +0x067B 0x0084 +0x067C 0xFFAB +0x067D 0xFFFB +0x067E 0x3FB8 +0x067F 0x0100 +0x0680 0x0DBC +0x0681 0x1FB8 +0x0682 0x0100 +0x0683 0x0800 +0x0684 0x0904 +0x0685 0x0808 +0x0686 0xFFB0 +0x0687 0xFFEC +0x0688 0x0924 +0x0689 0xFFA3 +0x068A 0x0011 +0x068B 0x1908 +0x068C 0x02CC +0x068D 0x3F8C +0x068E 0x000D +0x068F 0xFFAB +0x0690 0x0004 +0x0691 0x0F89 +0x0692 0x0000 +0x0693 0x3F88 +0x0694 0x0001 +0x0695 0x024C +0x0696 0x3F8C +0x0697 0x000A +0x0698 0xFFAB +0x0699 0x0002 +0x069A 0x0F89 +0x069B 0x0000 +0x069C 0x0124 +0x069D 0x3FB8 +0x069E 0x0100 +0x069F 0x0DBC +0x06A0 0x1FB8 +0x06A1 0x0100 +0x06A2 0x0800 +0x06A3 0x0904 +0x06A4 0x0068 +0x06A5 0x0188 +0x06A6 0x3A08 +0x06A7 0xFFAB +0x06A8 0x0005 +0x06A9 0x00A8 +0x06AA 0xFFA3 +0x06AB 0x0004 +0x06AC 0xFFA0 +0x06AD 0xFFF6 +0x06AE 0x01C8 +0x06AF 0x3228 +0x06B0 0x3FB8 +0x06B1 0x0100 +0x06B2 0x0DBC +0x06B3 0x1FB8 +0x06B4 0x0100 +0x06B5 0x0900 +0x06B6 0xBA28 +0x06B7 0xCF81 +0x06B8 0x0000 +0x06B9 0xFFA3 +0x06BA 0x000A +0x06BB 0xC801 +0x06BC 0xFFAB +0x06BD 0x0003 +0x06BE 0x0028 +0x06BF 0xFFA0 +0x06C0 0x0004 +0x06C1 0x1F80 +0x06C2 0x0001 +0x06C3 0xFFA0 +0x06C4 0xFFF2 +0x06C5 0x3FB8 +0x06C6 0x0100 +0x06C7 0x0DBC +0x06C8 0x1FB8 +0x06C9 0x0100 +0x06CA 0x0800 +0x06CB 0x0904 +0x06CC 0x0104 +0x06CD 0xFFA3 +0x06CE 0x0005 +0x06CF 0x0A02 +0x06D0 0x3F84 +0x06D1 0x0001 +0x06D2 0xFFA0 +0x06D3 0xFFF8 +0x06D4 0x3FB8 +0x06D5 0x0100 +0x06D6 0x0DBC +0x06D7 0x1FB8 +0x06D8 0x0100 +0x06D9 0x0800 +0x06DA 0x0904 +0x06DB 0x0A08 +0x06DC 0x0208 +0x06DD 0xFFA3 +0x06DE 0x0005 +0x06DF 0x0086 +0x06E0 0x3F88 +0x06E1 0x0001 +0x06E2 0xFFA0 +0x06E3 0xFFF8 +0x06E4 0x3FB8 +0x06E5 0x0100 +0x06E6 0x0DBC +0x06E7 0xE000 diff --git a/monitor/qmon.asm b/monitor/qmon.asm new file mode 100644 index 00000000..b00a94b8 --- /dev/null +++ b/monitor/qmon.asm @@ -0,0 +1,247 @@ +;; +;; QMON - a simple monitor for the QNICE processor +;; +;; The labels and constants of each subsystem are prefixed with a short name denoting the particular +;; subsystem, followed by a dollar sign. Examples for this are IO$BASE or STR$STRMP etc. Labels +;; within a routine follow this prefix style but have an additional underscore following the dollar +;; sign to denote that these labels should normally not be the target of a branch or subroutine call +;; from outside code areas. +;; +;; B. Ulmann fecit +;; +;; 17-DEC-2007: Begin of coding +;; 03-AUG-2015: After upgrading the emulator and fixing some (serious) bugs the work on the +;; monitor continues +;; 06-AUG-2015: Basic monitor functions implemented +;; +;; Known bugs: +;; +;; Bits and pieces: +;; - All functions expect their input parameters in the registers R8, R9 and maybe R10. +;; - The result of a function is returned in the first non-used high numbered register, so if a +;; function expects its parameters in R8 and R9, it will return its result in R10. If it only +;; expects one parameter, the result will be +;; returned in R9 respectively. +;; - Every function name starts with its subsection name followed by a dollar sign, so all string +;; routines have names starting with "STR$". +;; - Labels within a function always have an underscore following the subsystem name, so a label +;; within the routine STR$CMP would have the form "STR$_CMP...". So never jump to a label of the +;; form "$_..." since this will be a label buried inside a function. +;; - Every subsystem (string routines, IO routines etc.) has its own constants which are always +;; located after the code for the routines. +;; - To assemble this monitor just call the "asm" shell script which will use the C preprocessor +;; to include the necessary library files. +;; + .ORG 0x0000 ; The monitor begins at address 0x0000, so the lower + ; address EPROMs should be mapped into memory by hardware + ; default on power up. +; +; Some useful constants +; + +; +; Main program +; +QMON$COLDSTART MOVE IO$BASE, SP ; Set up stack pointer + MOVE QMON$WELCOME, R8 ; Print welcome message + RSUB IO$PUTS, 1 + MOVE QMON$LAST_ADDR, R8 ; Clear memory after the monitor + ADD 0x0001, R8 ; Start address + MOVE IO$BASE, R9 ; Determine length of memory area + SUB R8, R9 ; to be cleared + SUB 0x0001, R9 ; We need one stack cell for the following call + XOR R10, R10 ; Clear with zero words + RSUB MEM$FILL, 1 ; Clear +;;TODO: Clear registers +QMON$WARMSTART MOVE IO$BASE, SP ; Set up stack pointer + AND 0x00FF, SR ; Reset register bank to zero + RSUB IO$PUT_CRLF, 1 +QMON$MAIN_LOOP MOVE QMON$PROMPT, R8 ; Print monitor prompt + RSUB IO$PUTS, 1 + RSUB IO$GETCHAR, 1 ; Wait for a key being pressed + RSUB CHR$TO_UPPER, 1 ; Convert it into an uppercase letter + RSUB IO$PUTCHAR, 1 ; Echo the character + CMP 'C', R8 ; Control group? + RBRA QMON$MAYBE_M, !Z ; No +; Control group + MOVE QMON$CG_C, R8 + RSUB IO$PUTS, 1 + RSUB IO$GETCHAR, 1 ; Get command character + RSUB CHR$TO_UPPER, 1 + CMP 'C', R8 ; Cold start? + RBRA QMON$C_MAYBE_H, !Z ; No... +; CONTROL/COLDSTART: + MOVE QMON$CG_C_C, R8 + RSUB IO$PUTS, 1 + RBRA QMON$COLDSTART, 1 ; Yes! +QMON$C_MAYBE_H CMP 'H', R8 ; Halt? + RBRA QMON$MAYBE_R, !Z +; CONTROL/HALT: + MOVE QMON$CG_C_H, R8 + RSUB IO$PUTS, 1 + HALT +QMON$MAYBE_R CMP 'R', R8 ; Run? + RBRA QMON$C_ILLEGAL, !Z ; No +; CONTROL/RUN: + MOVE QMON$CG_C_R, R8 + RSUB IO$PUTS, 1 + RSUB IO$GET_W_HEX, 1 ; Get address + RSUB IO$PUT_CRLF, 1 + ABRA R8, 1 ; Jump to address specified +QMON$C_ILLEGAL MOVE QMON$ILLCMD, R8 ; Control group C, illegal command + RSUB IO$PUTS, 1 + RBRA QMON$MAIN_LOOP, 1 +QMON$MAYBE_M CMP 'M', R8 ; Compare with 'M' + RBRA QMON$MAYBE_H, !Z ; No M, try next... +; Memory control group: + MOVE QMON$CG_M, R8 ; Print control group name + RSUB IO$PUTS, 1 + RSUB IO$GETCHAR, 1 ; Get command character + RSUB CHR$TO_UPPER, 1 ; ...convert it to upper case + CMP 'C', R8 ; 'Change'? + RBRA QMON$M_MAYBE_D, !Z +; MEMORY/CHANGE: + MOVE QMON$CG_M_C, R8 ; Print prompt for address + RSUB IO$PUTS, 1 + RSUB IO$GET_W_HEX, 1 ; Read in address + MOVE R8, R0 + MOVE QMON$CG_M_C1, R8 ; Prepare output of current value + RSUB IO$PUTS, 1 + MOVE @R0, R8 ; Get current value + RSUB IO$PUT_W_HEX, 1 ; Print current value + MOVE QMON$CG_M_C2, R8 ; Prompt for new value + RSUB IO$PUTS, 1 + RSUB IO$GET_W_HEX, 1 + MOVE R8, @R0 + RSUB IO$PUT_CRLF, 1 + RBRA QMON$MAIN_LOOP, 1 +QMON$M_MAYBE_D CMP 'D', R8 + RBRA QMON$M_MAYBE_E, !Z ; No D, try next... +; MEMORY/DUMP: + MOVE QMON$CG_M_D, R8 ; Print prompt for start address + RSUB IO$PUTS, 1 + RSUB IO$GET_W_HEX, 1 ; Get start address + MOVE R8, R0 ; Remember start address in R8 + MOVE QMON$CG_M_D2, R8 ; Print prompt for end address + RSUB IO$PUTS, 1 + RSUB IO$GET_W_HEX, 1 ; Get end address + RSUB IO$PUT_CRLF, 1 + MOVE R8, R9 + MOVE R0, R8 + RSUB IO$DUMP_MEMORY, 1 ; Dump memory contents + RSUB IO$PUT_CRLF, 1 + RBRA QMON$MAIN_LOOP, 1 +QMON$M_MAYBE_E CMP 'E', R8 ; Is it an 'E'? + RBRA QMON$M_MAYBE_F, !Z ; No... +; MEMORY/EXAMINE: + MOVE QMON$CG_M_E, R8 ; Print prompt for address + RSUB IO$PUTS, 1 + RSUB IO$GET_W_HEX, 1 ; Read address + MOVE R8, R0 + MOVE ' ', R8 + RSUB IO$PUTCHAR, 1 + MOVE @R0, R8 + RSUB IO$PUT_W_HEX, 1 + RSUB IO$PUT_CRLF, 1 + RBRA QMON$MAIN_LOOP, 1 +QMON$M_MAYBE_F CMP 'F', R8 + RBRA QMON$M_MAYBE_L, !Z +; MEMORY/FILL: + MOVE QMON$CG_M_F, R8 + RSUB IO$PUTS, 1 + RSUB IO$GET_W_HEX, 1 + MOVE R8, R0 + MOVE QMON$CG_M_F2, R8 + RSUB IO$PUTS, 1 + RSUB IO$GET_W_HEX, 1 + MOVE R8, R1 + MOVE QMON$CG_M_F3, R8 + RSUB IO$PUTS, 1 + RSUB IO$GET_W_HEX, 1 + MOVE R8, R10 + MOVE R0, R8 + SUB R0, R1 + ADD 0x0001, R1 + MOVE R1, R9 + RSUB MEM$FILL, 1 + RSUB IO$PUT_CRLF, 1 + RBRA QMON$MAIN_LOOP, 1 +QMON$M_MAYBE_L CMP 'L', R8 + RBRA QMON$M_MAYBE_M, !Z +; MEMORY/LOAD: + MOVE QMON$CG_M_L, R8 + RSUB IO$PUTS, 1 +_QMON$ML_LOOP RSUB IO$GET_W_HEX, 1 ; Get address + MOVE R8, R0 + RSUB IO$GET_W_HEX, 1 ; Get value + MOVE R8, @R0 + RBRA _QMON$ML_LOOP, 1 +QMON$M_MAYBE_M CMP 'M', R8 + RBRA QMON$M_ILLEGAL, !Z +; MEMORY/MOVE: + MOVE QMON$CG_M_M, R8 + RSUB IO$PUTS, 1 + RSUB IO$GET_W_HEX, 1 + MOVE R8, R0 + MOVE QMON$CG_M_M2, R8 + RSUB IO$PUTS, 1 + RSUB IO$GET_W_HEX, 1 + MOVE R8, R1 + MOVE QMON$CG_M_M3, R8 + RSUB IO$PUTS, 1 + RSUB IO$GET_W_HEX, 1 + MOVE R8, R10 + MOVE R0, R8 + MOVE R1, R9 + RSUB MEM$MOVE, 1 + RSUB IO$PUT_CRLF, 1 + RBRA QMON$MAIN_LOOP, 1 +QMON$M_ILLEGAL MOVE QMON$ILLCMD, R8 + RSUB IO$PUTS, 1 + RBRA QMON$MAIN_LOOP, 1 +QMON$MAYBE_H CMP 'H', R8 + RBRA QMON$NOT_H, !Z ; No H, try next... +; HELP: + MOVE QMON$HELP, R8 ; H(elp) - print help text + RSUB IO$PUTS, 1 + RBRA QMON$MAIN_LOOP, 1 +QMON$NOT_H MOVE QMON$ILLCMDGRP, R8A ; Illegal command group + RSUB IO$PUTS, 1 + RBRA QMON$MAIN_LOOP, 1 + +QMON$WELCOME .ASCII_P "\n\nSimple QNICE-monitor - Version 0.2 (Bernd Ulmann, August 2015)\n" + .ASCII_W "--------------------------------------------------------------\n\n" +QMON$PROMPT .ASCII_W "QMON> " +QMON$ILLCMDGRP .ASCII_W " *** Illegal command group ***\n" +QMON$ILLCMD .ASCII_W " *** Illegal command ***\n" +QMON$HELP .ASCII_P "ELP:\n\n" + .ASCII_P " C(control group):\n" + .ASCII_P " C(old start) H(alt) R(un)\n" + .ASCII_P " H(elp)\n" + .ASCII_P " M(emory group):\n" + .ASCII_P " C(hange) D(ump) E(xamine) F(ill) L(oad) M(ove)\n" + .ASCII_P "\n General: CTRL-E performs a warm start whenever an\n" + .ASCII_P " input from keyboard is expected.\n" + .ASCII_P "\n M(emory)L(oad) can be used to load assembler output\n" + .ASCII_P " by pasting it to the terminal. CTRL-E terminates.\n" + .ASCII_W "\n" +QMON$CG_C .ASCII_W "ONTROL/" +QMON$CG_C_C .ASCII_W "COLD START" +QMON$CG_C_H .ASCII_W "HALT\n\n" +QMON$CG_C_R .ASCII_W "RUN ADDRESS=" +QMON$CG_M .ASCII_W "EMORY/" +QMON$CG_M_C .ASCII_W "CHANGE ADDRESS=" +QMON$CG_M_C1 .ASCII_W " CURRENT VALUE=" +QMON$CG_M_C2 .ASCII_W " NEW VALUE=" +QMON$CG_M_D .ASCII_W "DUMP START ADDRESS=" +QMON$CG_M_D2 .ASCII_W " END ADDRESS=" +QMON$CG_M_E .ASCII_W "EXAMINE ADDRESS=" +QMON$CG_M_F .ASCII_W "FILL START ADDRESS=" +QMON$CG_M_F2 .ASCII_W " END ADDRESS=" +QMON$CG_M_F3 .ASCII_W " VALUE=" +QMON$CG_M_L .ASCII_W "LOAD - ENTER ADDRESS/VALUE PAIRS, TERMINATE WITH CTRL-E\n" +QMON$CG_M_M .ASCII_W "MOVE FROM=" +QMON$CG_M_M2 .ASCII_W " TO=" +QMON$CG_M_M3 .ASCII_W " LENGTH=" +; +QMON$COMMAND .BLOCK 0x0100 ; Reserve some memory for holding a command line diff --git a/monitor/string_library.asm b/monitor/string_library.asm new file mode 100644 index 00000000..ea2383bc --- /dev/null +++ b/monitor/string_library.asm @@ -0,0 +1,133 @@ +; +;;======================================================================================= +;; The collection of string related functions starts here +;;======================================================================================= +; +;*************************************************************************************** +;* CHR$TO_UPPER expects a character to be converted to upper case in R8 +;*************************************************************************************** +; +CHR$TO_UPPER INCRB + MOVE R8, R0 ; Save character + SUB 'a', R0 ; Less than 'a'? + RBRA _CHR$TO_UPPER_EXIT, N ; Yes - nothing to do + MOVE 'z', R0 ; Check if greater than 'z' + SUB R8, R0 + RBRA _CHR$TO_UPPER_EXIT, N ; Yes - nothing to do + SUB 'a', R8 ; Perform the conversion + ADD 'A', R8 +_CHR$TO_UPPER_EXIT DECRB + RET +; +;*************************************************************************************** +;* STR$TO_UPPER expects the address of a string to be converted to upper case in R8 +;*************************************************************************************** +; +STR$TO_UPPER INCRB ; Get a new scratch register page + MOVE R8, R0 ; Do not destroy parameters +_STR$TO_UPPER_LOOP MOVE @R0, R1 ; Null terminator found? + RBRA _STR$TO_UPPER_END, Z ; Yes - that is it + MOVE R1, R2 + SUB 'a', R2 ; Less than 'a'? + RBRA _STR$TO_UPPER_NEXT, N ; Yes + MOVE 'z', R2 ; Greater than 'z'? + SUB R1, R2 + RBRA _STR$TO_UPPER_NEXT, N ; Yes + SUB 'a', R1 ; Now convert the LC char to UC + ADD 'A', R1 + MOVE R1, @R0 ; Store it back into the string +_STR$TO_UPPER_NEXT ADD 0x001, R0 + RBRA _STR$TO_UPPER_LOOP, 1 ; Process next character +_STR$TO_UPPER_END DECRB ; Restore old register page + RET +; +;*************************************************************************************** +;* STR$LEN expects the address of a string in R8 and returns its length in R9 +;*************************************************************************************** +; +STR$LEN INCRB ; Get a new scratch register page + MOVE R8, R0 ; Do not work with the original pointer + MOVE 0xFFFF, R9 ; R9 = -1 +_STR$LEN_LOOP ADD 0x0001, R9 ; One character found + MOVE @R0++, R1 ; Was it the terminating null word? + RBRA _STR$LEN_LOOP, !Z ; No? + DECRB + RET +; +;*************************************************************************************** +;* STR$CHOMP removes a trailing LF/CR from a string pointed to by R8 +;*************************************************************************************** +; +STR$CHOMP INCRB ; Get a new register page + MOVE R8, R0 ; Save the start address of the string + MOVE R9, R1 ; R9 will be used later + MOVE R8, R2 ; R2 will be used as a working pointer + RSUB STR$LEN, 1 ; Determine the length of the string + MOVE R9, R9 ; Is the string empty? + RBRA _STR$CHOMP_EXIT, Z ; Yes + ADD R9, R2 ; R2 now points to the last string character + MOVE @--R2, R3 ; Get a character + SUB 0x000D, R3 ; Is it a CR (we are working from right!) + RBRA _STR$CHOMP_1, !Z ; No, so nothing to do so far + MOVE 0x0000, @R2 ; Yes, replace it with a null word + SUB 0x0001, R2 ; Proceed to second last character +_STR$CHOMP_1 MOVE @R2, R3 ; Now test for a line feed + SUB 0x000A, R3 + RBRA _STR$CHOMP_EXIT, !Z ; Nothing to do + MOVE 0x0000, @R2 ; Replace the LF with a null word +_STR$CHOMP_EXIT MOVE R1, R9 ; Restore R9 + DECRB ; Restore register bank + RET +; +;*************************************************************************************** +;* STR$CMP compares two strings +;* +;* R8: Pointer to the first string (S0), +;* R9: Pointer to the second string (S1), +;* +;* R10: negative if (S0 < S1), zero if (S0 == S1), positive if (S0 > S1) +; +;* The contents of R8 and R9 are being preserved during the run of this function +;*************************************************************************************** +; +STR$CMP INCRB ; Get a new register page + MOVE R8, R0 ; Save arguments + MOVE R9, R1 +_STR$CMP_LOOP MOVE @R0, R10 ; while (*s1 == *s2++) + MOVE @R1++, R2 + SUB R10, R2 + RBRA _STR$CMP_END, !Z + MOVE @R0++, R10 ; if (*s1++ == 0) + RBRA _STR$CMP_EXIT, Z ; return 0; + RBRA _STR$CMP_LOOP, 1 ; end-of-while-loop +_STR$CMP_END MOVE @--R1, R2 ; return (*s1 - (--*s2)); + SUB R2, R10 +_STR$CMP_EXIT DECRB ; Restore previous register page + RET +; +;*************************************************************************************** +;* STR$STRCHR seaches for the first occurrence of the character stored in R8 in a +;* string pointed to by R9. +;* +;* R8: Pointer to the string +;* R9: Character to be searched +;* +;* R10: Zero if the character has not been found, otherwise it contains a pointer +;* to the first occurrence of the character in the string +; +;* The contents of R8 and R9 are being preserved during the run of this function +;*************************************************************************************** +; +STR$STRCHR INCRB + MOVE R9, R0 + XOR R10, R10 +_STR$STRCHR_LOOP CMP 0x0000, @R0 ; while (*string) + RBRA _STR$STRCHR_EXIT, Z + CMP R8, @R0 ; if (*string == R8) + RBRA _STR$STRCHR_NEXT, !Z + MOVE R0, R10 + RBRA _STR$STRCHR_EXIT, 1 +_STR$STRCHR_NEXT ADD 0x0001, R0 ; string++ + RBRA _STR$STRCHR_LOOP, 1 +_STR$STRCHR_EXIT DECRB + RET diff --git a/monitor/sysdef.asm b/monitor/sysdef.asm new file mode 100644 index 00000000..019e8d47 --- /dev/null +++ b/monitor/sysdef.asm @@ -0,0 +1,31 @@ +; +; This file contains the necessary definitions for the simple QNICE-monitor. +; + +; +; Some assembler macros which make life much easier: +; +#define RET MOVE @R13++, R15 +#define INCRB ADD 0x0100, R14 +#define DECRB SUB 0x0100, R14 + +; +; Some register short names: +; +#define PC R15 +#define SR R14 +#define SP R13 + +; +; IO-page addresses: +; +IO$BASE .EQU 0xFC00 +IO$UART0_BASE .EQU 0xFC00 + +; +; UART-registers: +; +IO$UART_SRA .EQU 0x0001 ; Status register (relative to base address) +IO$UART_RHRA .EQU 0x0003 ; Receiving register (relative to base address) +IO$UART_THRA .EQU 0x0003 ; Transmitting register (relative to base address) + diff --git a/test_programs/bram.asm b/test_programs/bram.asm index 7fe3468b..c5f95ae2 100644 --- a/test_programs/bram.asm +++ b/test_programs/bram.asm @@ -1,13 +1,47 @@ -; simple Block RAM test done by sy2002 in August 2015 +; Block RAM test that includes running an application from RAM +; done by sy2002 in August 2015 IO$TIL_BASE .EQU 0xFF10 ; address of TIL-display RAM_VARIABLE .EQU 0x8000 ; address of a variable in RAM +STACK_TOP .EQU 0x8010 ; top of the stack +EXE_START .EQU 0x8011 ; start address of code in RAM - MOVE IO$TIL_BASE, R12 - MOVE RAM_VARIABLE, R0 + .ORG 0x0000 + + ; copy source code to RAM to execute it there + ; this tests multiple things, also, if relative jumps + ; are really working and if opcode fetches also work in RAM + + MOVE CODE_END, R0 ; end of "to-be-copied-code" + MOVE CODE_START, R1 ; run variable for copying: source + MOVE EXE_START, R2 ; run variable for copying: dest + MOVE 1, R3 ; we need to subtract 1 often + SUB R1, R0 ; how many bytes to copy - 1 + ; as the last opcode 2 bytes + +COPY_CODE MOVE @R1++, @R2++ ; copy from src to dst + SUB R3, R0 ; one less item to go + RBRA COPY_CODE, !N ; R0 is #bytes-1, so check for !N + ; instead of checking for !Z + + ABRA EXE_START, 1 ; execute code from RAM + + ; this is the test code that tests BRAM operations + ; and the stack and sub routine calls + ; it should show 0x2309 on the TIL on success + +CODE_START MOVE IO$TIL_BASE, R12 ; TIL display address + MOVE RAM_VARIABLE, R0 ; address of a variable in RAM + MOVE STACK_TOP, R13 ; setup stack pointer + + MOVE 0x22EE, @R0 ; write 0x22EE to variable in BRAM + RSUB ADD_IT, 1 ; use a sub routine to add 0x09 + RSUB ADD_IT, 1 ; ... multiple times ... + RSUB ADD_IT, 1 ; ... to the variable in BRAM - MOVE 0x2300, @R0 ; write 0x2300 to BRAM's 0x8000 - ADD 0x0009, @R0 ; add 9 to BRAM's 0x8000 MOVE @R0, @R12 ; display 0x2309 on the TIL HALT + +ADD_IT ADD 0x0009, @R0 ; add 9 to BRAM's 0x8000 +CODE_END MOVE @R13++, R15 ; return from sub routine \ No newline at end of file diff --git a/test_programs/bram.lis b/test_programs/bram.lis index d014fa0e..3c6a9a76 100644 --- a/test_programs/bram.lis +++ b/test_programs/bram.lis @@ -1,21 +1,58 @@ -000001 ; simple Block RAM test done by sy2002 in August 2015 -000002 -000003 IO$TIL_BASE .EQU 0xFF10 ; address of TIL-display -000004 RAM_VARIABLE .EQU 0x8000 ; address of a variable in RAM -000005 -000006 0000 0FB0 FF10 MOVE IO$TIL_BASE, R12 -000007 0002 0F80 8000 MOVE RAM_VARIABLE, R0 +000001 ; Block RAM test that includes running an application from RAM +000002 ; done by sy2002 in August 2015 +000003 +000004 IO$TIL_BASE .EQU 0xFF10 ; address of TIL-display +000005 RAM_VARIABLE .EQU 0x8000 ; address of a variable in RAM +000006 STACK_TOP .EQU 0x8010 ; top of the stack +000007 EXE_START .EQU 0x8011 ; start address of code in RAM 000008 -000009 0004 0F81 2300 MOVE 0x2300, @R0 ; write 0x2300 to BRAM's 0x8000 -000010 0006 1F81 0009 ADD 0x0009, @R0 ; add 9 to BRAM's 0x8000 -000011 0008 0071 MOVE @R0, @R12 ; display 0x2309 on the TIL -000012 -000013 0009 E000 HALT +000009 .ORG 0x0000 +000010 +000011 ; copy source code to RAM to execute it there +000012 ; this tests multiple things, also, if relative jumps +000013 ; are really working and if opcode fetches also work in RAM +000014 +000015 0000 0F80 0021 MOVE CODE_END, R0 ; end of "to-be-copied-code" +000016 0002 0F84 000F MOVE CODE_START, R1 ; run variable for copying: source +000017 0004 0F88 8011 MOVE EXE_START, R2 ; run variable for copying: dest +000018 0006 0F8C 0001 MOVE 1, R3 ; we need to subtract 1 often +000019 0008 3100 SUB R1, R0 ; how many bytes to copy - 1 +000020 ; as the last opcode 2 bytes +000021 +000022 0009 018A COPY_CODE MOVE @R1++, @R2++ ; copy from src to dst +000023 000A 3300 SUB R3, R0 ; one less item to go +000024 000B FFAC FFFC RBRA COPY_CODE, !N ; R0 is #bytes-1, so check for !N +000025 ; instead of checking for !Z +000026 +000027 000D FF80 8011 ABRA EXE_START, 1 ; execute code from RAM +000028 +000029 ; this is the test code that tests BRAM operations +000030 ; and the stack and sub routine calls +000031 ; it should show 0x2309 on the TIL on success +000032 +000033 000F 0FB0 FF10 CODE_START MOVE IO$TIL_BASE, R12 ; TIL display address +000034 0011 0F80 8000 MOVE RAM_VARIABLE, R0 ; address of a variable in RAM +000035 0013 0FB4 8010 MOVE STACK_TOP, R13 ; setup stack pointer +000036 +000037 0015 0F81 22EE MOVE 0x22EE, @R0 ; write 0x22EE to variable in BRAM +000038 0017 FFB0 0006 RSUB ADD_IT, 1 ; use a sub routine to add 0x09 +000039 0019 FFB0 0004 RSUB ADD_IT, 1 ; ... multiple times ... +000040 001B FFB0 0002 RSUB ADD_IT, 1 ; ... to the variable in BRAM +000041 +000042 001D 0071 MOVE @R0, @R12 ; display 0x2309 on the TIL +000043 +000044 001E E000 HALT +000045 +000046 001F 1F81 0009 ADD_IT ADD 0x0009, @R0 ; add 9 to BRAM's 0x8000 +000047 0021 0DBC CODE_END MOVE @R13++, R15 ; return from sub routine EQU-list: -------------------------------------------------------------------------------------------------------- -IO$TIL_BASE : 0xFF10 RAM_VARIABLE : 0x8000 +IO$TIL_BASE : 0xFF10 RAM_VARIABLE : 0x8000 STACK_TOP : 0x8010 +EXE_START : 0x8011 Label-list: -------------------------------------------------------------------------------------------------------- +COPY_CODE : 0x0009 CODE_START : 0x000F ADD_IT : 0x001F +CODE_END : 0x0021 diff --git a/test_programs/bram.out b/test_programs/bram.out index 2ee1832a..0470e389 100644 --- a/test_programs/bram.out +++ b/test_programs/bram.out @@ -1,10 +1,34 @@ -0x0000 0x0FB0 -0x0001 0xFF10 -0x0002 0x0F80 -0x0003 0x8000 -0x0004 0x0F81 -0x0005 0x2300 -0x0006 0x1F81 -0x0007 0x0009 -0x0008 0x0071 -0x0009 0xE000 +0x0000 0x0F80 +0x0001 0x0021 +0x0002 0x0F84 +0x0003 0x000F +0x0004 0x0F88 +0x0005 0x8011 +0x0006 0x0F8C +0x0007 0x0001 +0x0008 0x3100 +0x0009 0x018A +0x000A 0x3300 +0x000B 0xFFAC +0x000C 0xFFFC +0x000D 0xFF80 +0x000E 0x8011 +0x000F 0x0FB0 +0x0010 0xFF10 +0x0011 0x0F80 +0x0012 0x8000 +0x0013 0x0FB4 +0x0014 0x8010 +0x0015 0x0F81 +0x0016 0x22EE +0x0017 0xFFB0 +0x0018 0x0006 +0x0019 0xFFB0 +0x001A 0x0004 +0x001B 0xFFB0 +0x001C 0x0002 +0x001D 0x0071 +0x001E 0xE000 +0x001F 0x1F81 +0x0020 0x0009 +0x0021 0x0DBC diff --git a/test_programs/bram.rom b/test_programs/bram.rom index 2d9bd1ee..eebf5342 100644 --- a/test_programs/bram.rom +++ b/test_programs/bram.rom @@ -1,10 +1,34 @@ +0000111110000000 +0000000000100001 +0000111110000100 +0000000000001111 +0000111110001000 +1000000000010001 +0000111110001100 +0000000000000001 +0011000100000000 +0000000110001010 +0011001100000000 +1111111110101100 +1111111111111100 +1111111110000000 +1000000000010001 0000111110110000 1111111100010000 0000111110000000 1000000000000000 +0000111110110100 +1000000000010000 0000111110000001 -0010001100000000 -0001111110000001 -0000000000001001 +0010001011101110 +1111111110110000 +0000000000000110 +1111111110110000 +0000000000000100 +1111111110110000 +0000000000000010 0000000001110001 1110000000000000 +0001111110000001 +0000000000001001 +0000110110111100 diff --git a/test_programs/moves.asm b/test_programs/moves.asm index c133dd6c..9201cef5 100644 --- a/test_programs/moves.asm +++ b/test_programs/moves.asm @@ -1,5 +1,8 @@ ; MOVE and register unit tests including bank switching and some flag tests +; by sy2002, July/August 2015 + .ORG 0x0000 ; Start address + ; in both cases, R14 should be 0x0009, i.e. Z and 1 flag set ; as you cannot clear the status register MOVE 0x0000, R0 ; first try using a register to register operation, so lead R0 ... diff --git a/test_programs/moves.lis b/test_programs/moves.lis deleted file mode 100644 index 12d4412c..00000000 --- a/test_programs/moves.lis +++ /dev/null @@ -1,66 +0,0 @@ -000001 ; MOVE and register unit tests including bank switching and some flag tests -000002 -000003 ; in both cases, R14 should be 0x0009, i.e. Z and 1 flag set -000004 ; as you cannot clear the status register -000005 0000 0F80 0000 MOVE 0x0000, R0 ; first try using a register to register operation, so lead R0 ... -000006 0002 0038 MOVE R0, R14 ; ... and store it to R14 -000007 -000008 0003 0F84 0001 MOVE 1, R1 ; dummy operation to clear the Z flag, i.e. SR should be 1 right now -000009 -000010 N32768 .EQU 0x8000 ; 2-complement of -32768 as the assembler is not supporting negatives -000011 N2 .EQU 0xFFFE ; 2-complement of -2 -000012 -000013 0005 0F88 FFFF MOVE 0xFFFF, R2 ; dummy operation to set the X flag and N flag, i.e. SR should be 0x13 right now -000014 0007 0F8C 8000 MOVE N32768, R3 ; prepare overflow, N flag is set, SR should be 0x11 now -000015 0009 0F90 FFFE MOVE N2, R4 ; second component for overflow, SR stays 0x11 -000016 000B 1310 ADD R3, R4 ; R4 should be 0x7FFE now and the C and V flag should be set, i.e. SR should be 0x25 -000017 -000018 000C 0FB8 0000 MOVE 0x0000, R14 ; second try use a @R15++ operation, SR should be 9 afterwards -000019 -000020 -000021 ; check memory reading and register bank switching -000022 000E FF80 0014 ABRA NEXT1, 1 -000023 -000024 BANK .EQU 0x0100 ; this adds 1 to the upper 8 bit, i.e. can be used for bank switching -000025 -000026 0010 0041 DATA .ASCII_W "ABC" - 0011 0042 - 0012 0043 - 0013 0000 -000027 -000028 0014 0FA8 0010 NEXT1 MOVE DATA, R10 ; upper register bank -000029 -000030 0016 1FB8 0100 ADD BANK, R14 ; next register bank via @R15++ operation -000031 0018 0F80 0001 MOVE 1, R0 -000032 001A 0F84 0002 MOVE 2, R1 -000033 001C 0F88 0003 MOVE 3, R2 ; after this, (R0, R1, R2) must be (1, 2, 3) in bank #1 -000034 -000035 001E 0FAC 0100 MOVE BANK, R11 ; next register bank via register to register operation -000036 0020 1B38 ADD R11, R14 ; after this, the current bank should be #2 -000037 -000038 0021 0A80 MOVE @R10++, R0 -000039 0022 0A84 MOVE @R10++, R1 -000040 0023 0A88 MOVE @R10++, R2 -000041 0024 3AC8 SUB @--R10, R2 ; after this, (R0, R1, R2) must be (0x41, 0x42, 0x00) in bank #2 -000042 -000043 0025 1B38 ADD R11, R14 -000044 0026 0F9C 2309 MOVE 0x2309, R7 -000045 0028 0718 MOVE R7, R6 ; R7 and R6 shall contain 0x2309 in bank #3 -000046 -000047 0029 0F84 0000 MOVE 0, R1 -000048 002B AFB8 0004 OR 4, R14 ; set carry, SR = 0x305 -000049 002D 2F84 0001 ADDC 1, R1 ; R1 shall be 2, carry cleared afterwards therefore SR = 1 -000050 -000051 002F 3FB8 0100 SUB BANK, R14 ; switch bank to bank #2 -000052 0031 0A48 MOVE @R10, R2 ; after this (R0, R1, R2) must be (0x41, 0x42, 0x43) -000053 -000054 0032 E000 HALT - - -EQU-list: --------------------------------------------------------------------------------------------------------- -N32768 : 0x8000 N2 : 0xFFFE BANK : 0x0100 - -Label-list: --------------------------------------------------------------------------------------------------------- -DATA : 0x0010 NEXT1 : 0x0014 diff --git a/test_programs/moves.out b/test_programs/moves.out deleted file mode 100644 index be93bc19..00000000 --- a/test_programs/moves.out +++ /dev/null @@ -1,51 +0,0 @@ -0x0000 0x0F80 -0x0001 0x0000 -0x0002 0x0038 -0x0003 0x0F84 -0x0004 0x0001 -0x0005 0x0F88 -0x0006 0xFFFF -0x0007 0x0F8C -0x0008 0x8000 -0x0009 0x0F90 -0x000A 0xFFFE -0x000B 0x1310 -0x000C 0x0FB8 -0x000D 0x0000 -0x000E 0xFF80 -0x000F 0x0014 -0x0010 0x0041 -0x0011 0x0042 -0x0012 0x0043 -0x0013 0x0000 -0x0014 0x0FA8 -0x0015 0x0010 -0x0016 0x1FB8 -0x0017 0x0100 -0x0018 0x0F80 -0x0019 0x0001 -0x001A 0x0F84 -0x001B 0x0002 -0x001C 0x0F88 -0x001D 0x0003 -0x001E 0x0FAC -0x001F 0x0100 -0x0020 0x1B38 -0x0021 0x0A80 -0x0022 0x0A84 -0x0023 0x0A88 -0x0024 0x3AC8 -0x0025 0x1B38 -0x0026 0x0F9C -0x0027 0x2309 -0x0028 0x0718 -0x0029 0x0F84 -0x002A 0x0000 -0x002B 0xAFB8 -0x002C 0x0004 -0x002D 0x2F84 -0x002E 0x0001 -0x002F 0x3FB8 -0x0030 0x0100 -0x0031 0x0A48 -0x0032 0xE000 diff --git a/test_programs/moves.rom b/test_programs/moves.rom deleted file mode 100644 index 1b082426..00000000 --- a/test_programs/moves.rom +++ /dev/null @@ -1,51 +0,0 @@ -0000111110000000 -0000000000000000 -0000000000111000 -0000111110000100 -0000000000000001 -0000111110001000 -1111111111111111 -0000111110001100 -1000000000000000 -0000111110010000 -1111111111111110 -0001001100010000 -0000111110111000 -0000000000000000 -1111111110000000 -0000000000010100 -0000000001000001 -0000000001000010 -0000000001000011 -0000000000000000 -0000111110101000 -0000000000010000 -0001111110111000 -0000000100000000 -0000111110000000 -0000000000000001 -0000111110000100 -0000000000000010 -0000111110001000 -0000000000000011 -0000111110101100 -0000000100000000 -0001101100111000 -0000101010000000 -0000101010000100 -0000101010001000 -0011101011001000 -0001101100111000 -0000111110011100 -0010001100001001 -0000011100011000 -0000111110000100 -0000000000000000 -1010111110111000 -0000000000000100 -0010111110000100 -0000000000000001 -0011111110111000 -0000000100000000 -0000101001001000 -1110000000000000 diff --git a/test_programs/ramstacksub.asm b/test_programs/ramstacksub.asm index d00df6f8..1d0efa42 100644 --- a/test_programs/ramstacksub.asm +++ b/test_programs/ramstacksub.asm @@ -2,6 +2,8 @@ ; expects RAM to start at $8000, so works for example in environment "env1" ; done by sy2002 on August, 2nd 2015 + .ORG 0x0000 ; Start address + MOVE 0x8020, R13 ; setup stack pointer MOVE 0x8000, R8 ; source memory area diff --git a/test_programs/ramstacksub.lis b/test_programs/ramstacksub.lis deleted file mode 100644 index c28312c8..00000000 --- a/test_programs/ramstacksub.lis +++ /dev/null @@ -1,58 +0,0 @@ -000001 ; tests RAM, stack and the sub routine calls ASUB and RSUB -000002 ; expects RAM to start at $8000, so works for example in environment "env1" -000003 ; done by sy2002 on August, 2nd 2015 -000004 -000005 0000 0FB4 8020 MOVE 0x8020, R13 ; setup stack pointer -000006 -000007 0002 0FA0 8000 MOVE 0x8000, R8 ; source memory area -000008 0004 0FA4 800A MOVE 0x800A, R9 ; destination memory area -000009 0006 FFB0 0013 RSUB MEMFILL, 1 -000010 -000011 0008 0F80 1111 MOVE 0x1111, R0 -000012 000A 0F84 2222 MOVE 0x2222, R1 -000013 000C 0F88 3333 MOVE 0x3333, R2 -000014 -000015 000E 0FA0 8020 MOVE 0x8020, R8 -000016 0010 0FA4 802A MOVE 0x802A, R9 -000017 0012 FFB0 0007 RSUB MEMFILL, 1 -000018 -000019 0014 0F8C 4444 MOVE 0x4444, R3 -000020 0016 0F90 5555 MOVE 0x5555, R4 -000021 0018 0F94 6666 MOVE 0x6666, R5 -000022 -000023 001A E000 HALT -000024 -000025 -000026 001B 1FB8 0100 MEMFILL ADD 0x0100, R14 ; save register bank -000027 -000028 ;fill some data beginning at R8 -000029 001D 0800 MOVE R8, R0 -000030 001E 0F82 5DA8 MOVE 0x5DA8, @R0++ -000031 0020 0F82 1000 MOVE 0x1000, @R0++ -000032 0022 0F82 0100 MOVE 0x0100, @R0++ -000033 0024 0F82 0010 MOVE 0x0010, @R0++ -000034 0026 0F82 0001 MOVE 0x0001, @R0++ -000035 0028 0F82 1111 MOVE 0x1111, @R0++ -000036 002A 0F82 FFFF MOVE 0xFFFF, @R0++ -000037 -000038 ; copy the data from R8 to R9 -000039 002C 0800 MOVE R8, R0 -000040 002D 0904 MOVE R9, R1 -000041 002E 0F88 0007 MOVE 0x0007, R2 -000042 0030 0F8C 0001 MOVE 0x0001, R3 -000043 -000044 0032 0086 COPY MOVE @R0++, @R1++ -000045 0033 3308 SUB R3, R2 -000046 0034 FFAB FFFC RBRA COPY, !Z -000047 -000048 0036 3FB8 0100 SUB 0x0100, R14 ; restore register bank -000049 0038 0DBC MOVE @R13++, R15 ; return from sub routine -000050 - - -EQU-list: --------------------------------------------------------------------------------------------------------- - -Label-list: --------------------------------------------------------------------------------------------------------- -MEMFILL : 0x001B COPY : 0x0032 diff --git a/test_programs/ramstacksub.out b/test_programs/ramstacksub.out deleted file mode 100644 index 092a5da7..00000000 --- a/test_programs/ramstacksub.out +++ /dev/null @@ -1,57 +0,0 @@ -0x0000 0x0FB4 -0x0001 0x8020 -0x0002 0x0FA0 -0x0003 0x8000 -0x0004 0x0FA4 -0x0005 0x800A -0x0006 0xFFB0 -0x0007 0x0013 -0x0008 0x0F80 -0x0009 0x1111 -0x000A 0x0F84 -0x000B 0x2222 -0x000C 0x0F88 -0x000D 0x3333 -0x000E 0x0FA0 -0x000F 0x8020 -0x0010 0x0FA4 -0x0011 0x802A -0x0012 0xFFB0 -0x0013 0x0007 -0x0014 0x0F8C -0x0015 0x4444 -0x0016 0x0F90 -0x0017 0x5555 -0x0018 0x0F94 -0x0019 0x6666 -0x001A 0xE000 -0x001B 0x1FB8 -0x001C 0x0100 -0x001D 0x0800 -0x001E 0x0F82 -0x001F 0x5DA8 -0x0020 0x0F82 -0x0021 0x1000 -0x0022 0x0F82 -0x0023 0x0100 -0x0024 0x0F82 -0x0025 0x0010 -0x0026 0x0F82 -0x0027 0x0001 -0x0028 0x0F82 -0x0029 0x1111 -0x002A 0x0F82 -0x002B 0xFFFF -0x002C 0x0800 -0x002D 0x0904 -0x002E 0x0F88 -0x002F 0x0007 -0x0030 0x0F8C -0x0031 0x0001 -0x0032 0x0086 -0x0033 0x3308 -0x0034 0xFFAB -0x0035 0xFFFC -0x0036 0x3FB8 -0x0037 0x0100 -0x0038 0x0DBC diff --git a/test_programs/ramstacksub.rom b/test_programs/ramstacksub.rom deleted file mode 100644 index cee5843f..00000000 --- a/test_programs/ramstacksub.rom +++ /dev/null @@ -1,57 +0,0 @@ -0000111110110100 -1000000000100000 -0000111110100000 -1000000000000000 -0000111110100100 -1000000000001010 -1111111110110000 -0000000000010011 -0000111110000000 -0001000100010001 -0000111110000100 -0010001000100010 -0000111110001000 -0011001100110011 -0000111110100000 -1000000000100000 -0000111110100100 -1000000000101010 -1111111110110000 -0000000000000111 -0000111110001100 -0100010001000100 -0000111110010000 -0101010101010101 -0000111110010100 -0110011001100110 -1110000000000000 -0001111110111000 -0000000100000000 -0000100000000000 -0000111110000010 -0101110110101000 -0000111110000010 -0001000000000000 -0000111110000010 -0000000100000000 -0000111110000010 -0000000000010000 -0000111110000010 -0000000000000001 -0000111110000010 -0001000100010001 -0000111110000010 -1111111111111111 -0000100000000000 -0000100100000100 -0000111110001000 -0000000000000111 -0000111110001100 -0000000000000001 -0000000010000110 -0011001100001000 -1111111110101011 -1111111111111100 -0011111110111000 -0000000100000000 -0000110110111100 diff --git a/test_programs/regbank.lis b/test_programs/regbank.lis deleted file mode 100644 index a321d1e8..00000000 --- a/test_programs/regbank.lis +++ /dev/null @@ -1,129 +0,0 @@ -000001 ; Checks, if all register banks are working by putting data in all registers -000002 ; then generating a check sum for each register. The validity of the check sum -000003 ; for each register is shown on the emulated TIL by cycling through all 8 -000004 ; result registers, showing the actual value, then subtracting the correct -000005 ; value, so that next a "0" should be shown. -000006 ; -000007 ; Also, a simple RAM check is included, as this test program does subtractions -000008 ; in RAM and furtheron uses RSUB to call the delay routine, therefore a super -000009 ; small stack on RAM is utilized using two nested function calls -000010 ; -000011 ; Everything works correct, if the TIL displays the following sequence in -000012 ; a loop: 8080, 0000, 1700, 0000 -000013 ; -000014 ; done by sy2002 on August 2015 -000015 -000016 IO$TIL_BASE .EQU 0xFF10 ; Address of TIL-display -000017 -000018 ; about 10.000.000 cycles are needed to delay 1 sec -000019 WAIT_CYCLES1 .EQU 0x1388 ; 0x1388 = decimal 5.000 -000020 WAIT_CYCLES2 .EQU 0x07D0 ; 0x07D0 = decimal 2.000 -000021 -000022 NEXT_BANK .EQU 0x0100 ; added to SR: switch to next bank -000023 -000024 ; expected check sum values -000025 CHECK_R0 .EQU 0x8080 ; sum(1..256) = 32.896 = 0x8080 -000026 CHECK_R1 .EQU 0x1700 ; 256 x 23 = 5.888 = 0x1700 -000027 -000028 ; memory locations -000029 STACK_TOP .EQU 0x8020 ; top of the stack, initial SP -000030 VAR_DIFF .EQU 0x8000 ; variable in RAM to store the -000031 ; difference between a register -000032 ; value and the expected value -000033 -000034 .ORG 0x0000 -000035 -000036 0000 0FB4 8020 MOVE 0x8020, R13 ; setup stack pointer -000037 -000038 0002 AFB8 FF00 OR 0xFF00, R14 ; activate highest register page -000039 0004 0FA0 0100 MOVE 0x0100, R8 ; loop through 256 banks -000040 0006 0FA4 0001 MOVE 0x0001, R9 ; we need to sub 1 often -000041 0008 0FA8 0100 MOVE NEXT_BANK, R10 ; we need to sub 0x100 often -000042 000A 0FAC 0017 MOVE 23, R11 ; we need to move 23 often -000043 -000044 ; fill registers throughout 256 registerbanks with meaningful values -000045 000C 0800 BANK_LOOP MOVE R8, R0 ; move 256 downto 1 in all R0's -000046 000D 0B04 MOVE R11, R1 ; move 23 in all R1's -000047 000E 3A38 SUB R10, R14 ; previous register bank -000048 000F 3920 SUB R9, R8 ; decrease loop counter -000049 0010 FFAB FFFA RBRA BANK_LOOP, !Z ; loop 256 downto 1 (0 exits) -000050 -000051 ; calculate check sums over all registers and store the results in bank 0 -000052 0012 0FA0 00FF MOVE 0x00FF, R8 ; loop only through 255 as we -000053 0014 9FB8 00FF AND 0x00FF, R14 ; are adding everything to bank 0 -000054 -000055 0016 1A38 CHECK_LOOP ADD R10, R14 ; next bank -000056 -000057 0017 0030 MOVE R0, R12 ; use R12 as temp for R0 -000058 0018 0E2C MOVE R14, R11 ; save current bank page -000059 0019 9FB8 00FF AND 0x00FF, R14 ; back to bank 0 -000060 001B 1C00 ADD R12, R0 ; accumulate check sum in R0 -000061 001C 0B38 MOVE R11, R14 ; restore current bank page -000062 -000063 001D 0130 MOVE R1, R12 ; use R12 as temp for R1 -000064 001E 0E2C MOVE R14, R11 ; save current bank page -000065 001F 9FB8 00FF AND 0x00FF, R14 ; back to bank 0 -000066 0021 1C04 ADD R12, R1 ; accumulate check sum in R1 -000067 0022 0B38 MOVE R11, R14 ; restore current bank page -000068 -000069 0023 3920 SUB R9, R8 ; decrease loop counter -000070 0024 FFAB FFF0 RBRA CHECK_LOOP, !Z ; loop 255 downto 1 (0 exits) -000071 -000072 -000073 ; output results to TIL -000074 0026 9FB8 00FF AND 0x00FF, R14 ; switch back to reg bank 0 -000075 0028 0FB0 FF10 MOVE IO$TIL_BASE, R12 ; TIL MMIO display address -000076 -000077 ; display register R0 and the difference to the expected value -000078 002A 0020 DISPLAY_LOOP MOVE R0, R8 ; register = R8 -000079 002B 0FA4 8080 MOVE CHECK_R0, R9 ; expected value = R9 -000080 002D FFB0 0008 RSUB DISPLAY_REG, 1 ; call sub routine -000081 -000082 ; dito R1 -000083 002F 0120 MOVE R1, R8 -000084 0030 0FA4 1700 MOVE CHECK_R1, R9 -000085 0032 FFB0 0003 RSUB DISPLAY_REG, 1 -000086 -000087 0034 FFA0 FFF4 RBRA DISPLAY_LOOP, 1 -000088 -000089 0036 E000 HALT -000090 -000091 ; sub routine to display the register value and the expected value -000092 ; the sub routine uses another sub routine so this is also a nice first -000093 ; test of stacked sub routine calls -000094 ; input: R8 = register, R9 = expected value, R12 = TIL BASE -000095 0037 1FB8 0100 DISPLAY_REG ADD NEXT_BANK, R14 ; next register bank -000096 0039 0831 MOVE R8, @R12 ; display value on TIL -000097 003A FFB0 000A RSUB DELAY, 1 ; wait 1 second -000098 003C 0F80 8000 MOVE VAR_DIFF, R0 ; memory location of variable -000099 003E 0801 MOVE R8, @R0 ; store register value in var -000100 003F 3901 SUB R9, @R0 ; subtract expected value -000101 0040 0071 MOVE @R0, @R12 ; display difference reg vs. expct -000102 0041 FFB0 0003 RSUB DELAY, 1 ; wait 1 second -000103 0043 3FB8 0100 SUB NEXT_BANK, R14 ; previous register bank -000104 0045 0DBC MOVE @R13++, R15 ; return from sub routine -000105 -000106 ; sub routine to wait for about 1sec -000107 0046 1FB8 0100 DELAY ADD NEXT_BANK, R14 ; next register bank -000108 0048 0F84 07D0 MOVE WAIT_CYCLES2, R1 ; outer wait cycles (2.000) -000109 004A 0F80 1388 WAIT_LOOP2 MOVE WAIT_CYCLES1, R0 ; inner wait cycles (5.000) -000110 004C 3F80 0001 WAIT_LOOP1 SUB 1, R0 ; dec inner wait cycles and ... -000111 004E FFAB FFFC RBRA WAIT_LOOP1, !Z ; ... repeat if not zero -000112 0050 3F84 0001 SUB 1, R1 ; dec outer wait cycles and ... -000113 0052 FFAB FFF6 RBRA WAIT_LOOP2, !Z ; ... repeat if not zero -000114 0054 3FB8 0100 SUB NEXT_BANK, R14 ; previous register bank -000115 0056 0DBC MOVE @R13++, R15 ; return from sub routine -000116 - - -EQU-list: --------------------------------------------------------------------------------------------------------- -IO$TIL_BASE : 0xFF10 WAIT_CYCLES1 : 0x1388 WAIT_CYCLES2 : 0x07D0 -NEXT_BANK : 0x0100 CHECK_R0 : 0x8080 CHECK_R1 : 0x1700 -STACK_TOP : 0x8020 VAR_DIFF : 0x8000 - -Label-list: --------------------------------------------------------------------------------------------------------- -BANK_LOOP : 0x000C CHECK_LOOP : 0x0016 DISPLAY_LOOP : 0x002A -DISPLAY_REG : 0x0037 DELAY : 0x0046 WAIT_LOOP2 : 0x004A -WAIT_LOOP1 : 0x004C diff --git a/test_programs/regbank.out b/test_programs/regbank.out deleted file mode 100644 index 55c1c735..00000000 --- a/test_programs/regbank.out +++ /dev/null @@ -1,87 +0,0 @@ -0x0000 0x0FB4 -0x0001 0x8020 -0x0002 0xAFB8 -0x0003 0xFF00 -0x0004 0x0FA0 -0x0005 0x0100 -0x0006 0x0FA4 -0x0007 0x0001 -0x0008 0x0FA8 -0x0009 0x0100 -0x000A 0x0FAC -0x000B 0x0017 -0x000C 0x0800 -0x000D 0x0B04 -0x000E 0x3A38 -0x000F 0x3920 -0x0010 0xFFAB -0x0011 0xFFFA -0x0012 0x0FA0 -0x0013 0x00FF -0x0014 0x9FB8 -0x0015 0x00FF -0x0016 0x1A38 -0x0017 0x0030 -0x0018 0x0E2C -0x0019 0x9FB8 -0x001A 0x00FF -0x001B 0x1C00 -0x001C 0x0B38 -0x001D 0x0130 -0x001E 0x0E2C -0x001F 0x9FB8 -0x0020 0x00FF -0x0021 0x1C04 -0x0022 0x0B38 -0x0023 0x3920 -0x0024 0xFFAB -0x0025 0xFFF0 -0x0026 0x9FB8 -0x0027 0x00FF -0x0028 0x0FB0 -0x0029 0xFF10 -0x002A 0x0020 -0x002B 0x0FA4 -0x002C 0x8080 -0x002D 0xFFB0 -0x002E 0x0008 -0x002F 0x0120 -0x0030 0x0FA4 -0x0031 0x1700 -0x0032 0xFFB0 -0x0033 0x0003 -0x0034 0xFFA0 -0x0035 0xFFF4 -0x0036 0xE000 -0x0037 0x1FB8 -0x0038 0x0100 -0x0039 0x0831 -0x003A 0xFFB0 -0x003B 0x000A -0x003C 0x0F80 -0x003D 0x8000 -0x003E 0x0801 -0x003F 0x3901 -0x0040 0x0071 -0x0041 0xFFB0 -0x0042 0x0003 -0x0043 0x3FB8 -0x0044 0x0100 -0x0045 0x0DBC -0x0046 0x1FB8 -0x0047 0x0100 -0x0048 0x0F84 -0x0049 0x07D0 -0x004A 0x0F80 -0x004B 0x1388 -0x004C 0x3F80 -0x004D 0x0001 -0x004E 0xFFAB -0x004F 0xFFFC -0x0050 0x3F84 -0x0051 0x0001 -0x0052 0xFFAB -0x0053 0xFFF6 -0x0054 0x3FB8 -0x0055 0x0100 -0x0056 0x0DBC diff --git a/test_programs/regbank.rom b/test_programs/regbank.rom deleted file mode 100644 index dab1ae03..00000000 --- a/test_programs/regbank.rom +++ /dev/null @@ -1,87 +0,0 @@ -0000111110110100 -1000000000100000 -1010111110111000 -1111111100000000 -0000111110100000 -0000000100000000 -0000111110100100 -0000000000000001 -0000111110101000 -0000000100000000 -0000111110101100 -0000000000010111 -0000100000000000 -0000101100000100 -0011101000111000 -0011100100100000 -1111111110101011 -1111111111111010 -0000111110100000 -0000000011111111 -1001111110111000 -0000000011111111 -0001101000111000 -0000000000110000 -0000111000101100 -1001111110111000 -0000000011111111 -0001110000000000 -0000101100111000 -0000000100110000 -0000111000101100 -1001111110111000 -0000000011111111 -0001110000000100 -0000101100111000 -0011100100100000 -1111111110101011 -1111111111110000 -1001111110111000 -0000000011111111 -0000111110110000 -1111111100010000 -0000000000100000 -0000111110100100 -1000000010000000 -1111111110110000 -0000000000001000 -0000000100100000 -0000111110100100 -0001011100000000 -1111111110110000 -0000000000000011 -1111111110100000 -1111111111110100 -1110000000000000 -0001111110111000 -0000000100000000 -0000100000110001 -1111111110110000 -0000000000001010 -0000111110000000 -1000000000000000 -0000100000000001 -0011100100000001 -0000000001110001 -1111111110110000 -0000000000000011 -0011111110111000 -0000000100000000 -0000110110111100 -0001111110111000 -0000000100000000 -0000111110000100 -0000011111010000 -0000111110000000 -0001001110001000 -0011111110000000 -0000000000000001 -1111111110101011 -1111111111111100 -0011111110000100 -0000000000000001 -1111111110101011 -1111111111110110 -0011111110111000 -0000000100000000 -0000110110111100 diff --git a/test_programs/til_count.lis b/test_programs/til_count.lis deleted file mode 100644 index 0802a1a4..00000000 --- a/test_programs/til_count.lis +++ /dev/null @@ -1,67 +0,0 @@ -000001 ;; This is the very first "real" QNICE-FPGA test program which is and was used during the -000002 ;; initial development of QNICE-FPGA by sy2002 in July 2015. -000003 ;; -000004 ;; It is inspired by vaxman's original test program "til_count.asm" that displays -000005 ;; a count on the TIL-311 display on the original QNICE/A evaluation board. -000006 -000007 IO$TIL_BASE .EQU 0xFF10 ; Address of TIL-display -000008 IO$TIL_MASK .EQU 0xFF11 ; Mask register of TIL display -000009 -000010 FLAG_C_SET .EQU 0x0004 ; bit pattern for setting the carry flag with OR -000011 FLAG_C_CLEAR .EQU 0xFFFB ; bit pattern for clearing the carry flag with AND -000012 -000013 ; QNICE-FPGA in the current early stage of development is running at about 20 MIPS. As the -000014 ; inner loop consists of two instructions, we need to count to about 10.000.000 for having -000015 ; the effect of an ~1 Hz incrementing counter on the TIL. -000016 ; So we choose WAIT_CYCLES1 as 5.000 equ 0x1388 and WAIT_CYCLES2 as 2.000 equ 0x07D0 -000017 WAIT_CYCLES1 .EQU 0x1388 -000018 WAIT_CYCLES2 .EQU 0x07D0 -000019 -000020 .ORG 0x0000 ; Start address -000021 0000 0F80 0000 MOVE 0x0000, R0 ; Clear R0 -000022 0002 0F84 FF10 MOVE IO$TIL_BASE, R1 ; Base address of TIL-display for output -000023 0004 0FA4 FF11 MOVE IO$TIL_MASK, R9 ; Mask register of TIL-display for selecting which TIL is lit -000024 -000025 ; Write contents of R0 to the TIL-display -000026 0006 0005 LOOP MOVE R0, @R1 -000027 -000028 ; Create mask for TIL digits, so that only those TILs are lit, that are displaying non zero digits -000029 0007 0F90 000F MOVE 0x000F, R4 ; R4 is the resulting mask; at first, we assume all four digits are lit -000030 0009 0F94 F000 MOVE 0xF000, R5 ; R5 is the bit parttern to check, if a certain digit shall be lit -000031 000B 0F9C 0003 MOVE 0x0003, R7 ; R7 is the loop counter -000032 -000033 000D 0518 CREATE_MASK MOVE R5, R6 ; use the pattern and ... -000034 000E 9018 AND R0, R6 ; ... check if one of the bits is set at the digit position implied the mask -000035 000F FFAB 000A RBRA MASK_READY, !Z ; if bits are set, then mask is ready -000036 0011 9FB8 FFFB AND FLAG_C_CLEAR, R14 ; clear C because SHR fills with C (not necessarry, because C is never set before) -000037 0013 6F90 0001 SHR 1, R4 ; make the mask smaller by one bit -000038 0015 6F94 0004 SHR 4, R5 ; move the "scanner pattern" to the next digit (i.e. 4 bits) -000039 0017 3F9C 0001 SUB 1, R7 ; reduce counter (counter necessary to avoid endless loop in case of R0 == 0) -000040 0019 FFAB FFF2 RBRA CREATE_MASK, !Z ; next iteration -000041 -000042 ; Set mask register of TIL-display -000043 001B 0425 MASK_READY MOVE R4, @R9 -000044 -000045 ; waste cycles to approximate a 1 Hz execution -000046 001C 0F8C 07D0 MOVE WAIT_CYCLES2, R3 -000047 001E 0F88 1388 WAIT_LOOP2 MOVE WAIT_CYCLES1, R2 -000048 0020 3F88 0001 WAIT_LOOP1 SUB 1, R2 ; Decrement loop counter -000049 0022 FFAB FFFC RBRA WAIT_LOOP1, !Z ; If not zero, perform next loop -000050 0024 3F8C 0001 SUB 1, R3 -000051 0026 FFAB FFF6 RBRA WAIT_LOOP2, !Z -000052 -000053 0028 1F80 0001 ADD 1, R0 ; Increment R0 -000054 002A FFAB FFDA RBRA LOOP, !Z ; Unconditional jump to display the next value -000055 -000056 002C E000 HALT ; stop the CPU - - -EQU-list: --------------------------------------------------------------------------------------------------------- -IO$TIL_BASE : 0xFF10 IO$TIL_MASK : 0xFF11 FLAG_C_SET : 0x0004 -FLAG_C_CLEAR : 0xFFFB WAIT_CYCLES1 : 0x1388 WAIT_CYCLES2 : 0x07D0 - -Label-list: --------------------------------------------------------------------------------------------------------- -LOOP : 0x0006 CREATE_MASK : 0x000D MASK_READY : 0x001B -WAIT_LOOP2 : 0x001E WAIT_LOOP1 : 0x0020 diff --git a/test_programs/til_count.out b/test_programs/til_count.out deleted file mode 100644 index cc8769f2..00000000 --- a/test_programs/til_count.out +++ /dev/null @@ -1,45 +0,0 @@ -0x0000 0x0F80 -0x0001 0x0000 -0x0002 0x0F84 -0x0003 0xFF10 -0x0004 0x0FA4 -0x0005 0xFF11 -0x0006 0x0005 -0x0007 0x0F90 -0x0008 0x000F -0x0009 0x0F94 -0x000A 0xF000 -0x000B 0x0F9C -0x000C 0x0003 -0x000D 0x0518 -0x000E 0x9018 -0x000F 0xFFAB -0x0010 0x000A -0x0011 0x9FB8 -0x0012 0xFFFB -0x0013 0x6F90 -0x0014 0x0001 -0x0015 0x6F94 -0x0016 0x0004 -0x0017 0x3F9C -0x0018 0x0001 -0x0019 0xFFAB -0x001A 0xFFF2 -0x001B 0x0425 -0x001C 0x0F8C -0x001D 0x07D0 -0x001E 0x0F88 -0x001F 0x1388 -0x0020 0x3F88 -0x0021 0x0001 -0x0022 0xFFAB -0x0023 0xFFFC -0x0024 0x3F8C -0x0025 0x0001 -0x0026 0xFFAB -0x0027 0xFFF6 -0x0028 0x1F80 -0x0029 0x0001 -0x002A 0xFFAB -0x002B 0xFFDA -0x002C 0xE000 diff --git a/test_programs/til_count.rom b/test_programs/til_count.rom deleted file mode 100644 index 4a5d95ee..00000000 --- a/test_programs/til_count.rom +++ /dev/null @@ -1,45 +0,0 @@ -0000111110000000 -0000000000000000 -0000111110000100 -1111111100010000 -0000111110100100 -1111111100010001 -0000000000000101 -0000111110010000 -0000000000001111 -0000111110010100 -1111000000000000 -0000111110011100 -0000000000000011 -0000010100011000 -1001000000011000 -1111111110101011 -0000000000001010 -1001111110111000 -1111111111111011 -0110111110010000 -0000000000000001 -0110111110010100 -0000000000000100 -0011111110011100 -0000000000000001 -1111111110101011 -1111111111110010 -0000010000100101 -0000111110001100 -0000011111010000 -0000111110001000 -0001001110001000 -0011111110001000 -0000000000000001 -1111111110101011 -1111111111111100 -0011111110001100 -0000000000000001 -1111111110101011 -1111111111110110 -0001111110000000 -0000000000000001 -1111111110101011 -1111111111011010 -1110000000000000 diff --git a/test_programs/til_count_rom b/test_programs/til_count_rom deleted file mode 100644 index 4a5d95ee..00000000 --- a/test_programs/til_count_rom +++ /dev/null @@ -1,45 +0,0 @@ -0000111110000000 -0000000000000000 -0000111110000100 -1111111100010000 -0000111110100100 -1111111100010001 -0000000000000101 -0000111110010000 -0000000000001111 -0000111110010100 -1111000000000000 -0000111110011100 -0000000000000011 -0000010100011000 -1001000000011000 -1111111110101011 -0000000000001010 -1001111110111000 -1111111111111011 -0110111110010000 -0000000000000001 -0110111110010100 -0000000000000100 -0011111110011100 -0000000000000001 -1111111110101011 -1111111111110010 -0000010000100101 -0000111110001100 -0000011111010000 -0000111110001000 -0001001110001000 -0011111110001000 -0000000000000001 -1111111110101011 -1111111111111100 -0011111110001100 -0000000000000001 -1111111110101011 -1111111111110110 -0001111110000000 -0000000000000001 -1111111110101011 -1111111111011010 -1110000000000000 diff --git a/tools/asm b/tools/asm new file mode 100755 index 00000000..03e3fb11 --- /dev/null +++ b/tools/asm @@ -0,0 +1,33 @@ +#!/bin/bash + +# +# It turned out that the assembler needs some kind of a preprocessor. +# Since I am too lazy to write one I decided to use the cpp which is +# the purpose of this small wrapper script. Assembling some QNICE +# sources is done invoking this script with the source file name as +# its only parameter. +# + +temp_file=__temp__.asm + +if [ $# -ne 1 ] +then + echo "Usage: asm " + exit +fi + +assembler=`dirname $0`/qasm + +destination=${1/.asm/}.out + +# +# We use the standard C-preprocessor to perform the necessary +# preprocessor steps for the QNICE-assembler. Since the preprocessor +# inserts lines starting with a '#' which the assembler doest +# not like, these are subsequently removed with sed. +# +cpp -E $1 | sed '/^#.*/d' > $temp_file + +$assembler $temp_file $destination + +rm $temp_file diff --git a/tools/qasm.c b/tools/qasm.c new file mode 100644 index 00000000..97013c74 --- /dev/null +++ b/tools/qasm.c @@ -0,0 +1,1065 @@ +/* +** QNICE assembler: This program reads QNICE assembler code from a file and generates, as expected from an assembler :-), +** valid machine code based on this input. +** +** B. Ulmann, JUN-2007, DEC-2007, APR-2008, AUG-2015 +** +** Todo: +** More constant-formats: 'xy', -0x1234, 0b1010010, -b010010 +** +** Known bugs: +** +** 04-JUN-2008: Line numbers in error messages are sometimes a bit off reality (up to -5 has been observed) +*/ + +#include +#include +#include /* For malloc. */ +#include /* For isdigit. */ + +#ifndef TRUE +# define TRUE 1 +# define FALSE !TRUE +#endif + +#undef VERBOSE +#undef DEBUG + +#define STRING_LENGTH 255 + +#define COMMENT_CHAR ';' + +#define INSTRUCTION$NORMAL 0 +#define INSTRUCTION$BRANCH 1 +#define INSTRUCTION$DIRECTIVE 2 + +#define OPERAND$ILLEGAL -1 /* An illegal operand was found */ +#define OPERAND$MISSING 0 +#define OPERAND$LABEL_EQU 1 /* Such an operand can be resolved only during the second pass */ +#define OPERAND$CONSTANT 2 /* Constants can be resolved immediately in the first pass */ +#define OPERAND$RXX 3 +#define OPERAND$AT_RXX 4 +#define OPERAND$AT_RXX_PP 5 +#define OPERAND$AT_MM_RXX 6 + +#define STATE$INITIAL 0 +#define STATE$FINISHED 1 +#define STATE$LABELS_MISSING 2 +#define STATE$NOTHING_YET_DONE 3 + +typedef struct _data_entry +{ + char source[STRING_LENGTH], /* Original source line for printout */ + label[STRING_LENGTH], /* Name of a label if there was one */ + mnemonic[STRING_LENGTH], /* Undecoded mnemonic */ + src_op[STRING_LENGTH], /* Source operand */ + dest_op[STRING_LENGTH], /* Destination operand */ + error_text[STRING_LENGTH]; /* Text of error message if something went wrong during assembly */ + int address, /* Memory address for this instruction/directive */ + number_of_words, /* How many words of data are necessary for this line? */ + *data, /* Pointer to a list of number_of_words-ints holding the resulting data */ + opcode, opcode_type, /* Which opcode and which type* */ + src_op_type, /* If the source operand is a constant, it will be stored here */ + dest_op_type, /* The same holds true for the destination */ + src_op_code, /* The six bits describing the first operand */ + dest_op_code, /* The six bits describing the second operand */ + state; /* STATE$FINISHED, STATE$LABELS_MISSING */ + struct _data_entry *next; +} data_structure; + +typedef struct _equ_entry +{ + char name[STRING_LENGTH]; + int value; + struct _equ_entry *next; +} equ_structure; + +/* +** Global variables: +*/ + +data_structure *gbl$data = 0; +equ_structure *gbl$equs = 0; + +/* +** Expand all tabs by blanks, assuming that tab stops occur every eight columns. +*/ +void expand_tabs(char *dst, char *src) +{ + int i; + + i = 0; + while (*src) + { + i++; + if (*src != '\t') + *dst++ = *src++; + else + { + *dst++ = ' '; + for ( ; (i % 8); i++, *dst++ = ' '); + src++; + } + } + + *dst = (char) 0; +} + +/* +** Convert a string to uppercase. +*/ +void string2upper(char *string) +{ + while (*string) + { + *string = (char) toupper((int) *string); + string++; + } +} + +/* +** Convert a sequence of ASCII chars to a value. +*/ +unsigned int ascii2value(char *string) +{ + unsigned int result = 0; + + string++; /* Skip leading single quote */ + while (*string != '\'') /* We can rely on a trailing single quote */ + { + result <<= 8; + result += *string++; + } + + return result & 0xffff; +} + +/* +** Convert a sequence of '0' and '1' to a value. +*/ +unsigned int binstr2value(char *string) +{ + unsigned int result = 0; + + while (*string) + { + result <<= 1; + if (*string++ == '1') + result += 1; + } + + return result & 0xffff; +} + +/* +** Translate a mnemonic to its corresponding opcode. If mnemonic does not match, FALSE will be returned, otherwise it's TRUE. +*/ +int translate_mnemonic(char *string, int *opcode, int *type) +{ + int i; + static char *normal_mnemonics[] = {"MOVE", "ADD", "ADDC", "SUB", "SUBC", "SHL", "SHR", "SWAP", + "NOT", "AND", "OR", "XOR", "CMP", "RSRVD", "HALT", 0}, + *branch_mnemonics[] = {"ABRA", "ASUB", "RBRA", "RSUB", 0}, + *directives[] = {".ORG", ".ASCII_W", ".ASCII_P", ".EQU", ".BLOCK", 0}; + + if (!string) + return FALSE; + + string2upper(string); + + for (i = 0; normal_mnemonics[i]; i++) /* First try the "normal" mnemonics, i.e. no branches */ + if (!strcmp(string, normal_mnemonics[i])) + { + *type = INSTRUCTION$NORMAL; + *opcode = i; + return TRUE; + } + + for (i = 0; branch_mnemonics[i]; i++) /* Now try the branches */ + if (!strcmp(string, branch_mnemonics[i])) + { + *type = INSTRUCTION$BRANCH; + *opcode = i; + return TRUE; + } + + for (i = 0; directives[i]; i++) + if (!strcmp(string, directives[i])) + { + *type = INSTRUCTION$DIRECTIVE; + *opcode = i; + return TRUE; + } + + return FALSE; +} + +/* +** Local variant of strtok, just better. :-) The first call expects the string to be tokenized as its first argument. +** All subsequent calls only require the second argument to be set. If there is nothing left to be tokenized, a zero pointer +** will be returned. In contrast to strtok this routine will not alter the string to be tokenized since it +** operates on a local copy of this string. +*/ +char *tokenize(char *string, char *delimiters) +{ + static char local_copy[STRING_LENGTH], *position; + char *token; + + if (string) /* Initial call, create a copy of the string pointer */ + { + strcpy(local_copy, string); + position = local_copy; + } + else /* Subsequent call, scan local copy until a delimiter character will be found */ + { + while (*position && strchr(delimiters, *position)) /* Skip delimiters if there are any at the beginning of the string */ + position++; + + token = position; /* Now we are at the beginning of a token (or the end of the string :-) ) */ + + if (*position == '\'') /* Special case: Strings delimited by single quotes won't be split! */ + { + position++; + while (*position && *position != '\'') + position++; + } + + while (*position) + { + position++; + if (!*position || strchr(delimiters, *position)) /* Delimiter found */ + { + if (*position) + *position++ = (char) 0; /* Split string copy */ + return token; + } + } + } + + return NULL; +} + +/* +** replace_extension replaces the extension of a file name with another extension. If there is no extension in the input string +** then the new extension is just concatenated to the input name. +*/ +void replace_extension(char *destination, char *source, char *new_extension) +{ + char *delimiter; + + strcpy(destination, source); + if (!(delimiter = strrchr(destination, '.'))) /* No delimiter found */ + strcat(destination, "."); + else + *(delimiter + 1) = (char) 0; + + strcat(destination, new_extension); +} + +/* +** Print a simple usage text. +*/ +void print_help() +{ + printf("\nUsage:\nqasm [ []]\n\n"); +} + +/* +** Does exactly what you would expect. :-) +*/ +void chomp(char *string) +{ + if (string[strlen(string) - 2] == 0xa || string[strlen(string) - 2] == 0xd) + string[strlen(string) - 2] = (char) 0; + else if (string[strlen(string) - 1] == 0xa || string[strlen(string) - 1] == 0xd) + string[strlen(string) - 1] = (char) 0; +} + +/* +** Remove TABs from the source code +*/ +void remove_tabs(char *cp) +{ + while (*cp++) + if (*cp == 0x9) + *cp = ' '; +} + +/* +** The two following functions remove_trailing_blanks and remove_leading_blanks do exactly what you would expect. :-) +*/ +void remove_trailing_blanks (char *cp) +{ + int i; + + for (i = strlen (cp) - 1; i >= 0 && (*(cp + i) == '\t' || *(cp + i) == ' '); *(cp + i--) = 0); +} + +void remove_leading_blanks (char *string) +{ + char *cp; + + cp = string; + while (*cp == ' ' || *cp == '\t') + cp++; + + while ((*string++ = *cp++)); /* 26.07.2015: strcpy on Mac OS X 10.10.3 traps when copying a string partially to itself... */ +} + +/* +** find_label searches for a given label and returns its address by a pointer. The return value of the function denotes +** success (0) or failure (-1). +*/ +int find_label(char *name, int *address) +{ + data_structure *entry; + + for (entry = gbl$data; entry; entry = entry->next) + if (!strcmp(entry->label, name)) + { + *address = entry->address & 0xffff; + return 0; + } + + return -1; /* No corresponding label found! */ +} + +/* +** search_equ_list searches the list of currently known EQUs for a given entry. It returns -1 if nothing could be found, +** 0 otherwise. The result is returned via the second char-pointer. +*/ +int search_equ_list(char *name, int *value) +{ + equ_structure *entry; + + for (entry = gbl$equs; entry; entry = entry->next) + if (!strcmp(entry->name, name)) + { + *value = entry->value; + return 0; + } + + return -1; +} + +/* +** insert_into_equ_list inserts a new entry into the list of currently known EQUs. If the insert was successful, 0 will be +** returned. -1 denotes a memory problem, 1 denotes a duplicate entry! +*/ +int insert_into_equ_list(char *name, int value) +{ + int i; + equ_structure *entry; + static equ_structure *last; + +#ifdef DEBUG + printf("insert_into_equ_list: >>%s<< = %d/%04X\n", name, value, value); +#endif + if (!(entry = (equ_structure *) malloc(sizeof(equ_structure)))) + { + printf("insert_into_equ_list: Out of memory!\n"); + return -1; + } + + strcpy(entry->name, name); + entry->value = value; + entry->next = (equ_structure *) 0; + + if (!gbl$equs) /* This will be the very first entry in the list! */ + gbl$equs = last = entry; + else /* Not the first entry -> append to the end of the list */ + { + if (!search_equ_list(name, &i)) + return 1; + last = last->next = entry; + } + + return 0; +} + +/* +** Read the complete source file into a simlpe linked list. This list will be +** the basis for all of the following operations and will eventually contain +** the source code as well as the corresponding binary data. +*/ +int read_source(char *file_name) +{ + int counter; + char line[STRING_LENGTH]; + FILE *handle; + data_structure *entry, *previous; + + if (!(handle = fopen(file_name, "r"))) + { + printf("read_source: Unable to open source file >>%s<address = entry->opcode = entry->opcode_type = -1; + entry->src_op_type = entry->dest_op_type = OPERAND$MISSING; + entry->number_of_words = entry->src_op_code = entry->dest_op_code = 0; + entry->data = (int *) 0; + *(entry->label) = *(entry->mnemonic) = *(entry->src_op) = *(entry->dest_op) = *(entry->error_text) = (char) 0; + entry->next = (data_structure *) 0; + + strcpy(entry->source, line); /* Remember the source code line for later analysis and print out */ + + if (previous) /* This is not the first element in the list */ + previous = previous->next = entry; + else + previous = gbl$data; + } + + fclose(handle); + +#ifdef VERBOSE + printf("read_source: %d lines read\n", counter); +#endif + + return 0; +} + +/* +** str2int converts a string in base 16 or base 10 notation to an unsigned integer value. +** Base 16 values require a prefix "0x" or "$" while base 10 value do not require any prefix. +*/ +unsigned int str2int(char *string) +{ + int value; + + if (!string || !*string) /* An empty string is treated as a zero */ + return 0; + + if (!strncmp(string, "0X", 2) || !strncmp(string, "0x", 2)) + sscanf(string + 2, "%x", &value); + else if (!strncmp(string, "-0X", 3) || !strncmp(string, "-0x", 3)) + { + sscanf(string + 3, "%x", &value); + value = -value & 0xffff; + } + else if (!strncmp(string, "0B", 2) || !strncmp(string, "0b", 2)) + value = binstr2value(string + 2); + else if (!strncmp(string, "-0B", 3) || !strncmp(string, "-0b", 3)) + value = -binstr2value(string + 3) & 0xffff; + else if (*string == '$') + sscanf(string + 1, "%x", &value); + else if (*string == '\'' && *(string + strlen(string) - 1) == '\'') + value = ascii2value(string); + else + sscanf(string, "%d", &value); + + return value; +} + +/* +** decode_operand decodes a given operand. Its return value is the type of the operand, the pointer *op_code will be used to +** return the six (!) bits describing the operand. +*/ +int decode_operand(char *operand, int *op_code) +{ + int value, auto_increment, i, flag; + char *p; + + if ((char) toupper((int) *operand) == 'R') /* Maybe a simple register */ + { + flag = 1; /* Pretend it is a register number what follows */ + for (i = 1; i < strlen(operand) - 1; i++) + if (!isdigit(*(operand + i))) + flag = 0; + + if (flag) /* OK - it looks like a register description */ + { + value = str2int(operand + 1); + if (value < 0 || value > 15) /* Maybe it wasn't a register but a label? */ + return OPERAND$LABEL_EQU; + + *op_code = value << 2; + return OPERAND$RXX; + } + else + { + *op_code = 0x3e; + return OPERAND$LABEL_EQU; + } + } + else if (!strncmp(operand, "@R", 2)) /* Simple indirect addressing */ + { + if ((auto_increment = (operand[strlen(operand) - 1] == '+' && operand[strlen(operand) - 2] == '+'))) + operand[strlen(operand) - 2] = (char) 0; + + value = str2int(operand + 2); + if (value < 0 || value > 15) + return OPERAND$ILLEGAL; + + if (auto_increment) + { + *op_code = value << 2 | 2; + return OPERAND$AT_RXX; + } + else + { + *op_code = value << 2 | 1; + return OPERAND$AT_RXX_PP; + } + } + else if (!strncmp(operand, "@--R", 4)) /* Indirect addressing with predecrement */ + { + value = str2int(operand + 4); + if (value < 0 || value > 15) + return OPERAND$ILLEGAL; + + *op_code = value << 2 | 3; + return OPERAND$AT_MM_RXX; + } + /* Constants can be of the form 0x..., 0b..., -0x..., -0b..., or '...' */ + else if (isdigit(*operand) || *operand == '\'' || *operand == '-') + { + *op_code = 0x3e; + return OPERAND$CONSTANT; + } + + *op_code = 0x3e; + return OPERAND$LABEL_EQU; /* Such an operand can only be resolved during the second pass! */ +} + +/* +** assemble does all the real work of the assembler. It reads the source contained in the linked list and fills the +** corresponding elements of the list with addresses and data words as applicable. +*/ +int assemble() +{ + int opcode, type, line_counter, address, i, error_counter = 0, number_of_operands, negate, flag, value, size, + special_char; + char line[STRING_LENGTH], *p, *delimiters = " ,", *token, *sr_bits = "1XCZNVIM"; + data_structure *entry; + + /* First pass: */ +#ifdef DEBUG + printf("assemble: Starting first pass.\n"); +#endif + for (address = line_counter = 1, entry = gbl$data; entry; entry = entry->next, line_counter++) + { + strcpy(line, entry->source); /* Get a local copy of the line and clean it up */ + entry->state = STATE$NOTHING_YET_DONE; /* Still a lot to do */ + if ((p = strchr(line, COMMENT_CHAR))) /* Remove everything after the start of a comment */ + *p = (char) 0; + remove_leading_blanks(line); + remove_trailing_blanks(line); + remove_tabs(line); + + if (!strlen(line)) /* Skip empty lines */ + continue; + + tokenize(line, (char *) 0); /* Initialize tokenizing */ + token = tokenize((char *) 0, delimiters); + + if (translate_mnemonic(token, &opcode, &type)) /* First token is a mnemonic or a directive */ + strcpy(entry->mnemonic, token); + else /* If the first token is neither a mnemonic nor an opcode, assume it is a label */ + { + strcpy(entry->label, token); + token = tokenize((char *) 0, delimiters); /* Next token has to be a valid mnemonic or directive */ + if (!translate_mnemonic(token, &opcode, &type)) + { + sprintf(entry->error_text, "Line %d: No valid mnemonic/directive found (%s)!", line_counter, entry->source); + printf("assemble: %s\n", entry->error_text); + entry->opcode = entry->opcode_type = -1; + error_counter++; + continue; + } + strcpy(entry->mnemonic, token); + } + + entry->opcode = opcode; + entry->opcode_type = type; + entry->address = address; + + if (entry->opcode_type == INSTRUCTION$DIRECTIVE) /* A directive - do something... */ + { + address--; + + /* If the directive .ORG was found, it is now time to change the address */ + if (!strcmp(entry->mnemonic, ".ORG")) + { + entry->state = STATE$FINISHED; + token = tokenize((char *) 0, delimiters); /* Get new address */ + entry->address = -1; + address = str2int(token) - 1; /* - 1 since the address will be incremented later */ + } + else if (!strcmp(entry->mnemonic, ".ASCII_W") || !strcmp(entry->mnemonic, ".ASCII_P")) + { + /* + ** .ASCII_W expects a string of ASCII characters which will be stored one character per word (only the low byte + ** of each word is used, the upper byte is 0). The string will be automatically terminated by a zero-word (quite + ** compatible with the standard way of life in C. + ** + ** .ASCII_P works quite like .ASCII_W but no terminating zero word will be written (thus the P -> Plain). + ** + ** Both variants can deal with the special character '\n' which will be translated to CR/LF. + */ + + /* ASCII constants are enclosed in double quotes! */ + if (!strcmp(entry->mnemonic, ".ASCII_W")) + p = strstr(line, ".ASCII_W") + strlen(".ASCII_W") + 1; /* Get begin of argument, including blanks, so no tokenize! */ + else + p = strstr(line, ".ASCII_P") + strlen(".ASCII_P") + 1; /* Get begin of argument, including blanks, so no tokenize! */ + + remove_leading_blanks(p); + if (*p++ != '"') /* No double quote found! */ + { + sprintf(entry->error_text, "Line %d: Did not find opening double quote!", line_counter); + printf("assemble: %s\n", entry->error_text); + error_counter++; + continue; + } + + if (!(entry->data = (int *) malloc(strlen(p) * sizeof(int) + 1))) /* Maybe one word too much due to trailing " */ + { + printf("assemble (.ASCII_W/.ASCII_P): Out of memory, could not allocate %d words of memory!", (int) strlen(p)); + return -1; + } + + for (special_char = i = 0; i < strlen(p) && *(p + i) != '"'; i++, address++) + { + if (*(p + i) == '\\') + special_char = 1; + else if (special_char && *(p + i) == 'n') + { + *(entry->data + i - 1) = (char) 13; + *(entry->data + i) = (char) 10; + special_char = 0; + } + else if (special_char && *(p + i) != 'n') + { + *(entry->data + i - 1) = *(p + i - 1); + *(entry->data + i) = *(p + i); + } + else + { + special_char = 0; + *(entry->data + i) = 0xff & *(p + i); + } + } + + if (!strcmp(entry->mnemonic, ".ASCII_W")) /* No terminating zero word in case of .ASCII_P. */ + { + *(entry->data + i) = 0; + address++; + } + + if (*(p + i) != '"') + { + sprintf(entry->error_text, "Line %d: WARNING - Did not find closing double quote!", line_counter); + printf("assembler: %s\n", entry->error_text); + } + + entry->number_of_words = i; + if (!strcmp(entry->mnemonic, ".ASCII_W")) + entry->number_of_words++; + + entry->state = STATE$FINISHED; + } + else if (!strcmp(entry->mnemonic, ".BLOCK")) /* .BLOCK expects one argument being the size of the block to be reserved */ + { + token = tokenize((char *) 0, delimiters); /* Get size of block */ + size = str2int(token); + + if (!(entry->data = (int *) malloc(size * sizeof(int)))) + { + printf("assemble (.BLOCK): Out of memory, could not allocate %d words of memory!", (int) strlen(p)); + return -1; + } + + for (i = 0; i < size; i++, address++) + *(entry->data + i) = 0; + + entry->number_of_words = size; + entry->state = STATE$FINISHED; + } + else if (!strcmp(entry->mnemonic, ".EQU")) /* Introduce a string which will equal some value */ + { + token = tokenize((char *) 0, delimiters); + if (insert_into_equ_list(entry->label, str2int(token))) + { + /* + ** Design bug: Since an EQU does not get a corresponding code entry, the following + ** error message will only printed to stdout but not occur in the resulting listing! + */ + sprintf(entry->error_text, "Line %d: Duplicate equ-entry '%s'.", line_counter, entry->label); + printf("assemble: %s\n", entry->error_text); + error_counter++; + } + *(entry->label) = (char) 0; + entry->state = STATE$FINISHED; + entry->address = -1; + } + else + { + sprintf(entry->error_text, "Line %d: Unknown directive >>%s<<. Very strange!", line_counter, entry->mnemonic); + printf("assemble: %s\n", entry->error_text); + error_counter++; + continue; + } + } + else if (entry->opcode_type == INSTRUCTION$NORMAL) /* A simple mnemonic */ + { + entry->number_of_words = 1; /* At least one word to hold the instruction! */ + number_of_operands = entry->opcode == 0xe ? 0 : 2; /* All instructions except HALT require two operands. */ + + if (number_of_operands) /* Read operands. */ + { + if (!(token = tokenize((char *) 0, delimiters))) + { + sprintf(entry->error_text, "Line %d: No first operand found! (%s)", line_counter, entry->source); + printf("assemble: %s\n", entry->error_text); + error_counter++; + continue; + } + strcpy(entry->src_op, token); + + /* Determine type of first operand. */ + if ((entry->src_op_type = decode_operand(entry->src_op, &entry->src_op_code)) == OPERAND$ILLEGAL) + { + sprintf(entry->error_text, "Line %d: Illegal first operand! (%s)", line_counter, entry->source); + printf("assemble: %s\n", entry->error_text); + error_counter++; + continue; + } + + if (entry->src_op_type == OPERAND$CONSTANT || entry->src_op_type == OPERAND$LABEL_EQU) + { + entry->number_of_words++; + address++; + } + + if (number_of_operands > 1) + { + if (!(token = tokenize((char *) 0, delimiters))) + { + sprintf(entry->error_text, "Line %d: No second operand found! (%s)", line_counter, entry->source); + printf("assemble: %s\n", entry->error_text); + error_counter++; + continue; + } + strcpy(entry->dest_op, token); + + /* Determine the type of the second operand. */ + if ((entry->dest_op_type = decode_operand(entry->dest_op, &entry->dest_op_code)) == OPERAND$ILLEGAL) + { + sprintf(entry->error_text, "Line %d: Illegal second operand! (%s)", line_counter, entry->source); + printf("assemble: %s\n", entry->error_text); + error_counter++; + continue; + } + + if ((entry->dest_op_type == OPERAND$CONSTANT || entry->dest_op_type == OPERAND$LABEL_EQU) && + strcmp(entry->mnemonic, "CMP")) /* A constant as destination is OK only for CMP! */ + { + entry->number_of_words++; + address++; + sprintf(entry->error_text, "Line %d: A constant as destination operand ('%s') may not be what you wanted.", + line_counter, entry->dest_op); + printf("assemble: %s\n", entry->error_text); + /* This is just a warning, so no increment of error_counter is necessary! */ + } + } + } + + if (!(entry->data = (int *) malloc(entry->number_of_words * sizeof(int)))) + { + printf("assemble: Out of memory, could not allocate %d words of memory!", (int) strlen(p)); + return -1; + } + + entry->data[0] = (entry->opcode << 12 | ((entry->src_op_code & 0x3f) << 6) | ((entry->dest_op_code) & 0x3f)) & 0xffff; + + i = 1; + if (entry->src_op_type == OPERAND$CONSTANT) + *(entry->data + i++) = str2int(entry->src_op) & 0xffff; + + if (entry->dest_op_type == OPERAND$CONSTANT) + *(entry->data + i) = str2int(entry->dest_op) & 0xffff; + } + else if (entry->opcode_type == INSTRUCTION$BRANCH) /* Ups, a branch! */ + { + entry->number_of_words = 1; /* At least one word to hold the instruction! */ + + /* A branch always has two operands! */ + if (!(token = tokenize((char *) 0, delimiters))) + { + sprintf(entry->error_text, "Line %d: No first branch operand found! (%s)", line_counter, entry->source); + printf("assemble: %s\n", entry->error_text); + error_counter++; + continue; + } + strcpy(entry->src_op, token); + + if (!(token = tokenize((char *) 0, delimiters))) + { + sprintf(entry->error_text, "Line %d: No second branch operand found! (%s)", line_counter, entry->source); + printf("assemble: %s\n", entry->error_text); + error_counter++; + continue; + } + strcpy(entry->dest_op, token); + + /* Now we have both operands of the branch and the branch itself as well. Decode the first operand. */ + if ((entry->src_op_type = decode_operand(entry->src_op, &entry->src_op_code)) == OPERAND$ILLEGAL) + { + sprintf(entry->error_text, "Line %d: Illegal first operand! (%s)", line_counter, entry->source); + printf("assemble: %s\n", entry->error_text); + error_counter++; + continue; + } + + /* Now decode the second operand: Is it negated? Which flag is it? */ + p = entry->dest_op; + if ((negate = (*(entry->dest_op) == '!'))) + p++; + + for (flag = 0; sr_bits[flag]; flag++) + if (sr_bits[flag] == *p) + break; + + if (flag > 7) + { + sprintf(entry->error_text, "Line %d: Illegal condition flag! (%s)", line_counter, entry->source); + printf("assemble: %s\n", entry->error_text); + error_counter++; + continue; + } + + /* Now prepare for memory allocation and construction of the instruction itself. */ + if (entry->src_op_type == OPERAND$CONSTANT || entry->src_op_type == OPERAND$LABEL_EQU) + { + entry->number_of_words++; + address++; + } + + if (!(entry->data = (int *) malloc(entry->number_of_words * sizeof(int)))) + { + printf("assemble: Out of memory, could not allocate %d words of memory!", (int) strlen(p)); + return -1; + } + + /* Assemble the instruction itself */ + entry->data[0] = (0xf000 | + ((entry->src_op_code & 0x3f) << 6) | ((entry->opcode & 3) << 4) | ((negate & 1) << 3) | (flag & 0x7)) + & 0xffff; + + if (entry->src_op_type == OPERAND$CONSTANT) /* Labels are no constants in this context since they are not known in advance */ +/* entry->data[1] = (str2int(entry->src_op) - address - 1) & 0xffff; This was the old behaviour */ + entry->data[1] = str2int(entry->src_op) & 0xffff; + } + else + { + sprintf(entry->error_text, "Line %d: Unknown opcode type %d! Very strange error!", line_counter, entry->opcode_type); + printf("assemble: %s\n", entry->error_text); + error_counter++; + } + address++; + } + + /* Second pass: */ +#ifdef DEBUG + printf("assemble: Starting second pass.\n"); +#endif + for (entry = gbl$data; entry; entry = entry -> next) + { + i = 1; /* Index for data word array */ + if (entry->src_op_type == OPERAND$LABEL_EQU) /* Still unresolved label or equ! */ + { + value = 0; + if (search_equ_list(entry->src_op, &value)) + if (find_label(entry->src_op, &value)) + { + sprintf(entry->error_text, "Line %d: Unresolved label or equ >>%s<src_op); + printf("assemble: %s\n", entry->error_text); + error_counter++; + continue; + } + + if (entry->opcode_type == INSTRUCTION$BRANCH && *entry->mnemonic == 'R') /* Relative branch or subroutine call */ + entry->data[i++] = (value - entry->address - 2) & 0xffff; /* - 2 since address is a constant and occupies the next cell */ + else + entry->data[i++] = value; + } + + if (entry->dest_op_type == OPERAND$LABEL_EQU) /* Still unresolved label or equ! */ + { + value = 0; + if (search_equ_list(entry->dest_op, &value)) + if (find_label(entry->dest_op, &value)) + { + sprintf(entry->error_text, "Line %d: Unresolved label or equ >>%s<dest_op); + printf("assemble: %s\n", entry->error_text); + error_counter++; + continue; + } + + entry->data[i] = value; + } + } + + return error_counter; +} + +/* +** write_result scans the complete linked list containing the source code as well as the resulting binary code and creates a +** (binary) output file and the corresponting listing file. +*/ +int write_result(char *output_file_name, char *listing_file_name) +{ + int line_counter, i; + char address_string[STRING_LENGTH], data_string[STRING_LENGTH], line[STRING_LENGTH], second_word[STRING_LENGTH]; + FILE *output_handle, /* file handle for binary output data */ + *listing_handle; + data_structure *entry; + equ_structure *equ; + + if (!(output_handle = fopen(output_file_name, "w"))) + { + printf("write_result: Unable to open output file >>%s<>%s<next) + { + /* Write listing */ + if (*(entry->error_text)) /* If there was an error, print it preceeding the erroneous line */ + fprintf(listing_handle, "\n*** %s ***\n", entry->error_text); + + *address_string = *data_string = *second_word = (char) 0; + if (entry->address != -1) + { + sprintf(address_string, "%04X", entry->address & 0xffff); + if (entry->number_of_words) + sprintf(data_string, "%04X", entry->data[0] & 0xffff); + } + + if (entry->number_of_words == 2) /* Many instructions require two words, but should be displayed in a single line */ + sprintf(second_word, "%04X", entry->data[1] & 0xffff); + + expand_tabs(line, entry->source); + fprintf(listing_handle, "%06d %4s %4s %4s %s\n", ++line_counter, address_string, data_string, second_word, line); + if (entry->address != -1) /* Write binary data */ + fprintf(output_handle, "0x%4s 0x%4s\n", address_string, data_string); + + for (i = 1; i < entry->number_of_words; i++) /* If there is additional data as in .ASCII_W, write it */ + { + if (entry->number_of_words > 2) + fprintf(listing_handle, " %04X %04X\n", entry->address + i, entry->data[i]); + fprintf(output_handle, "0x%04X 0x%04X\n", entry->address + i, entry->data[i]); + } + } + + /* Generate a list of defined EQUs */ + fprintf(listing_handle, + "\n\nEQU-list:\n--------------------------------------------------------------------------------------------------------"); + for (i = 0, equ = gbl$equs; equ; equ = equ->next, i++) + { + if (!(i % 3)) + fprintf(listing_handle, "\n"); + fprintf(listing_handle, "%-24s: 0x%04X ", equ->name, equ->value & 0xffff); + } + + /* Generate a list of labels */ + fprintf(listing_handle, "\n\nLabel-list:\n--------------------------------------------------------------------------------------------------------"); + for (i = 0, entry = gbl$data; entry; entry = entry->next) + { + if(!*(entry->label)) + continue; + + if (!(i++ % 3)) + fprintf(listing_handle, "\n"); + fprintf(listing_handle, "%-24s: 0x%04X ", entry->label, entry->address & 0xffff); + } + fprintf(listing_handle, "\n"); + + fclose(output_handle); + fclose(listing_handle); + + return 0; +} + +int main(int argc, char **argv) +{ + int rc; + char *source_file_name, output_file_name[STRING_LENGTH], listing_file_name[STRING_LENGTH]; + + if (argc < 2 || argc > 4) + print_help(); + else + { + source_file_name = argv[1]; + *output_file_name = *listing_file_name = (char) 0; + + if (argc > 2) /* Output file name explicitly stated */ + { + strcpy(output_file_name, argv[2]); + if (argc > 3) /* Listing file name explicitly stated */ + strcpy(listing_file_name, argv[3]); + } + + if (!*output_file_name) + replace_extension(output_file_name, source_file_name, "bin"); + + if (!*listing_file_name) + { + if (*output_file_name) + replace_extension(listing_file_name, output_file_name, "lis"); + else + replace_extension(listing_file_name, source_file_name, "lis"); + } + +#ifdef VERBOSE + printf("Reading >>%s<<, writing >>%s<< (bin) and >>%s<< (lis).\n", + source_file_name, output_file_name, listing_file_name); +#endif + + if ((rc = read_source(source_file_name))) + return rc; + + if ((rc = assemble()) > 0) + { + printf("main: There were %d errors during assembly! No files written!\n", rc); + return rc; + } + else if (rc < 0) + { + printf("main: There was an unrecoverable error during assembly (%d)! No files written!\n", rc); + return rc; + } + + if ((rc = write_result(output_file_name, listing_file_name))) + return rc; + } + + return 0; +} diff --git a/vhdl/env1_globals.vhd b/vhdl/env1_globals.vhd index 31c97c1d..063ac77a 100644 --- a/vhdl/env1_globals.vhd +++ b/vhdl/env1_globals.vhd @@ -10,16 +10,16 @@ use IEEE.STD_LOGIC_1164.all; package env1_globals is -- file name and file size (in lines) of the file that is converted to the ROM located at 0x0000 -constant ROM_FILE : string := "../test_programs/regbank.rom"; -constant ROM_SIZE : integer := 87; +constant ROM_FILE : string := "../test_programs/bram.rom"; +constant ROM_SIZE : integer := 34; -- size of lower register bank: should be 256 -- set to 16 during development for faster synthesis, routing, etc. -constant SHADOW_REGFILE_SIZE : integer := 256; +constant SHADOW_REGFILE_SIZE : integer := 16; -- size of the block RAM in 16bit words: should be 32768 -- set to 256 during development for tracability during simulation -constant BLOCK_RAM_SIZE : integer := 32768; +constant BLOCK_RAM_SIZE : integer := 256; end env1_globals; diff --git a/vhdl/qnice_cpu.vhd b/vhdl/qnice_cpu.vhd index b4ecd206..793ae69c 100644 --- a/vhdl/qnice_cpu.vhd +++ b/vhdl/qnice_cpu.vhd @@ -35,16 +35,16 @@ architecture beh of QNICE_CPU is component TriState_Buffer is generic ( DATA_WIDTH : integer range 1 to 32 -); -port ( +); +port ( I_CLK : in std_logic; -- synchronized with bidir bus - I_DIR_CTRL : in std_logic; -- 3-state enable input, high=output, low=input - + I_DIR_CTRL : in std_logic; -- 3-state enable input, high=output, low=input + IO_DATA : inout std_logic_vector(DATA_WIDTH - 1 downto 0); -- data to/from external pin on bidir bus - + I_DATA_TO_EXTERNAL : in std_logic_vector(DATA_WIDTH - 1 downto 0); -- data to send over bidir bus O_DATA_FROM_EXTERNAL : out std_logic_vector(DATA_WIDTH - 1 downto 0) -- data received over bidir bus -); +); end component; -- QNICE specific register file @@ -334,15 +334,39 @@ begin fsmCpuDataValid <= '0'; fsmNextCpuState <= cs_std_seq; fsmInstruction <= (others => '0'); - + +-- BTW: as soon as all this works, plus some more unit tests especially for some to be found complicated BRAM borderline +-- cases (e.g. ADD @R1, @R2, everything in RAM plus before and after some instructions that challenge the asyncreset), +-- I should checkin the ISA V1.2 docu PDF (create a qnice/doc folder) +-- check in the current assembler and stuff (inside the qnice folder), the emulator (within qnice folder, and the other +-- material from there) and call it "V1.0" (before nerd session), because this is then a fully fledged +-- QNICE CPU working in the initival ENV1 scenario. V1.1 could then be: plus UART plus Bernd's monitor is running. +-- Later, V2.0 might be the new ISA plus another scenario than env1, e.g. QBM-1. +-- the tagging in GIT/GITHUB is important, so that in a later "retro" session, we can compare ISA 1.2 in env1 with +-- a full "QBM-1" computer having the new ISA (e.g. new CMP command, etc.). +-- Update all tools in qnice folder to vaxman's latest material from SourceForge. + +-- @TODO (priority 2): Completely re-do the Tristate buffer topic. The lenghty wait-state and the #0000 situation when +-- writing are not necessary! As we have enough flip-flops, the Tristate handling can be done in "real-time". +-- as this has probably some pretty far reaching impact, the question is when to implement this. On the other hand, +-- the longer we wait, the bigger the impact will be. Some analysis with some MOVE, ADD & Co indirect @memory read/write +-- scenario will for sure help and create a more clear view. + -- as the previous state sets the direction control to read and the address to a meaningful value -- (i.e. 0 after cs_reset or current PC afterwards), the DATA_driver will take care, that at the -- falling edge of cs_fetch's clock cycle, DATA_From_Bus will contain the next opcode when cs_fetch => - fsmInstruction <= DATA_From_Bus; -- valid at falling edge - fsmPC <= PC + 1; - fsm_reg_read_addr1 <= DATA_From_Bus(11 downto 8); -- read Src register number - fsm_reg_read_addr2 <= DATA_From_Bus(5 downto 2); -- rest Dst register number + -- add wait cycles, if necessary (e.g. due to slow RAM) + if WAIT_FOR_DATA = '1' then + fsmNextCpuState <= cs_fetch; + + -- data from bus is available + else + fsmInstruction <= DATA_From_Bus; -- valid at falling edge + fsmPC <= PC + 1; + fsm_reg_read_addr1 <= DATA_From_Bus(11 downto 8); -- read Src register number + fsm_reg_read_addr2 <= DATA_From_Bus(5 downto 2); -- rest Dst register number + end if; when cs_decode => -- source and destination values in case of direct register addressing modes @@ -418,8 +442,7 @@ begin else -- read the indirect value from the bus and store it fsmSrc_Value <= DATA_FROM_Bus; - - + -- perform post increment if Src_Mode = amIndirPostInc then -- special handling of SR and PC as they are not stored in the register file @@ -583,7 +606,7 @@ begin end case; end if; end if; - + when cs_exepost_sub => fsmDataToBus <= DATA_To_Bus; fsmCpuDataDirCtrl <= '1';