From 390c734a61a54aa65af1306b0d015295c2af9e8c Mon Sep 17 00:00:00 2001 From: Gwenhael Le Moine Date: Wed, 20 Mar 2024 07:41:14 +0100 Subject: [PATCH] 2007-01-13: Updated to version 42 Signed-off-by: Gwenhael Le Moine --- DEBUGGER.TXT | 313 +++++ EMU48.EXE | Bin 0 -> 266240 bytes EMU48.TXT | 460 ++++++ EMU48PLUS.TXT | 29 + PROBLEMS.TXT | 56 + SOURCE/APPLE.C | 239 ++++ SOURCE/APPLE.H | 10 + SOURCE/CHANGES.TXT | 3265 +++++++++++++++++++++++++++++++++++++++++++ SOURCE/CHECKBOX.BMP | Bin 0 -> 250 bytes SOURCE/COLOR.H | 27 + SOURCE/CURSOR.C | 89 ++ SOURCE/DBGTOOL.BMP | Bin 0 -> 1198 bytes SOURCE/DDESERV.C | 169 +++ SOURCE/DEBUGDLL.C | 1082 ++++++++++++++ SOURCE/DEBUGGER.C | 2927 ++++++++++++++++++++++++++++++++++++++ SOURCE/DEBUGGER.H | 37 + SOURCE/DISASM.C | 1937 +++++++++++++++++++++++++ SOURCE/DISPLAY.C | 674 +++++++++ SOURCE/EMU48.C | 1615 +++++++++++++++++++++ SOURCE/EMU48.H | 347 +++++ SOURCE/EMU48.ICO | Bin 0 -> 1078 bytes SOURCE/EMU48.RC | 833 +++++++++++ SOURCE/EMU48.XML | 22 + SOURCE/EMU48DLL.C | 585 ++++++++ SOURCE/EMU48DLL.DSP | 256 ++++ SOURCE/EMU48DLL.DSW | 29 + SOURCE/EMU48DLL.H | 642 +++++++++ SOURCE/ENGINE.C | 632 +++++++++ SOURCE/EXTERNAL.C | 188 +++ SOURCE/Emu48.dsp | 372 +++++ SOURCE/Emu48.dsw | 29 + SOURCE/FETCH.C | 778 +++++++++++ SOURCE/FILES.C | 1553 ++++++++++++++++++++ SOURCE/I28F160.C | 716 ++++++++++ SOURCE/I28F160.H | 41 + SOURCE/IO.H | 150 ++ SOURCE/KEYBOARD.C | 129 ++ SOURCE/KEYMACRO.C | 339 +++++ SOURCE/KML.C | 2269 ++++++++++++++++++++++++++++++ SOURCE/KML.H | 125 ++ SOURCE/MOPS.C | 1762 +++++++++++++++++++++++ SOURCE/OPCODES.C | 2482 ++++++++++++++++++++++++++++++++ SOURCE/OPCODES.H | 445 ++++++ SOURCE/OPS.H | 488 +++++++ SOURCE/PCH.C | 5 + SOURCE/PCH.H | 33 + SOURCE/RESOURCE.H | 225 +++ SOURCE/RPL.C | 419 ++++++ SOURCE/SERIAL.C | 383 +++++ SOURCE/SETTINGS.C | 222 +++ SOURCE/STACK.C | 674 +++++++++ SOURCE/TIMER.C | 439 ++++++ SOURCE/TYPES.H | 110 ++ SOURCE/gpl.txt | 340 +++++ 54 files changed, 30991 insertions(+) create mode 100644 DEBUGGER.TXT create mode 100644 EMU48.EXE create mode 100644 EMU48.TXT create mode 100644 EMU48PLUS.TXT create mode 100644 PROBLEMS.TXT create mode 100644 SOURCE/APPLE.C create mode 100644 SOURCE/APPLE.H create mode 100644 SOURCE/CHANGES.TXT create mode 100644 SOURCE/CHECKBOX.BMP create mode 100644 SOURCE/COLOR.H create mode 100644 SOURCE/CURSOR.C create mode 100644 SOURCE/DBGTOOL.BMP create mode 100644 SOURCE/DDESERV.C create mode 100644 SOURCE/DEBUGDLL.C create mode 100644 SOURCE/DEBUGGER.C create mode 100644 SOURCE/DEBUGGER.H create mode 100644 SOURCE/DISASM.C create mode 100644 SOURCE/DISPLAY.C create mode 100644 SOURCE/EMU48.C create mode 100644 SOURCE/EMU48.H create mode 100644 SOURCE/EMU48.ICO create mode 100644 SOURCE/EMU48.RC create mode 100644 SOURCE/EMU48.XML create mode 100644 SOURCE/EMU48DLL.C create mode 100644 SOURCE/EMU48DLL.DSP create mode 100644 SOURCE/EMU48DLL.DSW create mode 100644 SOURCE/EMU48DLL.H create mode 100644 SOURCE/ENGINE.C create mode 100644 SOURCE/EXTERNAL.C create mode 100644 SOURCE/Emu48.dsp create mode 100644 SOURCE/Emu48.dsw create mode 100644 SOURCE/FETCH.C create mode 100644 SOURCE/FILES.C create mode 100644 SOURCE/I28F160.C create mode 100644 SOURCE/I28F160.H create mode 100644 SOURCE/IO.H create mode 100644 SOURCE/KEYBOARD.C create mode 100644 SOURCE/KEYMACRO.C create mode 100644 SOURCE/KML.C create mode 100644 SOURCE/KML.H create mode 100644 SOURCE/MOPS.C create mode 100644 SOURCE/OPCODES.C create mode 100644 SOURCE/OPCODES.H create mode 100644 SOURCE/OPS.H create mode 100644 SOURCE/PCH.C create mode 100644 SOURCE/PCH.H create mode 100644 SOURCE/RESOURCE.H create mode 100644 SOURCE/RPL.C create mode 100644 SOURCE/SERIAL.C create mode 100644 SOURCE/SETTINGS.C create mode 100644 SOURCE/STACK.C create mode 100644 SOURCE/TIMER.C create mode 100644 SOURCE/TYPES.H create mode 100644 SOURCE/gpl.txt diff --git a/DEBUGGER.TXT b/DEBUGGER.TXT new file mode 100644 index 0000000..7c68978 --- /dev/null +++ b/DEBUGGER.TXT @@ -0,0 +1,313 @@ +Debugger in Emu48/Tools/Debugger... +----------------------------------- + +This is a short description of the internal assembly debugger of Emu48. + +The debugger was designed to help customers inspecting assembler code objects, a part that cannot be handled satisfactorily by the JAZZ package. Thanks to Mika Heiskanen and all the others supporting this great program. + +After starting the debugger the emulation will stop at the current program counter position. The emulation will continue after closing the debugger window. Please remember that the clock now shows the wrong time. + + +1.) Menu Debug + +- Run F5 + +Continue calculator emulation under debugger control. The emulation will stop at a breakpoint. Please remember that the emulation speed is slower than without debugger control. + +- Run to Cursor F6 + +Execute program until address at cursor position is reached. Breakpoints are still active and may stop execution before. + +- Step Into F7 + +Execute one code instruction. + +- Step Over F8 + +Execute a GOSUB, GOSUBL or GOSBVL as one instruction. Normally the instruction cursor will set to the position behind the GOSUB instruction. + +But this makes trouble in the following code part: + + GOSUB + + NIBASC /Hello world/ ++ C=RSTK + +The program counter will never reach the address behind the GOSUB instruction. The debugger solve this problem by breaking the emulation when the stack has the same level before the GOSUB instruction. In this example the single step execution will continue after the C=RSTK instruction. + +- Step Out F9 + +Continue the program until a RTI, RTN, RTNC, RTNCC, RTNNC, RTNSC, RTNSXN, RTNYES instruction is found above the current stack level. + +At some code constructions (mostly used to save space on the hardware stack) like + + C=RSTK + PC=C + +and + + C=RSTK + RSTK=C + RTN + +the stop address will be wrong. The problem in both code fragments is the C=RSTK opcode. In the first example there is no RTN instruction to stop. In the second one the C=RSTK instruction purge the original return address and then the RSTK=C instruction is interpreted as a GOSUB instruction. + +In opposite the following code will work fine: + + RSTK=C + .. + code <- F9 was pressed here + .. + GOSUB - + C=RSTK + RTN <- emulation will stop after this instruction +- RTN + +So be careful using the F9 key. + +- Break F11 + +Stops the emulation at the current program counter position. + + +2.) Menu Breakpoints + +- Set Breakpoint F2 + +Toggle a code breakpoint at the cursor position in the Code window. + +- Edit Breakpoints... + +You get a sorted list of all current breakpoints. When the breakpoint is checked it's enabled otherwise it's disabled. With "Add" you can add a new or enable an existing breakpoint, with "Delete" you can delete the selected ones. Addresses greater than #FFFFF are cut after the fifths nibble. When adding a new breakpoint, you must select if this is a "Code", "RPL", "Memory Access", "Memory Read" or "Memory Write" breakpoint. + + - "Code" stop before opcode execution on this address + - "RPL" stop on the first opcode of the selected RPL address + - "Memory Access" stop before reading or writing to the selected address + - "Memory Read" stop before reading the selected address + - "Memory Write" stop before writing to the selected address + +With a left mouse button double click on a breakpoint you can toggle the check box inside. When you use the space key instead, on all selected breakpoints the check box is toggled. + +- Clear All Breakpoints + +Clear all address specific breakpoints. + +- NOP3 Code Breakpoints + +What are NOP3 code breakpoints? As you know user programs are loaded somewhere in memory and can be moved after a garbage collection. So it's very difficult to break a user program at a hard set breakpoint with F2. To solve this problem the debugger will stop emulation at a NOP3 opcode. So you can easily add a NOP3 command into your sources to force a break condition. To enable this you have to check this item. + +NOP3 and NOP3, what's the difference? The Saturn CPU has no NOP command, so NOP3 is an opcode that is three nibbles long and doesn't change a register. In the HP SASM.DOC document two different opcodes are defined for NOP3: + + Opcode 820 for HST=0 0 + +and + + Opcode 420 for GOC + (next line) + +In the assembler of the HPTOOLS 3.x package NOP3 is defined as opcode 820. The advantage of the opcode is that the execution time is always the same, independent from the carry flag. This code is used in the HP48 ROM as well. So I decided to use the GOC opcode for a code breakpoint condition. + +A short example how to use a NOP3 Code breakpoint: + +ASSEMBLE + NIBASC /HPHP48-E/ + +BREAK MACRO + CON(3) #024 NOP3 + ENDM + +RPL +CODE + BREAK code breakpoint + + GOSBVL =SAVPTR save register + + GOSUB + problem for step over + NIBASC /Hello world/ ++ C=RSTK + + GOVLNG =GETPTRLOOP +ENDCODE + +- CODE Object Breakpoints + +If this item is checked, the debugger stops program execution at the first instruction of every DOCODE object which isn't located in ROM. For inspecting DOCODE objects in ROM use address CODE breakpoints instead please. + +- RPL Breakpoints + +If this item is checked, the debugger stops program execution on every instruction called after a PC=(A) or PC=(C) opcode. This is normally the begin of a new RPL command. RPL breakpoints use a "-R" marker instead of the assembler "->" PC position marker. + + +3.) Menu Interrupts + +- Step Over Interrupts + +If this item is checked, interrupt handler code will be skipped. This option is useful when you don't want to debug the interrupt handler. But be careful, when you disable the interrupts all code until interrupt enable belong to the interrupt handler code and couldn't executed in single step any more. Enabled breakpoints are still active. + +You can also use this option if you want to quit the interrupt handler. Just check this option, press F7 for "Step Into" for stopping the debugger behind the RTI instruction, and uncheck this option again. + + +4.) Menu Info + +- Last Instructions... + +This is a short viewer for the last 255 executed CPU addresses. The disassembled opcode maybe wrong, because only the CPU address of each command was saved and memory mapping may have changed meanwhile. In the "Last Instructions" dialog you can copy selected lines to the clipboard or clear this list. + +- Profiler... + +This opens a small toolbox window which shows the number of CPU cycles and the corresponding execution time of the instruction sequence between the last two breakpoints. The CPU cycles are only approximate values, the real cycles are depending mostly on the used ROM to Saturn CPU core interface. + +- Write Only Registers... + +Some of the display registers have a different meaning on reading and writing. This dialog shows the data written to the write only I/O registers. + + +5.) Code window + +This windows shows you the disassembled code. The line with the current PC is marked with a "->" or "-R" between the address and the disassembly. + +You can use the UP, PAGE UP, DOWN and PAGE DOWN keys to scroll the window content. There is one strange behavior, when you move to higher addresses the debugger is able to disassemble the next line correctly, but when you move to cursor to lower addresses the debugger does not know if this address is at the begin or inside of an opcode. In result you get wrong disassembled lines. + +Context menu pressing the right mouse button: + +- Go to address... G + +Moves the cursor to the specified code address. + +- Go to PC + +Sets the cursor to the actual position of the PC. + +- Set breakpoint F2 + +Toggle a code breakpoint at the cursor position in the Code window. + +- Set PC to selection + +Set the PC to the cursor position. Be careful with this command, you change the execution order of the commands! + + +6.) Register window + +Here you can see the actual contents of the CPU registers. The values are only updated at a program execution stop. All changed CPU registers are highlighted. + +With the left mouse button you change the content of the register. On bit registers, like CY and Mode, the state change immediately without any request. + + +7.) Memory window + +This windows shows the memory content in the selected context. + +You can use the arrow, PAGE UP and PAGE DOWN keys to move the cursor to a memory position. With a double click on the left mouse button (only in Map mode) you can change the content of the two addresses. When the memory position is read only (ROM or write protected RAM) the content wouldn't change. + +Context menu pressing the right mouse button: + +- Go to address... G + +Moves the cursor to the specified memory address. + +- Go to PC + +Sets the cursor to the actual position of the PC. + +- Go to D0 + +Sets the cursor to the actual position of the D0 register. + +- Go to D1 + +Sets the cursor to the actual position of the D1 register. + +- Go to Stack + +Sets the cursor to the return address placed in the top level of the stack. + +- Follow + +Follow is a Pop-up menu to change the address behavior of the memory window. Normally the address of the memory window is static and only change by entering a new address. With Follow the memory window view follow the content of a selected address or register. In follow mode the memory window is only updated after an emulation step. + +- Follow none + +This is the default mode. The address of the memory window is static. + +- Follow Address Content + +This is a special mode of indirect addressing. You can specify an address which content will we interpreted as memory pointer. The memory window follow this memory pointer. + +- Follow Register PC/D0/D1 + +The memory window follow the content of the selected register. + +- Find... F + +Calls the "Find" dialog box, allowing you to search for a data sequence in hexadecimal or ASCII mode. The search area is selected by the memory view Mapping mode described in the following section. When you close the "Find" dialog box, you will loose all saved strings in the data combo box. + +- Mapping + +Mapping is a Pop-up menu to select the memory view of the Memory window. Normally the CPU see only 512KB of the total memory, the rest is banked or covered by other modules. The following menu entries select the memory chip connected with the chosen Chip Select signal of the MMU. The connections are calculator model dependent. + +- Mapping Map + +This is the default mode. Here the Memory window shows what the CPU see. In this mode you can also change the memory content of writeable memory. + +- Mapping NCE1/NCE2/CE1/CE2/NCE3 + +Here the Memory window shows the content of the selected Chip Select signal. The content is showed in a linear address model and it's content can't be changed in this mode. + +Here's a comparison of the mapping of the emulated calculator models: + +Abbreviations: ROM = Read Only Memory + RAM = Random Access Memory + Flash = electrical reprogramming ROM + Slt = Memory Card Slot + BS = Bank Switcher (no memory) + nc. = not connected + + | HP38G | HP39/40G | HP48S/SX | HP48G/G+/GX | HP49G +----------------------------------------------------------------------------- +NCE1 | ROM 512KB | ROM 1024KB | ROM 256KB | ROM 512KB | Flash 2048KB +NCE2 | RAM 32KB | RAM 128KB | RAM 32KB | RAM 32/128KB | RAM 256KB +CE1 | nc. | BS | Slt1 32/128KB | BS | BS +CE2 | nc. | nc. | Slt2 32/128KB | Slt1 32/128KB | RAM 128KB +NCE3 | nc. | RAM 128KB | nc. | Slt2 32KB-4MB | RAM 128KB + + +8.) Stack window + +The content of the hardware stack is viewed here. In "1:" is the current return address. A double click on an item shows the address content in the Code window. + +Context menu pressing the right mouse button: + +- Push + +Push a new element before the current selection onto the stack. + +- Pop + +Pop the selected element from the stack. + +- Modify + +Modifies the stack content of the current selection. + + +9.) MMU window + +The configuration of the memory controllers is viewed here. The viewed addresses are the first address of each module area and may differ from the given address in the CONFIG command. + +This example + + LC(5) #C0000 128KB size + CONFIG + LC(5) #98765 start address of module + CONFIG + +will config a 128KB module at address #80000 and not at the given address. So the MMU viewer will show you the address #80000. + + +10.) Miscellaneous window + +The Miscellaneous window show you the internal state of the interrupt flag, the 1ms keyboard handler and the contents of the Bank Switcher latch. The Bank Switcher item is only enabled on calculators with a latch inside. You see the loaded value of the address lines A6-A0. You have to ignore the last bit (A0), because it isn't wired to the six bit latch. + +You can change the values by pressing the left mouse button over the old content. + + +02/24/06 (c) by Christoph Gießelink diff --git a/EMU48.EXE b/EMU48.EXE new file mode 100644 index 0000000000000000000000000000000000000000..300c3ce433e38e20b2697e42c9205130c277b49f GIT binary patch literal 266240 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 +$4LBWL8V~?uPi!fs$BBdw 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 literal 0 HcmV?d00001 diff --git a/EMU48.TXT b/EMU48.TXT new file mode 100644 index 0000000..3c52496 --- /dev/null +++ b/EMU48.TXT @@ -0,0 +1,460 @@ + + + + Emu48 - A freeware HP38/39/40/48/49 Emulator + for Windows 9x, ME, NT, 2000 and XP + + + + ******************** + * OPERATING SYSTEM * + ******************** + +This version of Emu48 should work with all Intel Win32 platforms. You may +recompile the sources to run Emu48 with Windows NT on a DEC Alpha. + + + **************** + * INSTALLATION * + **************** + +Emu48 is distributed in 1 archive: +- Emu48-1_35.zip All files and sources + +To install Emu48, just unzip Emu48-1_35.zip into an empty directory. When you +first run Emu48, it will detect the directory in which you installed it, and +will write its configuration to a file named Emu48.ini in your Windows +directory. If you move the Emu48 directory to another place you have to change +the directory path inside the Emu48.ini file manually or have to delete the +Emu48.ini file. + +You can also update your current version with the Unofficial Service Packs: +- E48BP3x.ZIP New EXE-File +- E48SP3x.ZIP Sources of the Service Pack + +Replace the original EXE file please. + + + ************************ + * YOU NEED A ROM IMAGE * + ************************ + +Emu48 needs an image of a calculator ROM to be able to run. Since fall 2000 the +emulator ROM's for the HP38, 39, 40, 48 and 49 are freely available on different +internet sites. Because there's no license for the distribution of the ROM +images, they aren't included in the Emu48 package. You can still use the classic +way extracting them from your own calculator. But in mostly all cases you have +to convert the ROM files into the Emu48 ROM format. + +- HP38: +To upload the ROM of your HP38G, you will need a special aplet called "ROM +UPLOAD", available at http://www.epita.fr/~avenar_j/hp. Once you've uploaded the +ROM, you have to convert it using the Convert utility. + +To do that, start a Command Prompt while running Windows, and type: + Convert ROM.38G + +Where is the path to your ROM image. This will create a file named +ROM.38G. This tool will also check its validity. + +- HP39/40: +To upload the ROM of your HP39G/HP40G, you will need a special aplet called "ROM +UPLOAD", available at http://privat.swol.de/ChristophGiesselink/emu48.htm. Once +you've uploaded the ROM, you have to convert it using the Rom2emu utility. + +To do that, start a Command Prompt while running Windows, and type: + Rom2emu ROM.39G + +There's also a HP39G/HP40G beta ROM for emulators at +http://www.epita.fr/~avenar_j/hp/39.htm for download. + +- HP48: +If you have already used another HP48 emulator, you can convert the ROM using +the Convert utility. + +To do that, start a Command Prompt while running Windows, and type: + Convert ROM.48G +or Convert ROM.48S + +Where is the path to your old ROM image. This will create a file +named ROM.48G or ROM.48S, depending on the version you own. This tool should be +able to read any style of ROM image, and will also check its validity. Note that +if you run it with only one parameter, no file will be written, but it will +still check the validity of the ROM. + +If you have never used an HP48 emulator, and don't have a ROM dump, you can +either use Jean-Yves Avenard's ROMUPL.BIN or the ROMDump Wizard V1.x, which will +almost automatically get the ROM from your HP48. After the download you may have +to convert your dump with the CONVERT utility into the Emu48 format. + +You can find the latest version of the ROM dump programs on: +ROMUPL.BIN http://www.epita.fr/~avenar_j/hp/calcen.html +ROMDump Wizard http://privat.swol.de/ChristophGiesselink/index.htm + +- HP49: +There's no ROM download program available so far. But you can find a HP49G ROM +for emulators in the YorkeM emulator package or in the HP49G SDK on +http://www.hpcalc.org in the HP49 section. + + + **************** + * HOW TO START * + **************** + +When Emu48 is installed and you have put the ROM image(s), which must be in the +Emu48 ROM format, into the Emu48 directory, you can start Emu48. You'll see a +"Choose Your KML Script" box. + +KML Scripts in fact define the visual aspect of Emu48, the behavior of the +buttons, of the keyboard, ... It's a GREAT way to customize your copy of Emu48. + +Check that the path in the "Emu48 Directory" text area is correct. Modify it if +the directory in which you installed Emu48 is not the directory displayed. Click +the refresh button ("V") after modifying it to update the list box or use the +("...") button to start the directory browser. + +Choose a KML script in the list box for your calculator ROM you put into Emu48's +directory. + +Several HP48 scripts are included in the Emu48 archive: + * Emu48's Default Faceplate for HP48G/GX + * Emu48's Default Faceplate for HP48S/SX + These two are simple scripts, good for 800x600 display resolution. + * Casey's Gx with Toolbar and Touch Screen + * Casey's Sx with Toolbar and Touch Screen + These script uses many advanced features, and is a good demonstration of + the power of Emu48's scripting language KML. Try it, it is really great! + * Floating buttons + This one looks really great. + * Small but realistic HP48 Gx + This one has been designed for small resolutions such as 640x480. + Note: some things in this script have to be fixed. + +If you want other great scripts, visit Rechlin's great HP archive + http://www.hpcalc.org/ + +And if you are interested in writing new scripts, get the KML 2.0 documentation +from Christoph's page at http://privat.swol.de/ChristophGiesselink/emu48.htm + +Once you have selected a script, press OK to start the emulator. In most cases, +when Emu48 crash after pressing the OK button, you forgot to convert the ROM +image into the emulator format. While it's running, you can use the View/Change +KML Script... command to change the visual aspect of Emu48. + + + *************** + * KML SCRIPTS * + *************** + +Don't use TRUELCD.KMI for emulating display contrast in your scripts. It's not +fully correct. The hardware contrast values are in the area from 0 to 31. But +the ROMs bounds them to useful values. The HP48 S(X) ROM use only display +contrast values between 3 and 19 and the HP48 G(X) ROM values between 9 and 24. + +Maybe you have to adjust the "Rom" filename in the "Global" section. This mostly +happen with the HP49G ROM name. Some KML files use the name ROM.E49, that's the +name of the emulator ROM file published by HP. But Emu48 state files for the +HP49G have the same file extension, so the use of ROM.49G is preferred now. + + + **************** + * COMMAND LINE * + **************** + +The command line syntax is "Emu48 [E48file [Port2file]]". The first parameter +sets the filename for the emulation data, the second parameter the Port2 file. +You're not able to set a Port 2 file without setting the emulation data file. +The arguments are optional. + + + ******************* + * LOAD/SAVE FILES * + ******************* + +There are two ways to transfer files from or to the emulator. The one way is to +use the serial port to transfer the data directly from your HP to the emulator. +The second way is to load data, saved on your PC, into the stack of the +emulator. You can do this by using the Edit/Load Object... command or with the +file Drag and Drop feature. But there's one important restriction, the data must +a HP binary file (begin with HPHP48- or HPHP49-, this depends on your emulated +calculator)! If not, the data is load as string. The Edit/Save Object... command +will save the data in stack level 1 on the PC (always binary mode). Be sure, +when you use the second way for data transfer, that no program is running on the +emulator. The second way doesn't work on a HP38, because he has no stack. So you +can load aplets only from the serial port. + + + ***************** + * DRAG AND DROP * + ***************** + +Dropping HP objects over the emulator window will load program files (like the +command "Load object...") on the stack. Be sure that the emulator isn't busy +before doing this. + + + ******************* + * SHARED RAM CARD * + ******************* + +You can add a SHARED (explained below) RAM card of up to 4MB to a HP48. By +default, no such card will be created when you start Emu48. The MkShared.exe +utility will allow you to create it. + +To create a Port 2 RAM Card, call the program, select the RAM Card size, enter +the card file name and press the 'Create' button. That's it. Please remember, +this program replace the destination file without any request! + +If you use RAM cards greater than 128 KB in a HP48SX, you can only see the first +128 KB of the card. Please remember, the firmware of all HP48GX versions has a +bug when using a 4MB RAM card. You always get the message "Warning: Invalid Card +Data" at startup and Port 33 is unaccessible. This is not a bug of the emulator! + +When you have created this file, run Emu48, and use the Close menu item to close +the calculator state. Now select File/Settings. In the "Port 2" text area, type +the name of the file you created (if you don't include a path, it will be +searched for in Emu48's directory). + +You can also tick the check box "Port 2 Is Shared". When the box is cleared, +only the first instance of Emu48 will allow you to use the RAM card in Port 2. +When this box is checked, the first instance of Emu48 will give you both read +and write access to this RAM card. If you start Emu48 in another instance, the +RAM card in Port 2 will be write-protected. Thus you can transfer files very +easily between two calculators. This RAM card is used by both S/SX and G/GX +types. + + + *********************** + * FLASH ROM EMULATION * + *********************** + +The HP49G save the operation system in a reprogramable memory, a so called flash +memory. The flash memory is divided into two parts, into the Operating System +and into a User Data area. The User Data area is viewed as Port 2 in the HP49G. +Emu48 saves the Port 2 data in the ROM file (normally ROM.49G). As default +setting the ROM file is writeable in the first instance of Emu48. When you open +another instance of a HP49G emulation the Port 2 area is READ ONLY, that mean +all changes in Port 2 are lost when you exit this instance. If you don't want to +save data in Port 2 and want to protect the operating systems from overwriting, +you're able protect the ROM file. To do this, close all Emu48 instances and set +the variable 'Writeable' defined in the Emu48.ini file, section [ROM] to zero. + + + *********************** + * COPY / PASTE STRING * + *********************** + +With the menu items "Copy String" and "Paste String" in the "Edit" menu you're +able to copy HP string objects from the stack to the PC clipboard and vice +versa. + + + ********** + * BACKUP * + ********** + +Emu48 includes a backup feature (in the Edit menu). It save the complete state +of the calculator (excepting the ROM and Port 2 content) in the computer's +memory. You might want to use it before doing something risky, and if you don't +want to save to the disk. It provides some kind of Undo feature. It is also used +by Emu48 when you want to save or load a new document, to restore its old state +if you cancel the operation or of something goes wrong. + + + ************ + * KEYBOARD * + ************ + +To enter a character to the emulator use the PC keyboard (key translation +depends on the used KML script) or the mouse. If you press the left mouse +button, the emulator key is pressed as long as you press the mouse button or +leaving the area of the emulator button. Sometimes you need to press more then +one key (contrast setting, warmstart, ...). To do this, press the right mouse +button. All "locked" buttons are released after enter a key with the left mouse +button. + + + ********* + * CLOCK * + ********* + +The emulator time is synchronized with the PC time at startup of the emulator. +This may cause problems with other non original operating systems running on the +HP. On HP48 S(X) calculators the address area #00052-#00070, on all other +emulated calculators the address area #00058-#00076 in System RAM are rewritten +with the actual time information. + + + ************* + * EMU48.INI * + ************* + +The section [Timers] in the Emu48.ini file isn't used any more. The variable +values are replaced by useful constants. You may delete this section if you +want. Starting an old version of Emu48 (V1.07 and earlier) will add this section +again. If you move the Emu48 directory to another place, you have to adjust the +variable 'Emu48Directory' in the [Files] section. + + + ************************ + * REAL SPEED EMULATION * + ************************ + +As you recognized the speed of the emulated HP is much faster than an original +one. The reason is, the assembler commands are emulated faster than the original +CPU can execute them. On one side this is a big advantage (faster execution of +programs) on the other side this cause many trouble. In Emu48 only the timers +work with the original speed. In result all commands like User-RPL WAIT wait +more or less the correct time. But many programs like shells or editors use an +own key handler to realize an autorepeat implementation. Normally these programs +use the execution time of each assembler command for waiting. On Emu48 this time +is much shorter, so the time between each key read is shorter as well and you +get a very fast key repetition. The editor ED from the JAZZ package hasn't this +problem, because the key input is synchronized with one of the timers. To solve +this problem Emu48 generally slow down emulation if a key is pressed. To solve +some other speed depending problems you are able to slow down the whole +emulation speed. There are two variables 'SXCycles=82' and 'GXCycles=123' +defined in the Emu48.ini file, section [Emulator] which control the "real" speed +and key repetition slow down for each calculator type. Each numeric value is +representing the allowed CPU cycles in a 16384Hz time frame. Because the used +cycle statements (from SASM.DOC) in Emu48 doesn't correspond to the real values +of the CPU, the saved values are estimated by comparing the execution time of a +program to the real calculator. Increasing the value fitting to your ROM will +make the "real speed" HP faster and vice versa. No warranty to the functionality +of Emu48 when you go below the default values. + + + ************************* + * SERIAL PORT EMULATION * + ************************* + +The serial ports are emulated as well now. You may choose the same serial port +for wire and IR. Remember that the IR port only work with 2400 Baud. If you want +to change the serial port settings, but they are disabled, close the serial port +with the command CLOSEIO or power cycle the HP first. + +Now it's possible to make transfers between the real calculator and Emu48. If +you have problems with the connection please try the following. There's a simple +way to check if your serial port is used by another program. First disable the +serial settings in both combo boxes and very important close the settings +dialog. Reopen the settings dialog and choose the COM port in the wire combo box +to the port the HP is connected with. When you open this combo box you only see +valid (unused) serial ports. Don't use the IR combo box, it only works with 2400 +Baud. The next important thing are the serial settings of the real calculator +and Emu48, they must be equal. If this doesn't work then mostly there's a +hardware or a resource problem of the serial port. Check this with connecting +the HP with a transfer program you like on the same serial port. + + + **************** + * DISASSEMBLER * + **************** + +With the internal disassembler you're able to disassemble the Saturn chip +address area. With the default Map setting the disassembler always see the +mapped memory address. If for example you configured the RAM at #00000 you will +see the RAM and not the ROM at this address. With the other module settings you +specify a special module for disassembly. Each module use a linear address mode, +beginning at address #00000 and will not overlapped by other modules. So, for +example, you can access the second port of a HP48 RAM card greater than 128KB at +address #40000 (128 * 1024 * 2). The "Copy Data" button copies the selected +disassembler lines to the PC clipboard. + + + ************** + * DDE SERVER * + ************** + +I implemented a DDE server in Emu48 to transmit data from and to the HP stack +with DDE. You have the same restrictions like with the commands "Load object..." +and "Save Object...", that a running program may corrupt memory. Take care to +transmit data only after the acknowledge of the last DDE transaction. + +Technical data: + +Servername: Emu48 +Topicname: Stack +Item: - (ignored, must be a nonzero string) +Clipboardformat: "CF_HPOBJ" (user defined) + +The DDE commands CONNECT, POKE and REQUEST are supported. + +The structure of the clipboard format "CF_HPOBJ": + ++--------+------------------------------------+ +| 4 Byte | HP object | ++--------+------------------------------------+ + \ \ + \ +--- normal HP object + +----------- length of object (LSB first) + + + ******************** + * TROUBLE SHOOTING * + ******************** + +Visit the Emu48 FAQ site at http://privat.swol.de/ChristophGiesselink/index.htm +to get more information please. + + + *********** + * SUPPORT * + *********** + +We cannot provide any individual support for Emu48. All informations about Emu48 +will be on the Emu48 Official Homepage on the Web: + + http://www.epita.fr/~sebc/Emu48/index.html + +or on the Emu48 FAQ at + + http://privat.swol.de/ChristophGiesselink/index.htm + + + *************** + * LEGAL STUFF * + *************** + +Emu48 - An HP38/39/40/48/49 Emulator +Copyright (C) 2005 Sebastien Carlier & Christoph Gießelink + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or (at your option) any later +version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place, Suite 330, Boston, MA 02111-1307 USA + + + *************** + * The Authors * + *************** + +Paper Mail: + Sebastien Carlier + 10 Allee des bergeronnettes + 35340 LIFFRE + FRANCE + +E-Mail: + sebc@epita.fr + +Homepage: + http://www.epita.fr/~sebc/Emu48/index.html + + +Paper Mail: + Christoph Giesselink + GERMANY + +E-Mail: + c dot giesselink at gmx dot de + +Homepage: + http://privat.swol.de/ChristophGiesselink/index.htm diff --git a/EMU48PLUS.TXT b/EMU48PLUS.TXT new file mode 100644 index 0000000..98c0726 --- /dev/null +++ b/EMU48PLUS.TXT @@ -0,0 +1,29 @@ +Emu48 1.42+ + +Emu48+ is a modified version of Emu48 to add support for the ARM-based +calculators. It does not emulate the ARM CPU, but it enhances the +Saturn emulation to more closely match the emulation provided by the +Saturn emulator on the ARM-based calculators. + +Emu48+ adds support for many of the Saturn+ instructions, including +some of the BUSCC instructions, and it also adds support for the 80-line +display used on the 49G+ and 50G. + +At present, the additional calculators supported in Emu48+ are the +49G+, 48GII (hardware revision 1), 50G, and 39G+. + +To create KML scripts for the additional calculator models, use the +following model codes: + +39G+: Model "P" + +49G+: Model "Q" + +48GII: Model "2" + +50G: Model "Q" + Class 50 + +Most of the code that was changed in Emu48+ over Emu48 was provided by +Cyrille de Brebisson of Hewlett-Packard. + diff --git a/PROBLEMS.TXT b/PROBLEMS.TXT new file mode 100644 index 0000000..b44af79 --- /dev/null +++ b/PROBLEMS.TXT @@ -0,0 +1,56 @@ +Known bugs and restrictions of Emu48 V1.42 +------------------------------------------ + +- the following I/O bits aren't emulated (incomplete) + DTEST (0x102) [VDIG LID TRIM] + DSPCTL (0x103) [LRT LRTD LRTC BIN] + LPD (0x108) [LB2 LB1 LB0 VLBI] + LPE (0x109) [ELBI EVLBI GRST RST] + CMODE (0x10A) Mode register + IOC (0x110) [ERBZ] + RCS (0x111) [RX RER RBZ] + SRQ1 (0x118) [ISQR VSRQ] + SRQ2 (0x119) [LSRQ] + IRC (0x11A) [IRI EIRU EIRI IRE] + LCR (0x11C) [LED ELBE LBZ LBF] + LBR (0x11D) [LBO] +- the baudrates 1920, 3840, 7680 and 15360 aren't emulated on most + operating systems + Windows 95a 1920, 3840, 7680 work, 15360 fail + Windows 98, NT4.0, 2000, XP all baudrates fail +- problems when receiving a break signal on the serial port + Windows 98, NT4.0 SP4 no retrigger on port open + Windows 98 timing problems setting the RER bit + Windows 2000 SP2 no known problems +- System-RPL commands VERYVERYSLOW, VERYSLOW and SLOW depends on PC + speed (are realized as simple down counter in ROM) +- display updating differs from the real machine +- screen VBL counter values may skip after large display operations + like turning on or updating the whole display +- executing an opcode over a MMU boundary will fail +- read on an unconfigured address (open data bus) will not show the + same value like a real calculator +- the Yorke hardware signals BEN and DA19 aren't fully supported, + because the emulator don't use a multiplexed AR18 / NCE3 data line + -> all programs that run on a real calculator will run as well, + programs with incorrect DA19 / BEN handling may run on the + emulator but will crash on a real calculator +- incomplete reset logic of the bank switcher FF, on real + calculators a reset happen after about 4s in deep sleep, in the + emulator this happens immediately +- no MP interrupt on card control circuit or timer restart +- no beeper support with OUT command -> all programs that aren't + use the "=makebeep" subroutine, like alarm wake up, have no sound +- beeper emulation, ATTN key doesn't work +- no infrared printer support +- Shell OS: clock isn't synchronized with real time +- HP49G: the flash memory is emulated now with some restrictions + - no flash programming times, the flash state machine returns + immediately the ready signal + - only one write buffer, second not needed because of prior reason + - not fully tested, especially the status byte may return + incorrect values (error bits) + - quitting the emulator while programming the flash isn't allowed, + because the content of flash state machine isn't saved so far + +08/22/06 (c) by Christoph Gießelink, c dot giesselink at gmx dot de diff --git a/SOURCE/APPLE.C b/SOURCE/APPLE.C new file mode 100644 index 0000000..f1b08d2 --- /dev/null +++ b/SOURCE/APPLE.C @@ -0,0 +1,239 @@ +/* + * apple.c + * + * This file is part of Emu48 + * + * Copyright (C) 2005 CdB for HP + * Copyright (C) 2006 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "Opcodes.h" +#include "apple.h" +#include "io.h" // I/O register definitions +#include "i28f160.h" + +#define w Chipset + +#define _KB(s) ((s) * 1024 * 2) + +#pragma intrinsic(memset,memcpy) + +#include "Ops.h" + +static QWORD DecodeReg64(LPBYTE R, BYTE byNF) +{ + QWORD qwVal = Npack64(R,16); // generate 64bit number from register + + switch (byNF) // field selector + { + case 0: return (qwVal >> (w.P*4)) & 0xf; // P + case 1: return qwVal & ~((QWORD)~0 << ((w.P+1)*4));// WP + case 2: return (qwVal >> 8) & 0xf; // XS + case 3: return qwVal & 0xfff; // X + case 4: return (qwVal >> 60) & 0xf; // S + case 5: return (qwVal >> 12) & 0x0000ffffffffffff; // M + case 6: return qwVal & 0xff; // B + case 7: return qwVal; // W + case 15: return qwVal & 0xfffff; // A +// default: return qwVal & w.fld[I[6]-8]; // F1-F7 + default: return qwVal; + } +} + +static QWORD o80BReg164(LPBYTE I) +{ + switch (I[5] & 3) + { + case 0: return DecodeReg64(w.A, I[6]); + case 1: return DecodeReg64(w.B, I[6]); + case 2: return DecodeReg64(w.C, I[6]); + case 3: return DecodeReg64(w.D, I[6]); + default: return 0; + } +} + +static QWORD o80BReg264(LPBYTE I) +{ + switch ((I[5]>>2) & 3) + { + case 0: return DecodeReg64(w.A, I[6]); + case 1: return DecodeReg64(w.B, I[6]); + case 2: return DecodeReg64(w.C, I[6]); + case 3: return DecodeReg64(w.D, I[6]); + default: return 0; + } +} + +static void EncodeReg64(QWORD v, LPBYTE R, BYTE byNF) +{ + if (byNF > 7 && byNF < 15) // user mask area F1-F7 + byNF = 7; // use W area + + Nunpack64(R+F_s[byNF], v, F_l[byNF]); + return; +} + +static void o80BRegWrite(QWORD v, LPBYTE I) +{ + switch (I[5] & 3) + { + case 0: EncodeReg64(v, w.A, I[6]); break; + case 1: EncodeReg64(v, w.B, I[6]); break; + case 2: EncodeReg64(v, w.C, I[6]); break; + case 3: EncodeReg64(v, w.D, I[6]); break; + } +} + +// setup basic memory configuration +static VOID o80B04(VOID) +{ + DWORD a; + + a = Npack(w.C,5); // save C[A] + Reset(); // unconfig all devices + Nunpack(w.C,0x100,5); // IO: 0x00100 + Config(); // addr + Nunpack(w.C,0x80000,5); // RAM: 0x80000 size 256KB + Config(); // size + Config(); // addr + Nunpack(w.C,a,5); // restore C[A] + w.P = 0; + PCHANGED; + return; +} + +// erase flash bank +static VOID o80B14(VOID) +{ + DWORD dwStart,dwStop; + + BYTE byBank = w.C[15]; // C[S] = bank to erase + +_ASSERT(FALSE); // not tested so far + // ROM is logically organized in 16 banks with 128KB + dwStart = byBank * _KB(128); // start address + dwStop = dwStart + _KB(128); // last address + if (byBank == 0) dwStart += _KB(64); // skip boot loader + + // clear bank + FillMemory(&pbyRom[dwStart],dwStop-dwStart,0x0F); + + w.carry = FALSE; // no error + return; +} + +// write bytes to flash +static VOID o80B24(VOID) +{ + DWORD dwNib,dwAddr,dwSize; + + dwNib = Npack(w.C,5) * 2; // no. of nibbles to copy + dwAddr = FlashROMAddr(w.d1); // linear addr in flash chip + + dwSize = dwRomSize - dwAddr; // remaining memory size in flash + if (dwNib > dwSize) dwNib = dwSize; // prevent buffer overflow + + Npeek(pbyRom+dwAddr,w.d0,dwNib); // copy data + + w.d0 += dwNib; // update source register + w.d1 += dwNib; // update destination register + w.carry = FALSE; // no error + return; +} + +// CdB for HP: add apples BUSCC commands +VOID o80BExt(LPBYTE I) // Saturnator extentions +{ + DWORD a; + + w.pc+=2; + switch (I[3]+(I[4]<<4)) + { + case 0x00: break; // RPL2 not implemented + case 0x05: External(&w); PCHANGED; break; // BEEP2 implemented using Emu48's beep + case 0x06: break; // MOVEDN not implemented + case 0x07: break; // MOVEUP not implemented + case 0x08: break; // CRTEMP not implemented + case 0x0A: break; // KEYDN not implemented + case 0x0B: break; // no doslow implemented + case 0x10: // simulate off function + { + BOOL bShutdn = TRUE; // shut down + + // only shut down when no timer wake up + if (w.IORam[TIMER1_CTRL]&WKE) // WKE bit of timer1 is set + { + if (ReadT1()&0x08) // and MSB of timer1 is set + { + w.IORam[TIMER1_CTRL] &= ~WKE; // clear WKE + bShutdn = FALSE; // don't shut down + } + } + if (w.IORam[TIMER2_CTRL]&WKE) // WKE bit of timer2 is set + { + if (ReadT2()&0x80000000) // and MSB of timer2 is set + { + w.IORam[TIMER2_CTRL] &= ~WKE; // clear WKE + bShutdn = FALSE; // don't shut down + } + } + if (w.in==0 && bShutdn) // shut down only when enabled + { + w.Shutdn = TRUE; // set mode before exit emulation loop + bInterrupt = TRUE; + } + } + break; + case 0x11: w.pc+=2; break; // do not do gettime, just skip the RTN after it to fall in the normal gettime function (only valid in untouched ROM) + case 0x12: break; // do not do settime, fall in the normal settime function (only valid in untouched ROM) + case 0x13: break; // RESETOS not implemented + case 0x14: break; // AUTOTEST not implemented + case 0x15: break; // NATIVE? not implemented + case 0x17: break; // SERIAL not implemented + case 0x28: w.HST |= I[5]; w.pc+=1; break; // HST=1.x + case 0x29: w.A[4]= w.A[3]= w.A[2]= w.A[0]= 0; if (cCurrentRomType=='Q') w.A[1]=5; else w.A[1]=4; break; // screen height = 0x50 = 80 + case 0x2A: w.A[4]= w.A[3]= w.A[2]= 0; w.A[1]=8; w.A[0]=3; break; // screen width = 0x83 = 131 + case 0x2B: w.carry = (cCurrentRomType == '2'); break; // it is medium apple + case 0x2C: w.carry = (cCurrentRomType == 'Q'); break; // it is big apple + case 0x2E: w.carry = (nCurrentClass == 50); break; // it is big apple V2 + case 0x30: w.d0address= Npack(w.C,5)>>12; Map(0,0xff); break; //config_disp0 Ca:address 4K data + case 0x31: w.d0address=0; Map(0,0xff); RefreshDisp0(); break; //unconfig_disp0 does the refresh + case 0x32: RefreshDisp0(); break; //refresh_disp0 force refresh + case 0x33: a= Npack(w.C,2); if (a>(DWORD)SCREENHEIGHT) a= SCREENHEIGHT; /* w.lcounter = (SCREENHEIGHT-a) */; w.d0size= a; RefreshDisp0(); break; //set_lines_disp0 nb in Cb + case 0x34: w.d0offset= Npack(w.C,5); w.d0offset &= 0x7FF; break; //set_offset_disp0 offset to disp in disp0 + case 0x35: Nunpack(w.C,w.d0offset,5); break; // native_get_line_disp0 + case 0x38: w.HST |= I[5]; w.pc+=3; break; // ?HST=1.x not implemented + case 0x40: o80B04(); break; // setup basic memory configuration +// case 0x41: o80B14(); break; // erase flash bank + case 0x42: o80B24(); break; // write bytes into flash +// case 0x43: ??? // format flash bank + case 0x50: break; // REMON not implemented + case 0x51: break; // REMOFF not implemented + case 0x56: break; // OUTBYT not implemented + case 0x57: w.D[0]= w.D[1]= 0; break; + case 0x60: break; // ACCESSSD not implemented + case 0x61: break; // PORTTAG? not implemented + case 0x64: w.carry= FALSE; break; // no SD card inserted + case 0x66: w.carry= FALSE; break; // simulate format fail card inserted + case 0x7F: w.pc+=1; break; // SETFLDn not implemented + case 0x80: { QWORD b = o80BReg264(I); o80BRegWrite(b, I); w.pc+=2; break; } // R=R=R + case 0x81: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a+b, I); w.pc+=2; break; } // R=R+R + case 0x82: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a-b, I); w.pc+=2; break; } // R=R-R + case 0x83: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a*b, I); w.pc+=2; break; } // R=R*R + case 0x84: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a/b, I); w.pc+=2; break; } // R=R/R + case 0x85: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a%b, I); w.pc+=2; break; } // R=R%R + case 0x86: { QWORD b = o80BReg264(I); o80BRegWrite(~b, I); w.pc+=2; break; } // R=-R-1 + case 0x87: { QWORD b = o80BReg264(I); o80BRegWrite((QWORD)(-(__int64)b), I); w.pc+=2; break; } // R=-R + case 0x88: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a<>b, I); w.pc+=2; break; } // R=R>R + case 0x8A: { QWORD a = o80BReg164(I); QWORD b = o80BReg264(I); o80BRegWrite(a^b, I); w.pc+=2; break; } // R=R^R + case 0x90: w.pc+=2; break; // do not do RCKBp, just skip the RTN after it to fall in the normal function (only valid in untouched ROM) + case 0xEE: break; // ARMFLUSH not implemented + case 0xEF: break; // ARMSYS not implemented + case 0xFF: break; // ARMSAT not implemented + default: w.pc-= 2; + } + return; +} diff --git a/SOURCE/APPLE.H b/SOURCE/APPLE.H new file mode 100644 index 0000000..b4d1cc3 --- /dev/null +++ b/SOURCE/APPLE.H @@ -0,0 +1,10 @@ +/* + * apple.h + * + * This file is part of Emu48 + * + * Copyright (C) 2006 Christoph Gießelink + * + */ + +extern VOID o80BExt(LPBYTE I); // Saturnator extentions diff --git a/SOURCE/CHANGES.TXT b/SOURCE/CHANGES.TXT new file mode 100644 index 0000000..9fa4599 --- /dev/null +++ b/SOURCE/CHANGES.TXT @@ -0,0 +1,3265 @@ +-------------------------------------------------------------------- +Service Pack 42 for Emu48 Version 1.0 + +EMU48.C +- major changes in function SettingsProc(), added "Port 2 is + Writeable" handling and make port2 changeable without closing the + current emulation session + +EMU48.H +- extern declaration of global function + +EMU48.RC +- added item "Port 2 is Writeable" in settings dialog +- changed version and copyright + +FILES.C +- changed function CrcPort2(), made it global accessible + +KML.C +- bugfix in function TransparentCircle(), there was no radius value + check for preventing division by 0 and color low adjust value + wasn't reached because of wrong color offset divisor -> adjusted + LOWADJ definition to get similar output to buggy earlier version + +OPCODES.C +- bugfix in function o807(), Chipset.in register must be refreshed + before checking for a pressed key + +TIMER.C +- changed function CalcT2(), in the case of a pending timer2 + interrupt return always the timer2 value 0xFFFFFFFF + + +Service Pack 41 for Emu48 Version 1.0 + +DDESERV.C +- removed warnings when compiling under VS2005 +- changed function DdeCallback(), case XTYP_POKE and XTYP_REQUEST + now decode the item name for the stack level + +DEBUGGER.C +- removed warnings when compiling under VS2005 + +DISASM.C +- bugfix in function disasm_8(), the ?HS=0 n opcode showed always as + ?=0 opcode + +DISPLAY.C +- changed function StopDisplay(), adjusted to new prototype of + ReadIO() + +EMU48.C +- removed warnings when compiling under VS2005 +- moved CF_HPOBJ definition to EMU48.H +- changed function OnFileClose(), removed call of KillKML() because + will be done in ResetDocument() + +EMU48.H +- added CF_HPOBJ definition from EMU48.C +- changed function prototypes + +EMU48.RC +- changed version +- replaced CREATEPROCESS_MANIFEST_RESOURCE_ID definition in manifest + declaration by 1 + +EMU48DLL.C +- added DDE server + +EXTERNAL.C +- changed function BeepWave(), removed VS2005 warning +- changed function BeepWin9x(), disabled implementation for x64 + architecture + +FILES.C +- changed function PatchNibble(), added saving state that ROM + address is patched +- bugfix in function UpdatePatches(), the function maybe called for + patching the ROM when it's already patched, in this case the patch + restore list was overwritten with the patched data +- changed function PatchRom(), removed VS2005 warning +- changed function MapPort2(), improved checking for valid size +- changed function WriteStack(), added stack level argument +- changed function LoadObject(), adjusted call of WriteStack() to + new function prototype + +KEYBOARD.C +- bugfix in function ScanKeyboard(), if keyboard reading wasn't + active a released ON key wasn't cleared in the "in" register + +KEYMACRO.C +- removed warnings when compiling under VS2005 +- changed function OnToolMacroNew(), minor optimization writing + macro file header + +KML.C +- changed function DisplayChooseKml(), changed return type of dialog + box +- changed function ParseLine(), change typecast of szLexString from + DWORD to DWORD_PTR + +KML.H +- changed variable type of nParam[] element of KmlLine structure + because element is also used as pointer + +MOPS.C +- changed function Npeek() and Nread(), adjusted to new prototype of + ReadIO() +- changed prototype of function ReadIO(), added update argument +- bugfix in function ReadIO(), the LPE (0x109), RBR LSB (0x114) and + RBR MSB (0x115) register access had to differ between peek and + read mode + +PCH.H +- added _CRT_SECURE_NO_DEPRECATE definition +- added DWORD_PTR type definition + +RESOURCE.H +- removed CREATEPROCESS_MANIFEST_RESOURCE_ID and RT_MANIFEST + definition + +RPL.C +- added definition of EDITLINE +- added function RPL_Depth() returning stack depth +- changed function RPL_Pick(), added check of stack depth +- changed function RPL_Push(), new implementation with stack level + argument + +STACK.C +- adjusted calls of RPL_Push() to new function prototype +- changed function RPL_SetBcd(), removed compiler warning +- bugfix in function OnStackPaste(), case "any other format" must be + treated as string and not as binary object + +TIMER.C +- removed warnings when compiling under VS2005 + + +Service Pack 40 for Emu48 Version 1.0 + +APPLE.C +APPLE.H +- new module containing Saturnator BUSCC extentions and helper + functions prior located in opcodes.c +- changed BUSCC 2E (opcode 80BE2) implementation, return Carry = 1 + for big apple V2 when "Class 50" KML command is set +- added BUSCC 40 (opcode 80B04) implementation +- added BUSCC 42 (opcode 80B24) implementation + +EMU48.C +- bugfix in function OnViewScript(), removed error message because + it's wrong when script is ok, but log report was leaved with + Cancel button, also if clause of error message box was never + reached because error message box had no Cancel button +- changed function OnViewScript(), added call of + SetWindowPathTitle() to adjust length of window title +- changed function WinMain(), simplified document loading at startup + and replaced full filename title with length cutted filename + +EMU48.DSP +- added apple.c and apple.h sources + +EMU48.H +- added definition with valid calculator models +- added macro with model validation code +- extern declaration of global functions + +EMU48.RC +- changed version + +EMU48DLL.C +- changed function DLLCreateWnd(), simplified document loading at + startup and replaced full filename title with length cutted + filename + +EMU48DLL.DSP +- added apple.c and apple.h sources + +FETCH.C +- changed function EvalOpcode(), included assertion for detecting + access to packed memory data + +FILES.C +- moved function WriteStack() inside file +- added new function GetCutPathName(), creating a length cutted + filename +- added new function SetWindowPathTitle(), replacement for calling + SetWindowTitle() for getting length cutted filenames as title +- bugfix in function OpenDocument(), after reading CPU state, port + pointers must immediately be deleted because on error condition + function ResetDocument() tries to free this memory; added check + for valid calculator model +- changed function OpenDocument(), SaveDocumentAs() and + RestoreBackup(), replaced call of function SetWindowTitle() with + SetWindowPathTitle() +- changed function GetOpenFilename(), GetSaveAsFilename(), + GetLoadObjectFilename() and GetSaveObjectFilename(), moved + storage place of local file buffer from heap to stack + +I28F160.C +- bugfix in function WrStateE8N(), removed "Write to Buffer command + failed when requesting for the buffer length" code part introduced + in SP27; this was in reality a workaround for the actually fixed + Nwrite() flash access bug and illegal data length information + wasn't handled correctly + +KML.C +- changed function CreateKmlList(), select only KML files with a + valid Model statement +- bugfix in function Lex(), EOF without LF at end of non empty line + wasn't detected as EOL +- bugfix in function InitButton(), nested scancode block commands + hadn't handled correctly +- changed function InitKML(), changed checking for valid Model + argument and changed the corresponding error message + +MOPS.C +- bugfix in function UckBit(), when UART is off then the UCK bit + was always cleared, but it's always set +- bugfix in function Npeek(), Nread() and Nwrite(), flash access + over a 2KB MMU boundary caused problems in some cases +- bugfix in function WriteIO(), race condition when clearing the SON + bit in the IOC (0x110) register, the UART was still working so the + cleared IOC (0x110), RCS (0x111), TCS (0x112), RBR (0x114,0x115) + and TBR (0x116,0x117) register can be overwritten by the serial + thread + +OPCODES.C +- moved function DecodeReg64(), o80BReg164(), o80BReg264(), + EncodeReg64() and o80BRegWrite() to apple.c +- changed function o80B(), moved Saturnator extentions to apple.c + +SERIAL.C +- bugfix in function CommOpen(), method of port closing was illegal + because COM port dependent threads hadn't been stopped + + +Service Pack 39 for Emu48 Version 1.0 + +DDESERV.C +- bugfix in function DdeCallback(), used wrong return in XTYP_POKE + case for calculator models without stack + +DEBUGGER.C +- changed function UpdateMemoryWnd(), added follow dispatcher +- new fuction OnMemFollow() to handle follow menu entries +- changed function OnDblClick() and OnKeyUpDown(), replaced all + function calls of UpdateMemoryWnd() with ViewMemWnd() because of + the modified behavior of UpdateMemoryWnd() +- changed function Debugger(), added "Follow" menu entry + initialization in WM_INITDIALOG and menu commands handler in + WM_COMMAND section + +EMU48.C +- changed function SaveChanges(), default button for confirming the + "Do you want to save changes ?" message depends now on the + "SaveDefaultConfirm" INI-File setting (1=yes / 0=no button) + +EMU48.DSP +- added new build modes using Debug4x registry settings of Emu48.dll + +EMU48.H +- extern declaration of global variable + +EMU48.RC +- changed IDD_KMLLOG, replaced "Always" text with "Show Script + compilation result next time" +- added "Follow" menu entries in debugger "Memory" context menu +- changed version and copyright + +FILES.C +- bugfix in function NewDocument(), the backup now behave like with + function OpenDocument() -> the backup now contain the emulator + state before the new session and not the new session itself +- bugfix in function RestoreBackup(), window title wasn't updated +- bugfix in function CreateBIPalette(), for 16 and 32 bitcount + bitmaps no logical palette was created +- bugfix in function LoadBitmapFile(), function blocked loading + bitmap when ROM file was missing + +IO.H +- added DISP1CTL, LINENIBS, DISP2CTL and BAUD bit definitions + +KML.C +- changed table szLexDelim[], removed first element +- added new table eIsGlobalBlock[] with valid block tokens +- added new function IsGlobalBlock(), checking if token inside + eIsGlobalBlock[] table +- changed function Lex(), added assertion for mode checking +- changed function ParseLines(), added syntax checking for command + tokens and removed "Open block." message +- changed function ParseBlocks(), added syntax checking for block + tokens +- changed function InitLcd(), added zoom factor 3 to range check of + TOK_ZOOM +- bugfix in function LoadKMLGlobal(), keyword "Global" had only been + detected as first command in KML script + +KML.H +- adjusted definitions of LEX_BLOCK, LEX_COMMAND and LEX_PARAM to + modified table szLexDelim[] +- added TOK_TOPBAR and TOK_VGA definition + +RESOURCE.H +- added several definitions + +RPL.C +- added new function RPL_GarbageCol() for doing a garbage collection +- changed function RPL_CreateTemp(), added garbage collection on low + memory condition + +SETTINGS.C +- changed function ReadSettings() and WriteSettings(), added item + "SaveDefaultConfirm" in section [File] in the INI-File + + +Service Pack 38 for Emu48 Version 1.0 + +DISPLAY.C +- bugfix in function UpdateContrast(), used background Color 0 in + calculator off state even if Color 32 was defined +- bugfix in function CreateLcdBitmap(), DIB section shouldn't + created from the memory DC + +EMU48.C +- added function IsPort2Writeable() to check if read only file + attribute is not set on port2 file +- changed function SettingsProc(), added sound handling +- moved function OnStackCopy() and OnStackPaste() to STACK.C +- changed function WinMain(), added accelerator key access for menus + +EMU48.H +- extern declaration of global variables and functions + +EMU48.RC +- added "Sound" group in Settings dialog +- changed menu text "Copy String" to "Copy Stack" and menu text + "Paste String" to "Paste Stack" +- added accelerator resource +- changed version + +EMU48.DSP +- added stack.c sources + +EMU48DLL.C +- changed function DLLCreateWnd(), added accelerator table resource + loading +- added function EmuAcceleratorTable() for exporting the necessary + data to activate the emulator accelerator table in the main + message loop of the caller + +EMU48DLL.DSP +- added stack.c sources + +EMU48DLL.H +- extern declaration of global function + +ENGINE.C +- bugfix in function WorkerThread(), when changing into SM_RUN state + display constrast must also be restored + +EXTERNAL.C +- added function BeepWave(), beeper function using sound card +- added function BeepWin9x(), PC speaker function for working on + Win9x +- changed function External(), removed range checking of duration; + call function BeepWin9x() on Win9x instead of Beep() or in the + case of wave sound the function BeepWave() + +FILES.C +- bugfix in function SaveObject(), calculator models HP48GII and + HP49G+ hasn't used the HP49G object header + +KML.C +- bugfix in function SkipWhite(), skipping remark text hadn't stop + at string end +- bugfix in function ParseBlocks(), stopped script parsing without + error at unknown block token +- bugfix in function SkipLines(), TOK_END was handeled wrong -> + this caused a RunKey end if condition of TOK_IFPRESSED or + TOK_IFFLAG was false and there was no TOK_ELSE inside this level + +OPCODES.C +- bugfix in function o80BdecodeReg64(), WP and XS case worked wrong +- replaced function o80BdecodeReg64() with DecodeReg64() +- changed function o80BReg164() and o80BReg264(), adjusted to + DecodeReg64() call +- deleted function o80BWriteHex() +- replaced complete buggy function o80BEncodeReg64() with + EncodeReg64() +- changed function o80BRegWrite(), adjusted to EncodeReg64() call +- bugfix in function o80B(), case 85 of BUSCC extensions used '/' + instead of '%' operator +- added simulation of off switch for 49g+ family + +OPS.H +- added function Npack64() and Nunpack64() for 64bit register + packing/unpacking + +PCH.H +- added include conio.h + +RESOURCE.H +- added definition + +RPL.C +- added function RPL_GetSystemFlag() returning the state of the + given system flag + +SETTINGS.C +- changed function ReadSettings() and WriteSettings(), added item + "WaveBeep" in section [Emulator] in the INI-File + +STACK.C +- new modul with the message handler functions OnStackCopy() and + OnStackPaste() prior located in EMU48.C +- changed function OnStackCopy() and OnStackPaste(), they can now + also copy real numbers and HP49G Precision Integer (limited to + 127/128 digits incl. sign) from and to the clipboard + +TIMER.C +- changed function CheckT1() and CheckT2(), minor optimization in + checking if INT or WAKE bit is set + + +Service Pack 37 for Emu48 Version 1.0 + +DISPLAY.C +- replaced the DIBPIXELGRAY and DIBPIXELBW use with DIBPIXEL macro + to avoid the "if (bGrayscale)" construct in the inner loop of + display drawing +- renamed the bmiLcdGray structure to bmiLcd +- removed the bmiLcdBW structure, use bmiLcd instead +- replaced UpdateContrast() implementation by a optimized one +- bugfix in function CreateMainBitmap(), in error case hMainDC + variable wasn't cleared +- changed function UpdateMainDisplay() and UpdateMenuDisplay(), + added display buffer ckecking in debug mode +- bugfix in function GetLineCounter() and StartDisplay(), VBL down + counter counted 62,...,0,63 instead of 63,...,0 + +EMU48.C +- changed function SettingsProc(), make "grayscale" mode online + switchable + +EMU48.H +- bugfix in definition MENUHEIGHT, calculated number of lines was + always 1 to much (with the exception of Chipset.lcounter==0); + this caused a buffer overflow in function UpdateMenuDisplay() + which prevented program execution on Win9x + +EMU48.RC +- changed menu text order from "Copy Stack/Copy Screen/Paste Stack" + to "Copy Screen/Copy Stack/Paste Stack" +- changed "Enable Virtual LCD Delay" in Settings dialog to the Emu48 + style without a group box + +EMU48DLL.C +- changed function EmuCalculatorType(), removed workaround for older + Debug4x versions + +FILES.C +- changed function PatchRom(), changed ASCII string handling from + BYTE to CHAR data type + +KML.C +- bugfix in function InitKML(), changed log message for no "Model" + definition found and a missing "Bitmap" definition wasn't detected + +OPS.H +- changed function Nsrb(), speed optimization + +TIMER.C +- bugfix in function SetT1(), fixed unsynchronized access to timer1 + value, stop timer1 only if running and restart timer1 only when + timer2 is running + + +Service Pack 36 for Emu48 Version 1.0 + +- changed display management to use stretchBlit instead of doing it + by hand +- debugged usage of Apple header display to allow one to change the + header size to any size +- added runtime-configurable grayscale simulation support + +CHECKBOX.BMP +- fixed one pixel with wrong color + +DDESERV.C +- changed function DdeCallback(), removed delays after ON key state + change + +DEBUGGER.C +- bugfix in function Debugger(), used function to set icon which + hasn't worked properly under WinXP + +DISPLAY.C +- changed function UpdateMenuDisplay(), minor code optimizations + +EMU48.C +- changed function UpdateWindowStatus(), added macro specifiy menu + control +- changed function CopyItemsToClipboard(), simplified implementation + in UNICODE mode by saving the data in the CF_UNICODETEXT format +- changed function SettingsProc(), added IDC_PORT2LOAD button + handling +- changed function OnStackPaste(), minor code optimization +- changed function OnDropFiles(), OnStackPaste() and OnObjectLoad(), + removed delays after ON key state change +- changed function OnLButtonDown(), OnLButtonUp(), OnMouseMove(), + OnKeyDown() and OnKeyUp(), disable function handling when in macro + play mode +- bugfix in function WinMain(), szCurrentKml wasn't initialized +- changed function MainWndProc(), added some macro handler functions + +EMU48.DSP +- added keymacro.c sources + +EMU48.RC +- added "..." button Settings dialog +- changed "Always" checkbox in "KML Script Compilation Result" + dialog from a pushlike to normal one for better readability under + Windows XP style +- added "Macro Settings" dialog +- added Menuitem Tools "Macro..." +- changed version and copyright + +EMU48.H +- replaced T2CYCLES with the one from OPCODES.H + +EMU48DLL.DSP +- added keymacro.c sources + +ENGINE.C +- bugfix in function AdjustSpeed(), on large operations the + workaround condition for Win2k was detected and the following + synchronizing failed because the CPU didn't got the free running + cycles for the waited time +- bugfix in function UpdateKdnBit(), wrong DWORD truncation of + Chipset.cycles variable +- changed function WorkerThread(), close running keyboard macros + when switching into invalid state + +FILES.C +- changed function WriteStack(), minor optimization when saving as + string +- changed function MapRom(), added detection for packed ROM images +- bugfix in function OpenDocument(), check for empty KML file name + was wrong, so the error handler of the following InitKML() was + always called in this case +- bugfix in function OpenDocument(), reload KML button state from + last document close +- changed function LoadObject(), file access is now shared +- changed function DibNumColors() and CreateBIPalette(), changed + argument qualifier + +KEYBOARD.C +- changed function KeyboardEvent(), added call to key recording + function KeyMacroRecord() +- bugfix in function KeyboardEvent(), prevent buffer overflow with + too big out codes on Chipset.Keyboard_Row array + +KEYMACRO.C +- new modul supporting keyboard macro functions + +KML.C +- changed variable qualifier of eIsBlock[] table and removed + TOK_NONE item from it +- bugfix, global variables pKml and pKmlList hadn't be initialized +- removed usage of global variables bKmlLogOkEnabled and nKmlFiles +- changed function AddToLog(), changed argument qualifier and minor + code optimization +- changed function PrintfToLog(), use a local buffer instead of the + heap for the output buffer of the wvsprintf() function, this helps + to reduce fragmentation of the heap +- changed function KMLLogProc(), minor code optimization and removed + use of global variable bKmlLogOkEnabled and windows redraw in the + WM_INITDIALOG case +- changed function DisplayKMLLog(), removed use of global variable + bKmlLogOkEnabled and transfer state of bOkEnabled over the lParam + argument to the dialog box procedure +- bugfix in function CreateKmlList(), fixed a memory leak in the + case of no title +- changed function ChooseKMLProc(), minor code optimization +- changed function IsBlock(), minor code optimization +- bugfix in function ParseString(), fixed memory leak in error case + and optimized implementation +- changed function IncludeLines(), IncludeBlocks(), LoadKMLGlobal() + and InitKML(), file access is now shared +- added function ReloadButtons(), update internal button state from + chip keyboard matrix content +- added function PlayKey(), handle button with OutIn code + +KML.H +- added ReloadButtons() and PlayKey() function prototype + +MOPS.C +- bugfix in function WriteIO(), writing to timer2 failed if first + writing address was before the timer2 address area +- changed function WriteIO(), changing the CONTRLSB (0x101) or the + DISPTEST (0x102) register doesn't effect any display pointer, so + update request for recalculating the pointers removed + +OPCODES.C +- bugfix in function o802() and o803(), the r=IN opcodes work also + on odd addresses in the IO register memory mapping area + +OPS.H +- bugfix in function Nsub() and Nrsub(), in some situations wrong + results in dec mode with illegal decimal number entry as 2nd + operator + +PCH.H +- added VERIFY macro + +RESOURCE.H +- added several definitions + +SETTINGS.C +- changed function ReadSettings() and WriteSettings(), added section + [Macro] in INI-File + +TIMER.C +- bugfix in function RescheduleT2(), better accuracy on conversation + of timer2 ticks into multimedia timer ms +- changed function StartTimers(), added calculation of maximum + timer2 ticks that can be handled by one timer event +- changed function StartTimers() and StopTimers(), changed + multimedia timer to best possible timer resolution instead of + risking to fail with 1 ms resolution preset + + +Service Pack 35 for Emu48 Version 1.0 + +DDESERV.C +- replaced all LocalAlloc() with HeapAlloc() memory requests + +DEBUGGER.C +- replaced all LocalAlloc() with HeapAlloc() memory requests +- changed function UpdateProfileWnd(), replaced CONST TCHAR * with + LPCTSTR + +DISASM.C +- changed several variable qualifiers from TCHAR * to LPCTSTR and + replaced rest of TCHAR * by LPTSTR +- changed variable qualifier of hp_reg_1_af and hp_reg_2_af +- changed function append_tab(), speed optimization +- changed function append_field(), removed use of temporary variable + +DISPLAY.C +- renamed variable nLcdDoubled to nLcdZoom +- changed function UpdateMainDisplay() and UpdateMenuDisplay(), + minor improvement by reading each display line now with the actual + display start pointer + +EMU48.C +- renamed variable nLcdDoubled to nLcdZoom +- replaced all LocalAlloc() with HeapAlloc() memory requests +- bugfix in function CopyItemsToClipboard(), don't worked in UNICODE + mode because character translation was missing + +EMU48.H +- extern declaration of global variable +- moved T2CYCLES definition from OPCODES.H +- renamed variable nLcdDoubled to nLcdZoom +- changed function DuplicateString(), replaced LocalAlloc() with + HeapAlloc() memory request + +EMU48.RC +- changed version +- changed resource language description to English + +FILES.C +- replaced all LocalAlloc() with HeapAlloc() memory requests +- changed function CreateBIPalette(), allocated memory for + LOGPALETTE isn't initialized with zero any more + +KML.C +- renamed variable nLcdDoubled to nLcdZoom +- replaced all LocalAlloc() with HeapAlloc() memory requests +- bugfix in function PrintfToLog(), temporary buffer was half of + size in UNICODE mode +- changed function BrowseFolder() and ChooseKMLProc(), renamed + IDC_EMU48DIR to IDC_EMUDIR and IDC_EMU48DIRSEL to IDC_EMUDIRSEL +- bugfix in function BrowseFolder(), the shell's allocator release + was missing in one case +- changed function ChooseKMLProc(), optimized IDC_EMUDIRSEL command + case +- bugfix in function ParseString(), strings greater 127 characters + caused a buffer overflow in UNICODE mode +- bugfix in function KillKML(), display size wasn't reset to the + preset value at the end + +RESOURCE.H +- renamed IDC_EMU48DIR to IDC_EMUDIR +- renamed IDC_EMU48DIRSEL to IDC_EMUDIRSEL + +RPL.C +- replaced all LocalAlloc() with HeapAlloc() memory requests + +OPCODES.H +- moved T2CYCLES definition to EMU48.H + +OPS.H +- changed function Nadd(), optimized "illegal number in dec mode" + detection +- changed function Ninc(), Ndec(), Nsub() and Nrsub(), removed + register wrap handling +- changed function Nneg(), optimized non zero value handling +- bugfix in function Nsrc(), the Sticky Bit (SB) wasn't handled in + all rSRC opcodes + +TIMER.C +- bugfix in function CalcT2(), estimated timer2 ticks were sometimes + to large because there was a misbehavior between real time clock + and counted CPU cycles when in free running mode +- changed function RescheduleT2(), optimized initialization of + synchronization variables used in CalcT2() +- changed function SetHP48Time(), changed variable typ of ticks and + time from LONGLONG to ULONGLONG +- changed function ReadT2(), minor optimization calculating the + timer2 value + +Service Pack 34+ for Emu48 Version 1.0 +- added new calcualtor type: P, 2 and Q for 39G+, 48Gii and 49G+. +- added 80 lines display and header support +- added extra speed for Apple series +- added BUSCC function support +- change the compile always visible by default + +Service Pack 34 for Emu48 Version 1.0 + +CURSOR.C +- new modul to create a hand cursor + +DEBUGGER.C +- bugfix in function NewValue(), EnterAddr(), EnterBreakpoint(), + EditBreakpoint(), InfoIntr() and InfoWoRegister(), uncompleted + wParam argument decoding in WM_COMMAND case +- replaced function OnToggleCheck() with ToggleBreakpointItem(), a + more general function to toggle a breakpoint check box +- changed function EditBreakpoint(), adjusted LBN_DBLCLK case to use + the new ToggleBreakpointItem() function and modified behavior of + VK_SPACE to toogle all selected items + +DISPLAY.C +- replaced variable use of hOldLcdBitmap with hLcdBitmap +- replaced variable use of hOldMainBitmap with hMainBitmap + +EMU48.C +- removed limitation to 16 COM ports +- changed variable qualifier of szLicence +- added global variables +- added Critical Section initialization for CPU slow down +- changed function SetWindowTitle(), changed argument qualifier +- bugfix in function SettingsProc(), Disasm() and About(), + uncompleted wParam argument decoding in WM_COMMAND case +- changed function OnCreate() and OnDestroy(), added load and + destroy of two new cursor resources +- bugfix in function MainWndProc(), wrong wParam argument decoding + in WM_SYSCOMMAND case +- changed function WinMain(), removed class cursor from main window +- bugfix in function WinMain(), fixed possible buffer overflows in + state file loading message + +EMU48.DSP +- added cursor.c sources + +EMU48.H +- extern declaration of global variables and functions + +EMU48.RC +- changed IDC_DEBUG_MEM from static to listbox control, looks better + in connection with WinXP style +- changed version and copyright + +FILES.C +- renamed variable szEmu48Directory[] to szEmuDirectory[] +- changed buffer size information of files and pathes from 260 to + MAX_PATH definition +- bugfix in function GetOpenFilename(), GetSaveAsFilename(), + GetLoadObjectFilename() and GetSaveObjectFilename(), adjusted + size of temporary buffer to size of destination buffer and fixed + memory allocation bug in UNICODE mode +- changed function LoadBitmapFile(), it's better to validate the + pbyRom instead of the hRomFile variable to check if the ROM is + loaded to avoid possible side effects (ROM loaded by resource) and + removed uncalled code part + +ENGINE.C +- bugfix in function AdjustSpeed(), AdjKeySpeed() and SetSpeed(), + reference setting wasn't synchronized + +KML.C +- renamed variable szEmu48Directory[] to szEmuDirectory[] +- bugfix in function KMLLogProc(), uncompleted wParam argument + decoding in WM_COMMAND case and update of the bAlwaysDisplayLog + variable only in the IDOK and IDCANCEL case +- bugfix in function ChooseKMLProc(), uncompleted wParam argument + decoding in WM_COMMAND case and added UNREFERENCED_PARAMETER + statement +- bugfix in function ParseLines() and ParseBlocks(), fixed problem + of uninitialized variable which may occur on parsing corrupt KML + files +- bugfix in function KillKML(), set variables bDebug and nKMLFlags + to there startup values +- changed function MouseMovesTo(), added code part to select cursor + +OPCODES.C +- bugfix in function o3X(), o8082X(), oBb0(), oBb1(), oBb2(), + oBb3(), oBb4(), oBb5(), oBb6() and oBb7(), used wrong cpu cycles + +PCH.H +- added IDC_HAND definition for non Win2k OS project + +SETTINGS.C +- renamed variable szEmu48Directory[] to szEmuDirectory[] +- changed function ReadSettings() and WriteSettings(), added item + "ClassicCursor" in section [KML] in the INI-File + + +Service Pack 33 for Emu48 Version 1.0 + +DEBUGGER.C +- bugfix in function Debugger(), removed illegal call of + DestroyMenu() in WM_DESTROY message handler +- added function UpdateDbgCycleCounter() to handle a 64 bit CPU + cycles counter in debug mode +- added implementation of "Profiler" dialog box + +DEBUGGER.H +- extern declaration of global function + +EMU48.C +- changed function WinMain(), added non modal "Profiler" dialog box + in main message handler + +EMU48.H +- extern declaration of global variable + +EMU48.RC +- changed version and copyright +- bugfix in dialogs, some WS_EX_ styles (especially window borders) + don't worked on some operating systems +- added Menuitem Info "Profiler..." in debugger menu +- added "Profiler" dialog + +ENGINE.C +- bugfix in function Debugger(), fixed problem at quitting debugger + when last executed opcode was SHUTDN +- changed function Debugger(), added call for 64 bit cpu cycle + counter adjust + +KEYBOARD.C +- bugfix in function KeyboardEvent(), CPU slow down by calling the + function AdjKeySpeed() on key pressed must be activated here + +OPCODES.C +- changed function o802() and o803(), removed AdjKeySpeed() call + +RESOURCE.H +- added definitions + +RPL.C +- replaced object prolog entries by definitions + +SERIAL.C +- bugfix in function CommSetBaud(), changed DCB setting from 1 to 2 + stop bits which is closer to the original 2-3/16 stop bits + +TIMER.C +- changed function TimeProc(), removed check if timer running + + +Service Pack 32 for Emu48 Version 1.0 + +DDESERV.C +- bugfix in function DdeCallback(), DDE commands POKE and REQUEST + worked also on machines with no stack and debugger control in DDE + command POKE was missing + +DEBUGGER.C +- bugfix in function OnKeyF7() and OnKeyF8(), on expected longer + operations like with skipping interrrupt code, the menu and + toolbar should be set into run mode; a general setting into run + mode wasn't used because of menu flickering +- changed function OnStackPush() and OnStackPop(), stack elements + can be pushed or poped on any stack level now +- bugfix in function OnLButtonUp(), forgot P and WP field selection + update on P register changing +- changed function OnKeyRightLeft(), code optimizations +- changed function NotifyDebugger(), handle changed argument type +- bugfix in function Debugger(), set the menu and toolbar into run + mode at startup, this prevents wrong active buttons when interrupt + code should be skipped and the calculator can't leave the + interrupt code because the ON key is permanently down or another + interrupt source prevent leaving the interrupt handler + +DEBUGGER.H +- added new debugger notify defines + +EMU48.C +- changed function OnCreate(), don't enable DragAcceptFiles() here +- changed function OnDropFiles(), checking calculator model to + disable stack writing isn't necessary any more + +EMU48.DSP +- new makefile for MSVC 6.0 + +EMU48.H +- extern declaration of global function (DLL version) +- added hardware type define + +EMU48.RC +- changed version + +ENGINE.C +- changed function Debugger(), added ability to detect an ASM and + RPL breakpoint on the same address +- bugfix in function ResumeDebugger(), resume may hung in CPU + shutdown mode + +FILES.C +- changed function OpenDocument() and SaveDocumentAs(), added + callback notify function call for DLL version + +KML.C +- added "Hardware" keyword in Global section +- changed function CreateKmlList(), added checking the "Hardware" + keyword to ignore KML scripts for other emulators +- changed function InitGlobal(), added logfile output for the + "Hardware" keyword +- changed function InitKML(), enable object "Drag and Drop" + calculator model dependend here now + +KML.H +- added TOK_HARDWARE definition + + +Service Pack 31 for Emu48 Version 1.0 + +DEBUGGER.C +- bugfix in function Debugger(), used wrong background color in + WM_CTLCOLORSTATIC message handler for WinXP style + +DISPLAY.C +- bugfix in function WriteToMainDisplay() and WriteToMenuDisplay(), + drew to display even if the DON bit in the =BITOFFSET (0x100) + register was cleared + +EMU48.C +- added global variable +- changed function SettingsProc(), added IDC_OBJECTLOADWARNING check + box handling +- bugfix in function OnViewSettings(), removed calling of function + ReadSettings() because INI file settings may be invalid here +- changed function OnObjectLoad(), to disable warning message use a + global variable now + +EMU48.H +- extern declaration of global variable +- changed prototype of function ScanKeyboard() + +EMU48.RC +- added item "Show Load Object Warning" in Settings dialog +- added "..." button in "Choose Your KML Script" dialog +- changed version + +ENGINE.C +- bugfix in function UpdateKdnBit(), update KDN bit only when + keyboard interrupt is on and the timer is running + +FILES.C +- bugfix in function NewDocument(), set the RST bit in the LPE + (0x109) register to indicate a POR (Power On Reset) +- bugfix in function RestoreBackup(), when the KML script + initialization failed the Chipset.Port0, Chipset.Port1 and + Chipset.Port2 pointer referenced to unallocated memory + +IO.H +- added LPD and LPE definitions + +KEYBOARD.C +- bugfix in function ScanKeyboard(), a stopped timer don't prevent + keyboard status update, it prevents the 1ms keyboard polling and + generating a keyboard interrupt; also now distinguish keyboard + update between 1ms keyboard poll and direct update with A=IN and + C=IN command +- changed function KeyboardEvent(), call function ScanKeyboard() in + the 1ms poll context now + +KML.C +- new functions BrowseCallbackProc() and BrowseFolder(), helper + functions to browse a directory +- changed function ChooseKMLProc(), added implementation of browser + button, removed WM_SETTEXT message handler and changed WM_COMMAND + handler from 'if' to 'switch' structure +- bugfix in function ChooseKMLProc(), used wrong array size of + szEmu48Directory in UNICODE mode + +MOPS.C +- bugfix in function CpuReset(), set the RST bit in the LPE (0x109) + register to indicate NRES reset +- bugfix in function Npeek(), Nread() and Nwrite(), wrap around over + address #FFFFF failed +- bugfix in function ReadIO(), reset the RST bit in the LPE (0x109) + register after reading +- bugfix in function WriteIO(), added implementation of the RST bit + in the LPE (0x109) register + +OPCODES.C +- renamed define BIGENDIAN to _BIGENDIAN to avoid problems with the + definition in WINSOCK2.H +- bugfix in function o158x(), o159x(), o15Cx(), o15Dx(), o15Ax(), + o15Bx(), o15Ex(), o15Fx(), o807(), o819f0(), o819f1(), o819f2(), + o819f3(), oF0(), oF1(), oF2(), oF3(), oF4(), oF5(), oF6() and + oF7(), used wrong cpu cycles +- changed function o800() and o801(), call function ScanKeyboard() + in the 1ms poll context now +- bugfix in function o802() and o803(), added ScanKeyboard() + function call for direct Chipset.in update +- bugfix in function o8080(), added ScanKeyboard() function call to + activate 1ms keyboard polling +- changed function o80810(), call function ScanKeyboard() in the + direct update context now + +OPCODES.H +- changed some remarks + +PCH.H +- added include shlobj.h + +RESOURCE.H +- added definition + +SETTINGS.C +- changed definitions, allows setting of INI filename and registry + path in the makefile now +- changed function ReadSettings() and WriteSettings(), added item + "LoadObjectWarning" in section [Files] in the INI-File + + +Service Pack 30 for Emu48 Version 1.0 + +DEBUGGER.C +- bugfix in function Debugger(), used wrong debugger window position + when task bar was on top or left side +- added implementation of "Write-Only Register" dialog box + +DDESERV.C +- added io.h in header definition +- removed usage of Chipset.dispon variable + +DISPLAY.C +- added sources of gray scale emulation (experimental) +- replaced the LCD1_ROW by the LCD_ROW definition and the use of the + LCD2_ROW and the LCD3_ROW definitions by a calculation base on + LCD_ROW +- changed macro LINES(), added brackets forcing operator priority +- changed BITMAPINFO color table variable type from DWORD to RGBQUAD +- changed LCD bitmap from 64 palette entries to the number of used + colors in the LCD area, so function UpdateContrast() do not change + the drawing patterns to a new palette entry, it change the palette + itself now +- bugfix in function UpdateContrast(), switch palette to LCD off + background color if display is off +- bugfix in function DestroyLcdBitmap(), contrast palette wasn't + restored to startup colors +- changed function WriteToMainDisplay(), used wrong sizeof argument + to get the 4 byte x-multiplier in the calculation of the memory + position in the LCD bitmap +- bugfix in function StartDisplay(), the VBL counter of a switched + on display starts at the LINECOUNT (0x128,0x129) register content + +EMU48.C +- removed usage of Chipset.dispon variable +- bugfix in function SetCommList(), used wrong NO_SERIAL string + definition for UNICODE mode +- bugfix in function OnDestroy(), call function SwitchToState() only + if CPU emulation thread is running +- bugfix in function OnViewCopy(), clipboard contained wrong data + when left display margin was unequal to zero + +EMU48.H +- changed prototype of function StartDisplay() +- extern declaration of global function + +EMU48.RC +- fixed misspellings "hexdezimal" to "hexadecimal" +- added "Write-Only Register" dialog +- added Windows XP manifest resource +- changed version and copyright + +EMU48.XML +- manifest file for Windows XP + +ENGINE.C +- changed function WorkerThread(), added parameter to StartDisplay() + function call + +FILES.C +- bugfix in function NewDocument(), OpenDocument() and + RestoreBackup(), used wrong window restore position when task bar + was on top or left side +- bugfix in function SaveBackup(), get workspace instead of screen + coordinates of main window +- changed function LoadObject(), changed text output of "load error" + message boxes + +KML.C +- changed function InitAnnunciator() and DrawAnnunciator(), + determine the number of valid entries now from the annunciator + array size + +MOPS.C +- removed usage of Chipset.dispon variable +- bugfix in function Npeek(), fixed most of the wrong read values in + the I/O register area +- changed function Nwrite(), added conditional gray scale compiling + and added parameter to StartDisplay() function call +- bugfix in function WriteIO(), after changing the DON bit in + BITOFFSET (0x100) the display contrast must be updated as well + +RESOURCE.H +- added some definitions + +SERIAL.C +- redesign of character transmit part, on some PC systems corrupted + characters were sent + +TIMER.C +- workaround for Win2k in function CalcT2(), when detecting a + "negative" time use the CPU cycles for new timer2 value + + +Service Pack 29 for Emu48 Version 1.0 + +DEBUGGER.C +- bugfix in function UpdateMiscWnd(), readded trailing spaces in + "On" text +- changed function Debugger(), replaced bDbgEnable with nDbgState + access + +DEBUGGER.H +- added new debugger state defines + +DISPLAY.C +- added variable and functions from MOPS.C +- new functions StartDisplay() and StopDisplay() to control display + +EMU48.C +- added VOID as parameter in functions with no argument +- added Critical Section initialization for gray scale emulation +- changed function UpdateWindowStatus(), OnDropFiles(), + OnStackPaste() and OnObjectLoad(), replaced bDbgEnable with + nDbgState access +- changed function OnDropFiles(), OnFileClose(), OnStackPaste() and + OnObjectLoad(), added debugger control + +EMU48.H +- added defines for conditional gray scale compiling +- removed declaration of bDbgEnable +- extern declaration of global variables and functions + +EMU48.RC +- changed version and copyright + +ENGINE.C +- removed global variable bDbgEnable, replaced by a state in the + nDbgState variable +- added new functions SuspendDebugger() and ResumeDebugger() for + debugger control +- changed functions Debugger(), WaitForSleepState(), SwitchToState() + and WorkerThread(), added debugger control instead of general + debugger switching off +- changed function CheckSerial(), removed interrupt handling and + made function public +- changed function SwitchToState() and WorkerThread(), replaced + bDbgEnable with nDbgState access +- changed function WorkerThread(), main loop don't check status of + serial port any more, serial port must be initialized before + entering the RUN state now, added start/stop VBL counter at + entering/leaving the RUN state + +FILES.C +- changed function WriteStack(), minor optimization + +MOPS.C +- removed variable ioc_acc +- moved variable byVblRef and the functions F4096Hz() and + GetLineCounter() to DISPLAY.C +- changed function CpuReset(), must close serial port here +- changed function WriteIO(), control of the BITOFFSET (0x100) + register calls now the functions StartDisplay() and StopDisplay() + to modify the VBL counter reference +- changed function WriteIO(), changing the IOC (0x110) register + must also update the serial port status, because this isn't + handled in the CPU main loop any more + +TIMER.C +- changed function RescheduleT2(), calculate now ceil of time delay + to reduce checking of timer2 value without timer2 overflow + + +Service Pack 28 for Emu48 Version 1.0 + +DEBUGGER.C +- bugfix in table MemMap[], NCE3 reference in HP39/40 part was wrong +- bugfix in function SetMappingMenu(), used wrong variable type for + UNICODE mode +- changed function UpdateMiscWnd(), removed trailing spaces in text +- new functions OnStackPush(), OnStackPop() and OnStackModify() and + other changes for implementation of the stack context menu +- changed function OnFindOK(), minor optimizations +- changed function OnNewValue(), now returns exit condition + +DISPLAY.C +- removed usage of Chipset.dispon variable + +EMU48.C +- bugfix in function SetCommList(), detection of COM ports larger + than COM9 failed +- moved shutdown of CPU emulation thread from the WinMain() to the + OnDestroy() function to avoid trouble with closed window DC + +EMU48.DSP +- added configuration "Release Unicode" +- added configuration "Debug Unicode" + +EMU48.RC +- added "Stack" menu entries +- added accelerator key in "Find" dialog +- changed version and copyright + +I28F160.C +- bugfix in table byQueryTab[], content of not specified offset 0x03 + is 0x02; offset 0x1B and 0x1D contain 0x30 in the real chip +- changed function WrStateE8N(), WrStateE8D() and WrState60D(), use + device specific data from query table instead of program constants +- bugfix in function WrStateE8C(), don't set the BWSLBS bit in the + status register on trying to set a 0 bit +- changed function WrState40D(), removed useless increment +- bugfix in function WrState30C(), don't set the ECLBS bit in the + status register when trying to erase a lock bit with WP# = low +- changed function WrState20C() and WrState30C(), use device + dimensions from query table now and optimized data filling part +- bugfix in function WrState60D(), the BWSLBS or the ECLBS bit in + the status register is set when trying to modify lock bits with + WP# = low +- bugfix in function RdStateId() and RdStateQuery(), Flash commands + "Read Identifier Codes" and "Read Query" now behave on illegal + address access like the original calculator + +RESOURCE.H +- added definitions + +SERIAL.C +- bugfix in function SerialThread(), added implementation of the RER + bit in the RCS (0x111) register for receiver BREAK condition +- bugfix in function CommOpen(), opening COM ports larger than COM9 + failed +- bugfix in function CommTxBRK(), added LPB (Loop Back) emulation + + +Service Pack 27 for Emu48 Version 1.0 + +DEBUGGER.C +- various changes in many functions for implementation of the module + memory view +- changed function CreateToolbar(), replaced InitCommonControlsEx() + with InitCommonControls() function call to be compatible with the + project version 0x0200 + +EMU48.C +- changed function Disasm(), can use value of external port2 size + directly now + +EMU48.RC +- added "Mapping" menu entries +- changed version + +FILES.C +- changed function MapPort2(), don't limit port2 to 128KB here + +I28F160.C +- bugfix in function WrStateE8N(), a "Write to Buffer" command + failed when requesting for the buffer length + +KML.C +- added VOID as parameter in function prototypes +- replaced structure names changed in KML.H into the new ones + +KML.H +- changed structure names Token to KmlToken, Line to KmlLine, Block + to KmlBlock, Button to KmlButton and Annunciator to KmlAnnunciator + because the Line and Button definition clash with the MacOS API + +MOPS.C +- changed function MapP2(), limit port2 mapping size to 128KB here + +OPCODES.C +- changed function o138(), o139(), o13A(), o13B(), o13C(), o13D(), + o13E(), o13F(), o19d2(), o1Ad4(), o1Dd2() and o1Ed4(), added big + endian machine support (use define BIGENDIAN) + +PCH.H +- added _WIN32_IE definition for project version + +RESOURCE.H +- added definitions + + +Service Pack 26 for Emu48 Version 1.0 + +DEBUGGER.C +- bugfix in function ViewMemWnd(), wrong ASCII view of characters in + text area, strings are saved in LSB, MSB order +- changed function Debugger(), save and restore debugger window + position in session now +- bugfix in function Debugger(), missing initialization of menu + entry CODE Object Breakpoints +- bugfix in function OnFindOK(), search for ASCII character in + LSB, MSB order + +DISPLAY.C +- bugfix in function ResizeWindow(), 'Offset' command in KML script + section 'Background' hasn't worked + +EMU48.C +- added Critical Section initialization +- bugfix in function OnPaint(), 'Offset' command in KML script + section 'Background' hasn't worked + +EMU48.H +- extern declaration of global variable and function + +EMU48.RC +- changed version + +FILES.C +- changed functions ResetDocument(), ResetBackup() and + InitializeOFN(), replaced function FillMemory() with ZeroMemory() +- changed function OpenDocument(), can handle various Chipset sizes + now + +KML.C +- bugfix in function DrawButton() and ClipButton, 'Offset' command + in KML script section 'Background' hasn't worked + +MOPS.C +- bugfix function WriteIO(), when writing to the IOC (0x110) + register a possible USRQ interrupt wasn't generated +- changed function WriteIO(), writing the RER bit of the RCS (0x111) + register and writing the TBF bit of the TBR (0x116,0x117) register + is thread save now +- bugfix function WriteIO(), added implementation of the BRK bit in + the TCS (0x112) register +- changed function WriteIO(), removed NINT2 line update at writing + TBR (0x116,0x117) register, is doing in function CommTransmit() + +PCH.H +- added include ctype.h + +SERIAL.C +- minimized size of PC receive buffer +- bugfix in function SerialThread(), CommOpen(), CommClose() and + CommTransmit(), added implementation of the RER bit (only for + framing errors) in the RCS (0x111) register and of the TBF and TBZ + bit in the TCS (0x112) register +- new function CommTxBRK(), handle BREAK condition on Tx line + + +Service Pack 25 for Emu48 Version 1.0 + +DDESERV.C +- replaced variable type _TCHAR with TCHAR +- added some UNREFERENCED_PARAMETER statements + +DEBUGGER.C +- replaced variable type _TCHAR with TCHAR +- added UNREFERENCED_PARAMETER statement +- bugfix in function UpdateCodeWnd(), used uninitialized variables + at first call and optimized actual address searching +- changed function UpdateStackWnd(), save stack content also as + value for easier access +- changed function OnKeyF6(), optimized getting address of selected + item +- changed function OnKeyF8(), optimized GOSUB, GOSUBL, GOSBVL + decoding +- replaced functions OnNOP3Break(), OnRplBreak() and + OnInterruptsStepOverInt() with OnToggleMenuItem() +- changed function OnDblClick(), double click on stack window item + now shows the address content in the code window +- bugfix in function InfoIntr(), variable lIndex may be used without + having been initialized + +DISASM.C +- replaced variable type _TCHAR with TCHAR +- bugfix in function disassemble(), the r=-r fs opcodes showed + always as r=r-1 fs opcodes + +DISPLAY.C +- added UNICODE support in debug output parts +- used wrong black and white RGB definition, so default display + color was white on black background and not vice versa +- changed function UpdateContrast(), optimization + +EMU48.C +- replaced variable type _TCHAR with TCHAR +- changed function CopyItemsToClipboard(), SetCommList(), + OnStackCopy() and OnStackPaste(), added some brackets + +EMU48.H +- replaced variable type _TCHAR with TCHAR +- added MapData defines +- extern declaration of global variable and function +- removed declaration of Npack() and Nunpack() + +EMU48.RC +- changed properties of IDC_DEBUG_STACK +- added Menuitem Debug "CODE Objects Breakpoints" in debugger menu +- changed version and copyright + +ENGINE.C +- replaced variable type _TCHAR with TCHAR +- changed function Debugger(), added DOCODE breakpoint handling + +EXTERNAL.C +- added ops.h in header definition + +FILES.C +- replaced variable type _TCHAR with TCHAR +- added ops.h in header definition +- changed function PatchRom(), added typecast for GCC compiler +- bugfix in function PatchRom(), lines beginning with a remark were + sometimes decoded as a valid line +- bugfix in function MapPort2(), 32KB cards were rejected + +I28F160.C +- changed table definitions fnWrState[] and fnRdState[], removed a + CONST statement + +KML.C +- replaced variable type _TCHAR with TCHAR +- changed function ParseLines(), added some brackets +- changed prototypes of function GetStringParam(), GetIntegerParam() + and SkipLines(), fixed argument mismatch +- changed function RunLine(), added default case for GCC compiler +- changed function SkipWhite(), If() and InitKML(), removed unused + variables +- changed function KillKML(), optimizations +- changed function DrawAnnunciator(), optimizations + +KML.H +- replaced variable type _TCHAR with TCHAR + +MOPS.C +- added UNICODE support in debug output parts +- added ops.h in header definition +- moved MapData defines to EMU48.H +- made function MapData() public +- changed function Npeek(), Nread() and Nwrite(), added brackets for + better code understanding + +OPCODES.C +- changed function o8Cd4() and o8Ed4(), added some spaces for better + code understanding + +OPS.H +- changed function Npack() and Nunpack(), made them static +- changed function Nneg(), added some brackets + +RESOURCE.H +- added definition + +RPL.C +- added ops.h in header definition +- added missing HP39/40G entries in RAM entry table + +SERIAL.C +- replaced variable type _TCHAR with TCHAR +- changed function UpdateUSRQ(), added return type +- changed function CommTransmit() and CommReceive(), derivate + interrupt generation from the state of the USRQ bit now + +SETTINGS.C +- replaced variable type _TCHAR with TCHAR +- bugfix in registry macro WriteString(), used wrong data size in + UNICODE mode +- bugfix in function GetRegistryString(), returned wrong string + length and used wrong read buffer size in UNICODE mode + +TIMER.C +- added ops.h in header definition +- changed function MAX(), had no definition of return value +- changed function RescheduleT2(), added some brackets + + +Service Pack 24 for Emu48 Version 1.0 + +DBGTOOL.BMP +- bitmap with debugger toolbar buttons + +DEBUGGER.C +- added toolbar to debugger window +- changed function EnterBreakpoint(), DrawBreakpoint() and + EditBreakpoint(), added RPL breakpoint handling +- changed function OnToggleCheck(), changed method of item redraw +- changed function EditBreakpoint(), added multiple selection for + deleting breakpoints + +DEBUGGER.H +- added new breakpoint define + +DISASM.C +- bugfix in function rn_map(), handle NULL pointer pages now +- bugfix in function disasm_8(), fixed broken opcode 80810 + mnemonic + +EMU48.C +- changed function SetWindowTitle(), optimization + +EMU48.DSP +- added library comctl32.lib + +EMU48.RC +- added debugger toolbar +- added RPL breakpoint in "Enter breakpoint" dialog +- changed version and copyright + +ENGINE.C +- changed function Debugger(), added RPL breakpoint handling + +PCH.H +- added include commctrl.h + +RESOURCE.H +- added definitions + + +Service Pack 23 for Emu48 Version 1.0 + +DDESERV.C +- replaced numbers with state machines defines + +DEBUGGER.C +- new function DisableMenuKeys(), replace menu item disable in + function OnKeyF5() and OnKeyF9() +- bugfix in function StrToReg() and OnLButtonUp(), used ASCII + instead of a member function of TCHAR.H +- changed function OnLButtonUp(), skip automatically disabled + windows +- bugfix in function OnKeyF9(), "Run to Cursor" menu item wasn't + disabled + +DISASM.C +- bugfix in function disasm_8(), illegal opcodes beginning with 8081 + crashed emulator, illegal opcodes beginning with 818f were shown + as r=r+CON fs,n and r=r-CON fs,n opcodes, illegal opcodes + beginning with 819f were shown as rSRB.F opcodes + +EMU48.C +- replaced numbers with state machines defines +- moved the ShowWindow() call in main program behind the state file + load to avoid viewing the window at the standard startup position + +EMU48.RC +- changed version and copyright + +ENGINE.C +- replaced numbers with state machines defines +- bugfix in function Debugger(), checked wrong data breakpoint + addresses in connection with the P,WP,XS,S,M fields + +FILES.C +- bugfix in function SaveDocument(), when the emulator window is + minimized the wrong window position was saved in the Chipset + structure + +IO.H +- changed LINECOUNT MSB definitions + +KEYBOARD.C +- replaced numbers with state machines defines + +KML.C +- replaced numbers with state machines defines + +MOPS.C +- bugfix in function CpuReset(), BS-FF must be cleared as well + +OPCODES.C +- bugfix in function o807(), added BS-FF reset handling +- removed function o_invalid() + +OPCODES.H +- removed prototype of function o_invalid() + + +Service Pack 22 for Emu48 Version 1.0 + +DEBUGGER.C +- moved global variables to EMU48.C and ENGINE.C +- changed function ViewCodeWnd() and NotifyDebugger(), changed "->" + PC position marker to "-R" on RPL breakpoints +- bugfix in function DisableDebugger(), don't destroy handle before + windows closed +- changed function Debugger(), set signal for window and last + instruction buffer closed at end of WM_DESTROY message handler, + init additional variables +- added implementation of "Find" dialog box +- bugfix in function NewValue(), EnterAddr() and EnterBreakpoint(), + used ASCII instead of a member function of TCHAR.H +- changed function OnToggleCheck(), OnDrawBreakWnd(), + DrawBreakpoint() and EditBreakpoint(), now the list box element + use a link to the corresponding breakpoint element +- bugfix in function OnDrawBreakWnd(), sometimes the old focus + rectangle wasn't purged +- changed function EditBreakpoint(), checkbox can also be toggled + with the space key now +- bugfix in function OnEditBreakpoint(), redrawed code window always + at PC position and not at actual address + +DEBUGGER.H +- moved extern declaration of global variables to EMU48.H + +DISPLAY.C +- bugfix in function UpdateDisplayPointers(), UpdateMainDisplay(), + UpdateMenuDisplay(), WriteToMainDisplay(), WriteToMenuDisplay(), + calculated wrong display areas when LINECOUNT (0x128,0x129) + register was zero + +EMU48.C +- replaced numbers with state machines defines +- added global variable from DEBUGGER.C +- changed function UpdateWindowStatus(), use groups for + enable/disable menu items now +- changed function SettingsProc(), use global variable if COM port + is open instead of calling CommConnect() +- bugfix in function OnPaint(), calculated wrong rectangle for + display area when LINECOUNT (0x128,0x129) register was zero +- bugfix in function OnDropFiles(), OnStackPaste(), OnObjectLoad(), + can't switch on if in debugger mode +- changed function WinMain(), added non modal "Find" dialog box in + main message handler +- bugfix in function WinMain(), fixed a resource leak + +EMU48.DSP +- added library advapi32.lib + +EMU48.H +- removed extern declaration of global variable and function +- removed COM port state defines +- added state machine defines +- extern declaration of global variables +- changed function prototype + +EMU48.RC +- changed properties of IDC_BREAKEDIT_WND +- added "Find" dialog +- changed version + +ENGINE.C +- replaced numbers with state machines defines +- made COM port open flag public +- added global variables from DEBUGGER.C +- changed function Debugger(), a RPL breakpoint is now set on the + destination address of a PC=(A) or a PC=(C) instruction and the + RPL breakpoint detection with a prior A=DAT0 A D0=D0+ 5 sequence + has been removed +- changed function CheckSerial(), use return value of CommOpen() + instead of calling CommConnect() +- changed function WaitForSleepState(), close debugger before going + into sleep state +- changed function SwitchToState(), changed detection of emulation + thread termination and debugger now can be active when changing + from Run to Sleep state + +FILES.C +- changed function OpenDocument(), because of changed function + CpuReset() additional work for automatic restart on changed ROM + needed + +IO.H +- added BITOFFSET definition + +MOPS.C +- removed variable ir_ctrl_acc, was never used +- bugfix in function CpuReset(), changed some initial settings +- bugfix in function WriteIO(), force new COM port initialization + when the EIRU bit in the IRC (0x11A) register has changed +- bugfix in function WriteIO(), reading from the LINECOUNT + (0x128,0x129) register destroyed the settings for the + Chipset.lcounter variable + +RESOURCE.H +- added some definitions + +RPL.C +- changed function RPL_CreateTemp(), changed remarks + +SERIAL.C +- removed function CommConnect(), not used any more +- changed function CommOpen(), changed function return type +- bugfix in function CommOpen(), receiver state variables weren't + resetted +- bugfix in function CommClose(), changed detection of thread + termination and fixed a resource leak +- bugfix in function CommTransmit(), receive buffer may be + overflowed when LPB (Loop Back) bit was set + +SETTINGS.C +- prepared for registry support + + +Service Pack 21 for Emu48 Version 1.0 + +CHECKBOX.BMP +- bitmap with checked and unchecked box + +DDESERV.C +- prepared for UNICODE support + +DEBUGGER.C +- prepared for UNICODE support +- added implementation of "Run to Cursor" +- added HP39/40G stuff to function InitBsArea() and UpdateMiscWnd() +- added Enable/Disable of breakpoints in the Edit breakpoints dialog +- added breakpoint load/save file functions +- bugfix in function NewValue(), EnterAddr(), EnterBreakpoint(), + EditBreakpoint() and InfoIntr(), try to get a window handle at + begin of a dialog message handler cause illegal handles in some + cases + +DEBUGGER.H +- extern declaration of global variable and functions + +DISASM.C +- prepared for UNICODE support +- changed function rn_port2(), select internal or external port2 + memory by the Chipset.Port2Size variable now + +DISPLAY.C +- prepared for UNICODE support + +EMU48.C +- prepared for UNICODE support +- added HP39/40G stuff to function UpdateWindowStatus(), + SettingsProc(), OnDropFiles() and Disasm() +- bugfix in function OnPaint(), redraw display and button area even + if emulation is suspended, redraw only requested bitmap area and + redraw display without updating bitmap with display data +- bugfix in function OnFileNew(), overwrite title only on new + document + +EMU48.H +- prepared for UNICODE support + +EMU48.ICO +- deleted unused device images + +EMU48.RC +- changed properties of IDC_BREAKEDIT_WND +- added Menuitem Debug "Run to Cursor" in debugger menu +- changed version and copyright + +ENGINE.C +- prepared for UNICODE support +- added part for debugger function "Run to Cursor" + +EXTERNAL.C +- added HP39/40G stuff to the SFLAG53_56 definition + +FILES.C +- prepared for UNICODE support +- added new document type for HP39/40G in several functions +- bugfix in function MapPort2(), in the first program instance in + not shared mode a read only port2 file couldn't be loaded +- bugfix in function ResetDocument(), delete MMU mappings as well +- bugfix in function OpenDocument(), ask for new KML script when + emulator state file and KML script file use different 'Model' + settings +- changed function OpenDocument() and SaveDocument(), added debugger + breakpoint data handling + +IO.H +- added LINECOUNT definition + +KML.C +- prepared for UNICODE support +- added "Class" keyword in Global section +- deleted function IsDigit(), replaced by function _istdigit() +- bugfix in function AddToLog(), fixed a memory leak +- new function MapKMLFile(), map KML file into memory +- new functions iSqrt(), AdjustPixel() and TransparentCircle(), + subroutines for new button type +- changed function DrawButton(), changed button type 5 (was same + implementation as type 0) to draw transparent circles +- changed function RefreshButtons(), redraw only buttons in + requested area + +KML.H +- prepared for UNICODE support +- added TOK_CLASS definition + +MOPS.C +- new function UpdateDisplay(), handle display update +- changed function MapP2(), select internal or external port2 memory + by the Chipset.Port2Size variable now +- added HP39/40G stuff to function MapROM(), RomSwitch(), Nread(), + Nwrite() and ReadIO() +- bugfix in function Nwrite(), update display with address content +- changed function ReadIO(), set RX bit in the RCS (0x111) register + for no character receive +- bugfix in function ReadIO(), wrong implementation of TBR register + (0x116,0x117), read return last written byte +- bugfix in function WriteIO(), clearing the SON bit in the IOC + (0x110) register clears also the IOC (0x110), RCS (0x111), TCS + (0x112), RBR (0x114,0x115) and TBR (0x116,0x117) register +- bugfix in function WriteIO(), writing to the RCS (0x111), TCS + (0x111) and TBR (0x116,0x117) register only works when the SON bit + in the IOC (0x110) register is set + +OPCODES.C +- prepared for UNICODE support +- removed InfoMessage in SREQ? opcode + +PCH.H +- added include tchar.h + +RESOURCE.H +- added some definitions +- removed IDC_DISASM_MNEMONICS + +RPL.C +- bugfix in function Metakernel(), check for port1 memory before + reading + +SERIAL.C +- prepared for UNICODE support +- bugfix in function UpdateUSRQ(), USRQ bit in SRQ1 (0x118) register + only set on serial interrupt condition when UART enabled + +SETTINGS.C +- prepared for UNICODE support +- changed function ReadSettings(), use variable content as default + +TIMER.C +- bugfix in function StartTimers(), check for timer interrupts at + timer start + + +Service Pack 20 for Emu48 Version 1.0 + +DEBUGGER.C +- changed function UpdateStackWnd(), index of topmost item is now + saved at content update +- changed function CheckBreakpoint(), allow breakpoints in an area + now (used by memory breakpoints to get all touched addresses on a + r=DATx fs access) + +DEBUGGER.H +- changed prototype of function CheckBreakpoint() + +EMU48.C +- use semaphores to avoid GDI trouble with NT in OnPaint() +- specify processor affinity for the cpu emulation thread to avoid + problems with the QueryPerformanceCounter() function + +EMU48.H +- extern declaration of global variable + +EMU48.RC +- fixed multiple use of accelerator key in debugger menu +- changed version and copyright + +ENGINE.C +- bugfix in function Debugger(), check complete address range of + memory access area +- workaround for Win2k in function AdjustSpeed(), when command + sequence took over 50ms new synchronizing of the emulation slow + down part +- bugfix in function WorkerThread(), update NINT and NINT2 lines + after port status changing + +KML.C +- use semaphores to avoid GDI trouble with NT in RefreshButtons() + +MOPS.C +- bugfix in function UckBit() and F4096Hz(), depending on clock + value functions returned wrong results + +OPCODES.C +- moved address field start and length array from OPS.H and made + them public + +OPCODES.H +- extern declaration of global address field start and length + variables + +OPS.H +- moved address field start and length array to OPCODES.C +- bugfix in function FASTPTR(), address area is only 5 nibbles long + +RESOURCE.H +- deleted several unused definitions + +RPL.C +- added function Metakernel() to detect MK2.30 and prior +- changed function RPL_Pick() and RPL_Push() to support + "Save/Load Object" with a stack incompatible Metakernel versions + +SERIAL.C +- use own OVERLAPPED stucture for reading and writing +- bugfix in function CommClose(), added additional delay to fix + problems with some Kermit server +- bugfix in function CommTransmit(), wait until write completed + +SETTINGS.C +- changed function ReadSettings(), use variable content as default + + +Service Pack 19 for Emu48 Version 1.0 + +DEBUGGER.C +- several modifications for memory breakpoint handling +- added step over interrupt code part setting +- bugfix in function NewValue(), at wrong input numbers ignore input + string at and set focus to edit control +- bugfix in function EnterAddr(), at wrong input numbers set focus + to edit control + +DEBUGGER.H +- added breakpoint type defines +- extern declaration of global variable +- changed prototype of function CheckBreakpoint() + +DISPLAY.C +- added io.h in header definition +- bugfix in function CreateMainBitmap(), check error condition and + realize palette also in memory DC +- bugfix in CreateLcdBitmap(), must set color palette for LCD DC +- bugfix in function UpdateAnnunciators(), annunciators are off if + timer2 is stopped + +EMU48.C +- added io.h in header definition +- changed function SettingsProc(), added HP/Class mnemonic setting +- bugfix in function SettingsProc(), new implementation of card + detection port 1 +- moved initialize/remove of the Critical Section part from main + program to message handler +- changed function OnDropFiles(), OnStackCopy(), OnStackPaste(), + OnObjectLoad() and OnObjectSave(), don't wait for changed state + after function WaitForSleepState() +- bugfix in function OnObjectSave(), set info message when emulator + is busy +- changed function Disasm(), removed HP/Class mnemonic setting +- bugfix in function Disasm(), addresses > 0x1869F showed in wrong + format +- bugfix in function WinMain(), synchronized thread start + +EMU48.H +- changed definition of PORT1_PRESENT, PORT1_WRITE, PORT2_PRESENT + and PORT2_WRITE + +EMU48.RC +- added HP/Class mnemonics in "Settings" dialog +- added "Interrupts" part in debugger menu +- added "Enter breakpoint" dialog +- changed version and copyright + +ENGINE.C +- moved debugger part from function WorkerThread() to own function +- added C=DAT0 A, D0=D0+ 5, PC=(C) code sequence detection for RPL + breakpoint in debugger +- added step over interrupt code part handler in debugger section +- bugfix in function SwitchToState(), when switch from Invalid into + Run state then don't enter opcode loop on interrupt request or in + SHUTDN mode +- changed function WorkerThread(), added memory breakpoint handler +- bugfix in function WorkerThread(), timer emulation in debugger + part must check the timer RUN bit +- bugfix in function WorkerThread(), must set wakeup flag on SHUTDN + on interrupt request condition +- changed function WorkerThread(), minor optimization + +FILES.C +- added io.h in header definition +- bugfix in function OpenDocument(), check card detection only if + enabled and set NINT line to low on a MP interrupt +- changed function DibNumColors(), optimized by removing case switch + +IO.H +- added ANNCTRL and CARDSTAT definitions + +MOPS.C +- removed conditional compiling with WSMSET +- bugfix in function RomSwitch(), mirror smaller ROMs than 2MB +- bugfix in function Npeek(), wrong content of SRQ2 (0x119) register +- bugfix in function ReadIO(), wrong implementation of the SMP and + SWINT bit in the CARDCTL (0x10E) register +- bugfix in function ReadIO(), wrong implementation of the NINT2 and + NINT bit in the SRQ2 (0x119) register +- bugfix in function WriteIO(), wrong implementation of the SMP and + ECDT bit and removed some wrong stuff in the CARDCTL (0x10E) + register +- bugfix in function WriteIO(), the DA19 bit in the LINECOUNT + (0x129) register is also available in the Clarke hardware +- bugfix in function WriteIO(), the RUN bit in the TIMER2CTRL + (0x12F) register has an affection to the display annunciators + +OPCODES.C +- added io.h in header definition +- bugfix in RTI command, low NINT2 or NINT line reenter interrupt +- bugfix in function o808C() and o808E(), opcodes PC=(A) and PC=(C) + modify the CRC register + +OPCODES.H +- moved HST bit definition to TYPES.H + +RESOURCE.H +- added several definitions + +SETTINGS.C +- changed function ReadSettings() and WriteSettings(), added section + [Disassembler] in INI-File + +TYPES.H +- added definition of HST bits + + +Service Pack 18 for Emu48 Version 1.0 + +EMU48.RC +- changed version and copyright + +ENGINE.C +- bugfix in function WorkerThread(), ignore SHUTDN on interrupt + request + +SERIAL.C +- bugfix in function CommOpen(), must wait for EV_RXCHAR event + thread directly after opening the serial port + + +Service Pack 17 for Emu48 Version 1.0 + +DEBUGGER.C +- bugfix in function StrToReg() and OnLButtonUp(), wrong string to + HEX conversation with lowercase letters +- bugfix in function Debugger(), used wrong background color in + WM_CTLCOLORSTATIC message handler + +EMU48.C +- bugfix in function SettingsProc(), don't overwite Serial-Ir + setting in HP49 mode + +EMU48.DSP +- added i28f160.c sources + +EMU48.H +- extern declaration of global variable and functions + +EMU48.RC +- changed version and copyright + +FILES.C +- added new functions in the ROM patch part +- changed function PatchRom(), original content of patched address + is saved now +- changed function MapRom(), added ROM writing feature +- changed function UnmapRom(), restore original content before + closing +- bugfix in function MapPort2(), shared mode now works, when first + program instance opened the file with Read/Write access +- changed function NewDocument() and OpenDocument(), initialize + flash memory structure + +I28F160.C +- new modul with I28F160 flash memory implementation + +I28F160.H +- header file for flash memory implementation + +MOPS.C +- changed function MapROM(), added flash register mode +- changed function Npeek(), added flash access part +- changed function Nread(), completed flash access part +- changed function Nwrite(), completed flash access part + +OPS.H +- bugfix in function FASTPTR(), handle NULL pointer pages now +- bugfix in function Ninc(), Ndec(), Nadd(), Nsub(), Nrsub(), Nnot() + and Nneg(), wrong results in dec mode with illegal decimal number + entry + +SETTINGS.C +- changed function ReadSettings() and WriteSettings(), added section + [ROM] in INI-File + +TIMER.C +- bugfix in function CalcT2(), workaround to minimize skipping + timer2 values + + +Service Pack 16 for Emu48 Version 1.0 + +DEBUGGER.C +- added "MMU" and "Miscellaneous" view in debugger dialog +- added RPL exit breakpoint on A=DAT0 A, D0=D0+ 5, PC=(A) sequence +- added last instruction viewer in debugger +- renamed function name ViewStackWnd() to UpdateStackWnd() +- bugfix in function UpdateRegisterWnd() and OnCtlColorStatic(), + fixed problems of register update in RUN mode +- bugfix in function OnKeyF11(), in sleep mode break hasn't worked +- bugfix in function OnLButtonUp(), register setting wasn't disabled + in running state +- changed function NotifyDebugger(), added argument to detect RPL + breakpoint +- bugfix in function Debugger(), code disassembler wasn't reset to + mapped mode at startup and fixed a resource leak +- bugfix in function EditBreakpoint(), added breakpoint buffer + overflow check at add breakpoint command + +DEBUGGER.H +- extern declaration of global variables + +DISASM.C +- bugfix in function disasm_1(), the C=ss, ss=C and CssEX opcodes + showed always as A=ss, ss=A and AssEX opcodes +- bugfix in function disasm_8(), the r register in the rSRB.F fs + opcodes and the opcode lenght was wrong + +EMU48.C +- new function CopyItemsToClipboard(), copy selected items from + listbox to clipboard, extracted from function Disasm() +- added HP38G64K stuff to function UpdateWindowStatus() and + SettingsProc() +- changed function Disasm(), call function CopyItemsToClipboard() + now + +EMU48.H +- extern declaration of global functions + +EMU48.RC +- added "MMU" and "Miscellaneous" part in debugger dialog +- changed copyright date in IDD_ABOUT +- changed version and copyright + +ENGINE.C +- added RPL breakpoint handling +- added last instruction buffer update handling +- changed function WorkerThread(), added timer emulation in debugger + mode +- bugfix infunction WorkerThread(), in debugger mode Chipset.pc may + have been changed, so the FASTPTR access must be updated as well + +EXTERNAL.C +- some minor changes for the HP38G64K beeper emulation + +FILES.C +- added new document type for HP38G64K + +IO.H +- added CARDCTL and LCR definitions + +KEYBOARD.C +- bugfix in function ScanKeyboard(), update keyboard status only on + timer running and keyboard interrupt only occur on the rising edge + of "logic or" of IR[8:0] and not on the rising edge of each line; + the IRX15 interrupt (ON key) is level sensitive + +MOPS.C +- added function MapData() to detect the memory controller handle + the given address, needed for function Npeek(), Nread() and + Nwrite() +- bugfix in function MapROM(), no difference between Clarke and + Yorke chip in DA19 bit behavior, depends on ROM size +- bugfix in function Map(), on a HP49 NCE3 may mapped to ROM +- changed function CpuReset(), reset WSM state of flash memory +- bugfix in function Npeek(), update card status register in I/O + register area before reading and simulate an open data bus +- bugfix in function Nread(), loaded wrong bank switcher FF value + for a HP49 +- bugfix in function Nwrite(), loaded wrong bank switcher FF value + when writing on slot2 enabled (GX only) +- changed function Nread() and Nwrite(), added Flash memory access + detection part +- bugfix in function ReadIO(), the CARDSTATUS (0x10F) register + return zero when card detection is disabled +- bugfix in function WriteIO(), on a HP49 force new memory mapping + on changing the LED bit in the LCR (0x11C) register + +RESOURCE.H +- added several definitions + +TYPES.H +- conditional compiling of cycle counter (32bit EXE/64bit DLL) +- use position of Port2_NBanks for flash memory WSM state variable + + +Service Pack 15 for Emu48 Version 1.0 + +DEBUGGER.C +- added emulation run flag +- added notify function for emulation stopped +- added color highlighting on changed registers +- added implementation of "Step Out" (exit function) +- bugfix in function ViewMemWnd(), use the Npeek() function for + reading memory data, so fixed some problems with reading invalid + data from the I/O register area +- changed function OnClearAll(), changed argument to dialog handle +- bugfix in function OnDblClick(), fixed swapped nibbles after enter + new data +- changed function OnKeyCodeWnd(), changed first argument to dialog + handle +- bugfix in function Debugger(), removed context menu resource leaks + and a call of GetDlgItem() with invalid arguments + +DEBUGGER.H +- extern declaration of global functions +- moved WM_UPDATE definition to DEBUGGER.C + +EMU48.C +- replaced __argc and __argv variables by process variables +- replaced INI file handling part +- changed address in GPL license string +- added HP38 stuff to function UpdateWindowStatus() and + SettingsProc() +- changed function SettingsProc(), replaced Port1_Writeable variable +- bugfix in function OnViewReset(), some registers wasn't reset, + call reset function now +- bugfix in function OnDestroy(), moved functions SwitchToState() + and WriteSettings() to main program, because emulation thread may + not run and settings aren't read so far when main window close +- removed function FlushMessages() and all references, because it's + useless and references made trouble at a WM_QUIT message +- removed several other resource leaks on error conditions +- made function MainWndProc() public + +EMU48.DSP +- renamed serial.h into io.h in header definition +- added settings.c sources + +EMU48.H +- extern declaration of global variable and functions + +EMU48.RC +- changed language definition of dialog IDD_BREAKEDIT +- added Menuitem Debug "Step Out" in debugger menu +- changed version and copyright + +ENGINE.C +- renamed header Serial.h into io.h +- renamed wShutdnWake to bShutdnWake in Chipset structure +- moved bRealSpeed variable and make it public +- solved bRealSpeed name conflict +- added DWORD casting from Chipset.cycles variable +- added part for debugger function "Step Out" +- changed notify function for update debugger window + +EXTERNAL.C +- some minor changes for the HP38 beeper emulation + +FILES.C +- added new document type for HP38G +- replaced __argc and __argv variables by process variables +- added function CrcRom() to get a fingerprint of the ROM +- changed function NewDocument(), deleted Port1_Writeable variable, + isn't used any more +- bugfix in function OpenDocument(), in restore situation add port2 + only at HP48 emulation +- changed function OpenDocument(), check fingerprint of ROM +- changed function SaveDocument(), update fingerprint of ROM +- changed function SaveBackup(), save window position as well +- bugfix in function RestoreBackup(), lost port2 of a HP48 + +KEYBOARD.C +- renamed wShutdnWake to bShutdnWake in Chipset structure +- added DWORD casting from Chipset.cycles variable + +KLM.C +- bugfix in function InitKML(), on a KML script syntax error only + continue with the Cancel button + +IO.H +- old SERIAL.H, added I/O definitions + +MOPS.C +- renamed header Serial.h into io.h +- renamed function CRC() to UpCRC() +- changed function UckBit(), removed unused variable +- changed function MapP1(), use cards_status instead of + Port1_Writeable variable +- bugfix in function RomSwitch(), lached address line A6 was always + zero -> no access to upper half of flash memory +- added function CpuReset() to set cpu registers after reset +- changed function Npeek(), return data from I/O register area as + well now (for debugger support) +- bugfix in function Nread(), reading from the timer2 MSB (0x13F) + register updates the CRC (0x104-0x107) register +- bugfix in function ReadIO(), update the USRQ bit in the SRQ1 + (0x118) register after reading the RBR (0x114,0x115) register +- bugfix in function ReadIO(), update the NINT2 and NINT bits in the + SRQ2 (0x119) register +- bugfix in function WriteIO(), update the USRQ bit in the SRQ1 + (0x118) register after writing the IOC (0x110) register or the + TBR (0x116,0x117) register + +OPCODES.C +- added DWORD casting from w.cycles variable +- checked all undocumented cpu cycles and removed remarks +- bugfix in function o08(), o09(), o0A(), o0B(), o3X(), o8086n(), + o8087n(), o808An(), o808Bn() and o8Ed4(), changed number of cpu + cycles + +RESOURCE.H +- added several definitions + +SERIAL.C +- renamed header Serial.h into io.h +- renamed wShutdnWake to bShutdnWake in Chipset structure +- moved initialize/remove of Critical Section handler from the + function CommOpen() and CommClose() to main program +- added function UpdateUSRQ(), implements USRQ bit handling +- bugfix in function CommTransmit(), added LPB bit emulation in the + TCS (0x112) register and USRQ bit emulation in the SRQ1 (0x118) + register +- bugfix in function CommReceive(), only reject reading if com port + is closed and not whole operation and added USRQ bit emulation in + the SRQ1 (0x118) register + +SERIAL.H +- renamed to IO.H + +SETTINGS.C +- new modul to handle the INI file + +TIMER.C +- removed timer I/O definitions, replaced by include file IO.H +- replaced INT define with INTR (conflict with C variable type INT) +- renamed wShutdnWake variable to bShutdnWake +- bugfix in function CheckT1() and CheckT2(), added TSRQ bit + emulation in the SRQ1 (0x118) register + +TYPES.H +- changed variable type of wPosX and wPosY, position data is signed +- use position of wShutdnWake for wRomCrc +- use position of dwKdnCycles for cycles variable +- use position of Port1_Writeable for MSB of cycles variable +- use position of Port2_Writeable for dwKdnCycles variable +- use old position of the cycles variable for bShutdnWake + + +Service Pack 14 for Emu48 Version 1.0 + +COLOR.H +- new header with color definitions + +DDESERV.C +- added different files headers for HP48/49 + +DEBUGGER.C +- new modul for debugger routines + +DEBUGGER.H +- header file for debugger part + +DISASM.C +- changed function append_r_addr() and append_pc_comment(), relative + jumps now can viewed with the target address instead of a relative + offset and target address in remark; implemented a local variable +- bugfix in function rn_port2(), calculated port2 mask was wrong +- added HP49 stuff to function rn_port2() + +EMU48.DSP +- added debugger.c sources + +EMU48.C +- added debugger menu entry +- added HP49 stuff to the disassembler +- added HP49 stuff to function SettingsProc() +- changed function OnViewSettings(), removed port2 open/close + handling, now done in the document handler +- deleted unused function OnSysClose() +- bugfix in function Disasm(), in entry IDC_DISASM_PORT2 the highest + useable address was always wrong + +EMU48.RC +- added Menuitem Tools "Debugger..." +- changed version and copyright + +EMU48.H +- extern declaration of global variable and functions + +ENGINE.C +- moved inline function FASTPTR() to OPS.H +- added debug part in main emulation loop + +EXTERNAL.C +- some minor changes for the HP49 beeper emulation + +FILES.C +- added new document type for HP49 +- change in function WriteStack(), accept HP49 binary files now +- the port2 open/close handling is now controlled by the document +- several changes in port2 handling, the HP49 use an internal 128KB + RAM card instead of the external RAM card of the HP48 +- changed function UnmapPort2(), added return value for unmap + success +- changed function PatchRom(), longer addresses than 5 nibbles can + be used now +- changed function OpenDocument(), restore HP49 ROM bank selection + after document load +- changed function InitKML(), removed initialization of MMU + +KEYBOARD.C +- changed function ScanKeyboard(), added a flag for wake up from + SHUTDN mode + +KML.C +- bugfix in function PressButton(), if button already pressed, do + nothing -> fixed a draw problem with KML button type 3 +- bugfix in function RefreshButtons(), KML type 3 buttons, must + complete redraw before refresh them + +MOPS.C +- added HP49 MMU parts +- bugfix in function ReadIO(), update the UCK bit in the BAU (0x10D) + register + +OPS.H +- moved inline function FASTPTR() from ENGINE.C to header file + +RESOURCE.H +- added several definitions + +RPL.C +- added HP49 memory pointers +- added HP49 flash pointer object (=DOFASHP) +- added HP49 precision integer object (=DOINT) +- added HP49 precision real object (=DOLNGREAL) +- added HP49 precision complex object (=DOLNGCMP) +- added HP49 symbolic matrix object (=DOMATRIX) +- added HP49 aplet object (=DOAPLET) +- added HP49 mini font object (=DOMINIFONT) + +SERIAL.C +- changed function SerialThread(), added a flag for wake up from + SHUTDN mode + +TIMER.C +- changed functions CheckT1() and CheckT2(), added a flag for wake + up from SHUTDN mode + +TYPES.H +- use position of the wUnused1 variable for SHUTDN wake up flag + + +Service Pack 13 for Emu48 Version 1.0 + +DISASM.C +- bugfix in function disasm_8(), the HS=0 n opcode was showed wrong + +EMU48.DSP +- added fetch.c and opcodes.c sources + +EMU48.C +- bugfix in function Disasm() and About(), handled messages must + return TRUE + +EMU48.H +- added function prototypes + +ENGINE.C +- moved several definitions to OPCODE.H +- made function AdjKeySpeed() public +- replaced opcode handling include files by a function call, decoder + now works with tables instead of case switching +- deleted unused code parts in modul + +FETCH.C +- new modul with opcode dispatcher + +FETCH.H +- file deleted, replaced by FETCH.C + +FILES.C +- bugfix in function PatchRom(), no correct handling of remarks, + interpreter failed on lower case hex digits in arguments + +OPCODES.C +- new modul with opcode implementation + +OPCODES.H +- file content changed, replaced by OPCODES.C + + +Service Pack 12 for Emu48 Version 1.0 + +DISPLAY.C +- minor change in function UpdateMainDisplay() +- deleted remarked global variables and unused code parts in + function WriteToMainDisplay() + +EMU48.C +- added auto event object for controlling the CPU thread + +EMU48.H +- removed extern declaration of variable +- extern declaration of global variable and functions + +ENGINE.C +- new function CheckDisp(), synchronize display update now with the + line update counter +- replaced all ResumeThread() with SetEvent() calls +- replaced all SuspendThread() with WaitForSingleObject() calls + +KEYBOARD.C +- replaced all ResumeThread() with SetEvent() calls +- deleted unused code parts in function ScanKeyboard() + +MOPS.C +- flag for updating display area is now global +- new function GetLineCounter() for getting the actual display + refresh line +- changed function WriteIO(), display isn't updated here any more +- use serial definitions from "serial.h" now +- deleted remarked functions Nread2(), Nwrite2(), Nread5() and + Nwrite5() + +SERIAL.C +- replaced all ResumeThread() with SetEvent() calls + +TIMER.C +- bugfix in function SetT1(), loading same timer value doesn't + restart timer period +- replaced all ResumeThread() with SetEvent() calls + + +Service Pack 11 for Emu48 Version 1.0 + +EMU48.C +- removed cards status definitions +- bugfix in function OnFileExit(), continuing the emulation failed + after pressing the "Cancel" button in save dialogbox +- removed function OnSysClose(), no difference to function + OnFileExit() +- changed message implementation WM_SYSCOMMAND/SC_CLOSE to function + OnFileExit() + +EMU48.DSP +- added preprocessor definition "STRICT" to project + +EMU48.H +- moved cards status definitions from EMU48.C to header file +- extern declaration of global function + +EMU48.RC +- changed version and copyright + +ENGINE.C +- bugfix in function WorkerThread(), after changing the port2 card + status the memory mapping must be updated +- update HP48 time now in function WorkerThread() and not in + function StartTimers() + +FILES.C +- changed function MapPort2(), dwPort2Mask now shows the valid + address lines of the bank switcher FF + +MOPS.C +- bugfix in function MapP1() and MapP2(), clear mapping area if port + is configured but not plugged +- bugfix in function MapP2(), odd bank switch values loaded the + wrong page of port2 and bank switcher must be disabled on S(X) + versions +- changed function Nread(), simulate an open data bus now (fix + value for even and odd address) +- bugfix in function Nread(), don't change bank switcher value if + RAM or CE2 is on the same address, because these modules have + higher access priority +- bugfix in function Nwrite(), bank switching now works with the + 'datx=r fsd' command + +TIMER.C +- renamed function SetAccesstime() to SetHP48Time() and made + function public +- removed update of HP48 time in function StartTimers() + + +Service Pack 10 for Emu48 Version 1.0 + +DISPLAY.C +- use only one pattern field for all display resolutions +- added new display contrast scheme +- bugfix in function WriteToMainDisplay(), calculated source memory + address was illegal memory pointer + +EMU48.C +- bugfix in functions OnFileExit() and OnSysClose(), stop emulation + thread before saving emulation data +- changed function Disasm(), changed list box from single to multi + selectable items, added button to copy selected items to + clipboard +- moved function DragAcceptFiles() to WM_CREATE handler +- added function DragAcceptFiles() to WM_DESTROY handler +- allow second command line parameter as port2 filename +- added Critical Section initialization + +EMU48.H +- removed extern declaration of global function +- extern declaration of global variable and functions + +EMU48.RC +- added button in dialog IDD_DISASM +- reinsert button IDC_UPDATE in dialog IDD_CHOOSEKML +- changed version and copyright + +ENGINE.C +- functions CheckSerial() and AdjustSpeed() are inline coded now +- new function UpdateKdnBit() for updating the KDN bit by the 1ms + keyboard interrupt handler + +FILES.C +- added new function CrcPort2() to get a fingerprint of port2 +- changed function OpenDocument(), check fingerprint of port2 +- changed function SaveDocument(), update fingerprint of port2 + +KEYBOARD.C +- changed function Keyboard_GetIR() to static +- bugfix in function ScanKeyboard(), added fixes for the KDN bit + implementation and update keyboard interrupt pending flag when + 1ms keyboard handler is disabled + +KML.C +- bugfix in function ParseLine(), caused memory leaks when an error + occured +- bugfix in function ParseLines(), fixed a windows program abort on + some KML script or line include errors +- bugfix in function ParseBlock(), abort parsing on KML script + errors +- bugfix in function ParseBlocks(), fixed a windows program abort on + block include errors +- bugfix in function FreeLines(), caused memory leaks when different + argument types are used in a line + +MOPS.C +- added new function IOBit() +- bugfix in function ReadIO(), update the KDN bit in the SRQ2 + (0x119) register before reading + +OPCODES.H +- bugfix in A=IN and C=IN command, update KDN bit in the SRQ + register +- bugfix in all r=r+CON fs,n and r=r-CON fs,n opcodes, they always + work in HEX mode +- changes in INTON, INTOFF and RTI for KDN bit implementation +- changed RTI command, execute pending key interrupt only if enabled +- changed RSI command, set key interrupt pending flag also when 1ms + keyboard handler is disabled +- bugfix in INTON command, execute interrupt if key interrupt is + pending + +OPS.H +- added new inline functions Ninc16() and Ndec16() + +RESOURCE.H +- added/changed definitions +- removed ID_HELP_REGISTER + +TYPES.H +- use position of the uUnused1 variable for fingerprint of port2 +- use position of the Port2_Size variable for cpu cycles at start of + 1ms key interrupt handler + + +Service Pack 9 for Emu48 Version 1.0 + +DISASM.C +- changed output of HP Mnemonic P=n + +DISPLAY.C +- bugfix in function UpdateMainDisplay(), with off display and Zoom + > 1 only a part of the display was cleared +- changed function UpdateMenuDisplay(), use a common pointer offset + calculation and display output part now + +EMU48.C +- changed function OnViewCopy(), changed screen clipboard format + from CF_BITMAP to CF_DIB +- bugfix in function OnViewReset(), must unconfig MMU on reset too +- changed function OnAbout(), don't stop emulation while viewing + About box +- moved initialize/remove of the Critical Section part from message + handler to main program + +EMU48.H +- removed extern declaration of global variables and functions + +EMU48.RC +- changed design of IDD_ABOUT +- changed version and copyright + +ENGINE.C +- bugfix in function FASTPTR(), code execution is possible in IO + register area as well + +FETCH.H +- removed jump to extension opcode 81B0 + +FILES.C +- removed global variable + +MOPS.C +- bugfix in function MapP0(), MapP1(), MapP2(), MapBS(), mapping + area may have holes +- bugfix in function MapBS(), no read from ROM at mapped bank + switch control area +- bugfix in function MapP2(), if G(X) ROM select port2 only if + DA19=0 and BEN=1, now the saved bank switch control information is + used +- bugfix in function MapROM(), if G(X) ROM and DA19=0 (ROM disabled) + then mirror lower ROM at #80000 (AR18=0) +- bugfix in function Nread(), bank switching now works with every + 'r=datx fsd' command in the complete mapped bank switch control + area, an unconfigured bank switch area is now disabled and the + GX bank switch information (content of Flipflop) is saved now +- bugfix in function Nwrite(), at some conditions no update of the + display area and IO area couldn't be unconfigured +- removed functions Nread2(), Nwrite2(), Nread5() and Nwrite5() +- bugfix in function ReadIO(), several minor fixes in the LINECOUNT + (0x128,0x129) register emulation +- bugfix in function ReadIO(), the TIMER1CTRL and TIMER2CTRL + (0x12E,0x12F) register must be updated before reading +- bugfix in function WriteIO(), don't clear the XTRA bit in the + TIMER1CTRL (0x12E) register while setting +- bugfix in function WriteIO(), after setting new TIMER1CTRL and + TIMER2CTRL (0x12E,0x12F) register values the control bit + condition must be checked +- bugfix in function WriteIO(), handle start/stop of the LINECOUNT + register when the DON bit in the DISPIO (0x100) register has + changed +- bugfix in function WriteIO(), force new ROM mapping if DA19 in + the MSB LINECOUNT (0x129) register has changed on a G(X) ROM + +OPCODES.H +- Nwrite2() replaced by Nwrite() +- Nwrite5() replaced by Nwrite() +- Nread2() replaced by Nread() for the BS 'r=datx fsd' bugfix +- Nread5() replaced by Nread() for the BS 'r=datx fsd' bugfix +- bugfix in RTI command, generate interrupt if ON key is pressed +- bugfix in RSI command, don't set key interrupt pending flag if + INTOFF command is active +- removed extension opcode 81B0 + +TIMER.C +- removed assertions in function CheckT1() and CheckT2() that the + timers must run +- moved initialize/remove of Critical Section handler from the + function StartTimers() and StopTimers() to main program for bugfix + the problems with the Critical Section area of the function + ReadT1() and ReadT2() when the timers are stopped +- bugfix in function ReadT2(), timer2 was always running + +TYPES.H +- use Port2_Bank variable to save state of GX port2 Flip-Flop + + +Service Pack 8 for Emu48 Version 1.0 + +DISASM.C +- changed function read_nibble() to disassemble modules +- added new mapping functions for function read_nibble() +- bugfix in function append_imm_nibble(), HP Mnemonic hex number + was showed as decimal number + +EMU48.C +- changed variable types of function WinMain() +- added "drag and drop" implementation for HP objects +- added some features to function Disasm() +- removed all parts handled with the global variables bAccurateTimer + and uT1Period +- bugfix in function Disasm(), button "Next Address" caused a + windows program abort if the given address was outside of the + cpu address area +- bugfix in function UpdateWindowStatus(), Menuitem Tools + "Disassembler..." must be disabled when document file is closed +- bugfix in function OnStackPaste(), fixed problem when HP is off +- bugfix in function OnObjectLoad(), solve problem when calculator + is off and bugfixed method to switch emulator into sleep state +- bugfix in function OnObjectSave(), bugfixed method to switch + emulator into sleep state + +EMU48.DSP +- added library shell32.lib + +EMU48.H +- bugfix of variable hApp, used wrong type +- extern declaration of global variables +- removed extern declaration of global variables +- added some disassembler defines + +EMU48.RC +- added my name to dialog IDD_ABOUT +- added some radio buttons to dialog IDD_DISASM +- removed Timers part from dialog IDD_SETTINGS +- changed version and copyright + +ENGINE.C +- added global variable +- removed global variable +- bugfix in function AdjKeySpeed(), slow down whole emulation now, + when a key is pressed + +KEYBOARD.C +- changed key event signal in function KeyboardEvent() + +MOPS.C +- bugfix in function Uncnfg(), I/O mapped address area begin and end + on a 64 nibble boundary, Chipset.IOBase must be saved for the C=ID + command +- bugfix in function C_Eq_Id(), don't shift Chipset.IOBase for ID +- changed function Nread(), Nwrite(), Nread2(), Nwrite2(), Nread5() + and Nwrite5(), test Chipset.IOCfig as well for mapping + +OPCODES.H +- removed InfoMessage in BUSCB, BUSCC and BUSCD opcode, emulate as + NOP +- bugfix in RTI command, don't return from interrupt at timer1/2 + interrupt condition +- bugfix in SHUTDN command, don't shut down at timer1/2 wake up + condition +- bugfix in RSI command, detection of interrupt is in service was + wrong + +PCH.H +- added include shellapi.h + +RESOURCE.H +- added several definitions +- removed IDC_BROWSEDIR, IDC_REFRESH, IDC_USEMMTIMER, IDC_T1PERIOD, + IDC_T1DEFAULT + +TIMER.C +- removed global variables bAccurateTimer and uT1Period, replaced by + a static variable and a constant +- changed function CheckT1() and function CheckT2(), use function + parameter for the timer value now +- changed function ReadT1() and function ReadT2(), update timer + control bits after reading +- bugfix in function RescheduleT2(), execute timer2 event when MSB + of timer changed to update timer2 control register +- bugfix in function TimeProc(), waiting for timer2 overrun prevents + access to the timer2 value from the cpu emulation thread and reset + of variable uT2TimerId was outside synchronized area +- bugfix in function CheckT1() and function CheckT2(), fixed SRQ bit + handling +- bugfix in function SetT1() and function SetT2(), update timer + control bits and check for interrupts after setting +- bugfix in function StopTimers() and function SetT2(), killing + timer1 or timer2 in a Critical Section part may cause a dead lock + with the timer callback function (function timeKillEvent() waits + for the finish of the corresponding callback function, but the + callback function is locked by the Critical Section handler) + + +Service Pack 7 for Emu48 Version 1.0 + +DISASM.C +- new modul with disassembler + +DISPLAY.C +- bugfixes in function UpdateDisplayPointers(), next line offset was + sometimes wrong and the addresses of the main display area were + wrong when next line offset was negative +- new implementation of function WriteToMainDisplay(), fixed several + bugs (no output when next line offset was negative, sometimes + problems with left margin values > 4 and garbage on display) +- bugfix in function WriteToMenuDisplay(), menu wasn't updated + when next line offset was negative + +EMU48.C +- added global variable +- added semaphore handling for key scan +- added disassembler dialog box and menu entry +- bugfix in function OnViewCopy(), the clipboard wasn't closed when + EmptyClipboard() failed +- bugfix in function OnKeyDown(), suppress autorepeat feature of + keyboard + +EMU48.DSP +- added disasm.c source + +EMU48.H +- extern declaration of global variables +- added function prototypes + +EMU48.RC +- added Menuitem Tools "Disassembler..." +- changed version and copyright + +ENGINE.C +- added global variable +- bugfix in function AdjKeySpeed(), exit delay loop when a key + event has occured and use Chipset.Keyboard_Row[] instead of + Chipset.in to detect a pressed key + +FILES.C +- function LoadBitmapFile() save old hWindowDC palette now +- bugfix in function SaveDocumentAs(), previous opened file wasn't + closed + +KEYBOARD.C +- bugfix in function ScanKeyboard(), code part must be synchronized + and an interrupt was only generated when all input lines has been + low (no keys pressed) +- added parameter to function ScanKeyboard(), parameter indicates if + the Chipset.in interrupt register should be reset +- added key event signal in function KeyboardEvent() +- workaround in function KeyboardEvent(), hold key state for a + definite time + +KML.C +- bugfix in function ParseLines() and ParseBlocks(), allocated + memory for string parameter of include token wasn't freed +- bugfix in function KillKML(), palette resource couldn't freed, + when hWindowDC was open + +MOPS.C +- bugfix in function WriteIO(), the Chipset.start1 variable only + was updated when five nibbles at the DISPADDR (0x120) register + has been written +- bugfix in function WriteIO(), the Chipset.loffset variable only + was updated when three nibbles at the LINEOFFS (0x125) register + has been written +- bugfix in function WriteIO(), the Chipset.lcounter variable only + was updated when the MSB of the LINECOUNT (0x129) register has + been written +- bugfix in function WriteIO(), the Chipset.start2 variable only + was updated when five nibbles at the MENUADDR (0x130) register + has been written +- bugfix in function ReadIO(), the LINECOUNT (0x128-0x129) register + is a down counter + +OPCODES.H +- changed RSI command for thread synchronize + +RESOURCE.H +- added several definitions + +TIMER.C +- bugfix in function SetAccesstime(), sync time values must always + save in port0 and not at the mapped address +- bugfix in function RescheduleT2(), used wrong event time when + reference point for timer2 value wasn't saved + + +Service Pack 6 for Emu48 Version 1.0 + +DDESERV.C +- bugfix in XTYP_POKE part, fixed problem when HP is off + +DISPLAY.C +- added X4 display size parts + +EMU48.C +- changed function OnViewCopy() for X4 display size +- init lFreq variable for high performance counter here now + +EMU48.H +- changed type and name of bLcdDoubled to nLcdDoubled +- extern declaration of global variables + +EMU48.RC +- changed version and copyright + +EXTERNAL.C +- changed high limit of frequency, use SX specification +- added high limit of beep duration, use SX specification +- estimate cpu cycles for beep time +- adjust some cpu flags and registers modified by the original code + +KML.C +- changed TOK_ZOOM implementation in function InitLcd() + +MOPS.C +- changed implementation of registers 0x128-0x12D in function + ReadIO() + +OPCODES.H +- update field select table in "EXTENSION opcode o81B1" + +TIMER.C +- moved static lFreq variable to modul EMU48.C + + +Service Pack 5a for Emu48 Version 1.0 + +EMU48.C +- bugfix in OnPaint(), must redraw annunciators + + +Service Pack 5 for Emu48 Version 1.0 + +DDESERV.C +- return DDE error if emulator state couldn't change to sleep state + +EMU48.C +- added Copy/Paste Clipboard data from/to stack (strings only) + +EMU48.H +- extern declaration of global variables +- added function prototypes + +EMU48.RC +- added Menuitem Edit "Copy String" and "Paste String" +- fixed multiple use of accelerator keys +- changed version and copyright + +ENGINE.C +- added function WaitForSleepState(), wait for the SHUTDN command + -> stack isn't in use (when the clock is displayed, a time string + was sometimes on stack instead of the loaded item), then switch to + state 3 (Sleep state) +- added function AdjKeySpeed(), slow down key repetition + +FILES.C +- save mapping size of port2 in global variable +- optimized calculation of bank switch mask in MapPort2() + +MOPS.C +- bugfix in MapP0(), data offset was wrong +- bugfix in MapP1(), data offset was wrong +- bugfix in MapP2(), data offset was wrong and real size of modul + was zero +- bugfix in MapBS(), wrong pointer in mirrored data page +- bugfix in MapROM(), wrong pointer in mirrored data page +- bugfix in MapP0(), MapP1(), MapP2(), MapBS(), Config(), Uncnfg(), + mapped address area begin and end on a mapping size boundary +- bugfix in Map(), the access priority of CE1 and CE2 was wrong + (SX: port1<->port2, GX: port1<->bank select) +- bugfix in Uncnfg(), the unconfig priority of CE1 and CE2 was wrong + (SX: port1<->port2, GX: port1<->bank select) +- Map(), size optimized implementation +- Config(), size optimized implementation +- Uncnfg(), size optimized implementation +- C_Eq_Id(), size optimized implementation + +OPCODES.H +- insert key slow down in A=IN and C=IN + +RESOURCE.H +- added ID_STACK_COPY and ID_STACK_PASTE + + +Service Pack 4 for Emu48 Version 1.0 + +EMU48.C +- added code for port1 disable +- changed the default box setting of "Accurate Timer" in checked + +EMU48.RC +- added item "Port 1 is Plugged" +- changed version and copyright + +KEYBOARD.C +- bugfixes in 1 ms keyboard interrupt handler, keyboard interrupts + are generated at one of the following conditions: pressing the ON + key (non maskable), pressing a key detected by the 1 ms keyboard + scan when enabled (maskable), detect a key after an OUT command + or after the RSI command when a bit in the IN register is set + +MOPS.C +- bugfix in WriteIO(), TIMER1 was loaded with the current value + +OPCODES.H +- bugfix in INTON and INTOFF command, they aren't able to generate a + keyboard interrupt +- bugfix in RSI command, call interrupt routine or set interrupt + pending flag if a bit of the IN register is high +- RTI command changed, doesn't handle ON key here +- added InfoMessage for BUSCB command + +RESOURCE.H +- added IDC_PORT1EN + +SERIAL.C +- bugfix, synchronize threads for receive char + +TIMER.C +- changed function CheckT1(), quite similar to V1.0 with minor fixes +- changed function CheckT2(), minor fixes +- bugfix in function SetT1(), decrement value after full period, + don't check for interrupts after setting +- bugfix in function SetT2(), don't check for interrupts after + setting + + +Service Pack 3 for Emu48 Version 1.0 + +DDESERV.C +- new modul for DDE communication + +EMU48.C +- init DDE callback function and name service +- added section [Serial] in INI-File +- added combobox update in dialog box handler + +EMU48.DSP +- new makefile for MSVC 5.0 + +EMU48.H +- extern declaration of global variables +- added function prototypes + +EMU48.RC +- added block "Serial" +- changed version and copyright + +ENGINE.C +- bugfix in SetSpeed(), may enable "realspeed" with illegal + reference counts +- bugfix in SwitchToState(), switching from "Invalid" or "Sleep" + state into another, ResumeThread() may called before + SuspendThread() (unsynchronized threads) +- added code for serial device + +FILES.C +- separated stack writing code from LoadObject() to WriteStack() + +MOPS.C +- added global variables +- changed code in ReadIO() for BAUD, IO CONTROL, RCS, TCS, RBR, + SREQ?, IR CONTROL for serial support +- changed code in WriteIO() for BAUD, IOC, TBR, IRC for serial + support + +RESOURCE.H +- added IDC_WIRE and IDC_IR + +SERIAL.C +- new modul for serial support + +SERIAL.H +- new header with serial definitions + + +Service Pack 2 for Emu48 Version 1.0 + +DISPLAY.C +- bugfix in UpdateMainDisplay(), update display even if it's off + +EMU48.C +- added section [Emulator] in INI-File +- added checkbox update in dialog box handler + +EMU48.H +- extern declaration of variables + +EMU48.RC +- added item "Authentic Calculator Speed" +- changed version and copyright + +ENGINE.C +- added code to slow down emulation + +RESOURCE.H +- added IDC_REALSPEED + + +Service Pack 1 for Emu48 Version 1.0 + +DISPLAY.C +- red and blue has been changed in SetLcdColor() +- use display contrast value from chipset and not the default + value in CreateLcdBitmap() +- use semaphores to avoid GDI trouble with NT in + UpdateMainDisplay(), UpdateMenuDisplay(), WriteToMainDisplay() + and WriteToMenuDisplay() +- execute ResizeWindow() only if window exist +- bugfix in draw part of WriteToMainDisplay() + +EMU48.C +- version changed +- added global variable +- added semaphore handling to avoid GDI trouble with NT that prevent + graphic update sometimes +- added message for right mouse key +- bugfix of a parameter of GetPrivateProfileString() +- removed SwitchToState() and KillKML() at end of program +- eliminated memory leak caused by SetWindowTitle() +- function OnPaint() may returned without calling EndPaint() +- execute UpdateWindowStatus() only if window exist + +EMU48.H +- extern declaration of global variable +- added VOID as parameter in function prototypes + +EMU48.RC +- added/changed version and copyright + +EXTERNAL.C +- bugfix, check if beeper enabled +- changed beeper emulation, more realistic with Windows NT + +FILES.C +- avoid an open handle and adjust file attributes in + LoadBitmapFile() +- the WORD to int conversion used in function SetWindowPos() + failed; windows with negative position data wouldn't pop up with + NT (NT GDI is 32 bit coded, Windows 95 mostly 16 bit -> the + failed conversion doesn't matter with Windows 95) +- bugfix in close file handling of function OpenDocument() +- InitKML() need chipset for contrast setting in RestoreBackup() + +KML.C +- added global variables +- use semaphores to avoid GDI trouble with NT in DrawButton() and + DrawAnnunciator() +- added function ReleaseAllButtons() +- changed mouse handling for pressing more than one key + +OPCODES.H +- added S(X) oscillator cycles for each command +- bugfix in A=IN and C=IN command to emulate a saturn bug, on real + machines the assembler commands only work on an even address +- bugfix in all r=r+CON fs,n and r=r-CON fs,n opcodes to emulate a + saturn bug, on real machines commands with a single field selector + cause an overrun to other fields of the register +- NFdbl() replaced by NFadd() +- Ndbl() replaced by Nadd() + +OPS.H +- NFinc() definition changed +- NFdec() definition changed +- NFdbl() definition removed +- Ndbl() function removed +- changed implementation of Ninc(), added offset variable +- changed implementation of Ndec(), added offset variable +- optimized implementation of Nadd() +- optimized implementation of Nsub() +- optimized implementation of Nrsub() +- optimized implementation of Nnot() +- optimized implementation of Nneg() + +TIMER.C +- added global variables, defines and constants +- added function SetAccesstime() for setting the correct time +- bugfix in AbortT2() which prevent updating the clock sometime +- changed implementation of CheckT1() to correct blink frequency + of input cursor +- changed implementation of ReadT2() using a high resolution + timer to minimize skipping timer2 values +- bugfix in SetT2(), always check timer2 control register +- solved a problem with timer2 counts greater than 16.67 min. +- bugfix of wrong timer1/2 values (unsynchronized threads) + +TYPES.H +- use position of the t2_ticks variable for oscillator cycles + + +(c) by Christoph Gießelink diff --git a/SOURCE/CHECKBOX.BMP b/SOURCE/CHECKBOX.BMP new file mode 100644 index 0000000000000000000000000000000000000000..d7cfb44141d8a945d0fab355a91623f0789fa356 GIT binary patch literal 250 zcmZvVu?>JQ3p0EjIWypHynUt{5aDFzRf+^6Y1^^}&5n)zpe(as6 z`tA3VYK!!l>ki04uA0x#3F%TZ&FI}W#smM}f0-Qe%q$<^6c2ZKUEQ?QM?Ead^suaR zc+hXlmoN^wo7jKqX_?QuDmY)yrZXO-Lr*F)!%$ZR=t7U;yf3}=CNvanUnRr zT#tlsOI^|qb}f$epsvbe6;epn7^Cy-^^{H6*88;Z_&o$+!$SV!`O_n~X4E6?7|3Js z!lR5$7aL>6*nRF1W-z1AnQvI`GxnDf3Qi0jyyQ`V3e#r*6UeSc#IxdXTk_10IIleq q-s3#;rR~iFoN*a?1AKH6JP*I~mu`ud4Hl2eep(Vfp^6lc%x=F*kTZz@ literal 0 HcmV?d00001 diff --git a/SOURCE/DDESERV.C b/SOURCE/DDESERV.C new file mode 100644 index 0000000..24ffff0 --- /dev/null +++ b/SOURCE/DDESERV.C @@ -0,0 +1,169 @@ +/* + * DdeServ.c + * + * This file is part of Emu48 + * + * Copyright (C) 1998 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "io.h" + +HDDEDATA CALLBACK DdeCallback(UINT iType,UINT iFmt,HCONV hConv, + HSZ hsz1,HSZ hsz2,HDDEDATA hData, + DWORD dwData1,DWORD dwData2) +{ + TCHAR *psz,szBuffer[32]; + HDDEDATA hReturn; + LPBYTE lpData,lpHeader; + DWORD dwAddress,dwSize,dwLoop,dwIndex; + UINT nStkLvl; + BOOL bSuccess; + + // disable stack loading items on HP38G, HP39/40G + BOOL bStackEnable = cCurrentRomType!='6' && cCurrentRomType!='A' && cCurrentRomType!='E' && cCurrentRomType!='P'; // CdB for HP: add P type + + switch (iType) + { + case XTYP_CONNECT: + // get service name + DdeQueryString(idDdeInst,hsz2,szBuffer,ARRAYSIZEOF(szBuffer),0); + if (0 != lstrcmp(szBuffer,szAppName)) + return (HDDEDATA) FALSE; + // get topic name + DdeQueryString(idDdeInst,hsz1,szBuffer,ARRAYSIZEOF(szBuffer),0); + return (HDDEDATA) (INT_PTR) (0 == lstrcmp(szBuffer,szTopic)); + + case XTYP_POKE: + // quit on models without stack or illegal data format or not in running state + if (!bStackEnable || iFmt != uCF_HpObj || nState != SM_RUN) + return (HDDEDATA) DDE_FNOTPROCESSED; + + // get item name + DdeQueryString(idDdeInst,hsz2,szBuffer,ARRAYSIZEOF(szBuffer),0); + nStkLvl = _tcstoul(szBuffer,&psz,10); + if (*psz != 0 || nStkLvl < 1) // invalid number format + return (HDDEDATA) DDE_FNOTPROCESSED; + + DdeAccessData(hData,&dwSize); // fetch data size + DdeUnaccessData(hData); + + // reserve memory + if ((lpData = HeapAlloc(hHeap,0,dwSize * 2)) == NULL) + return (HDDEDATA) DDE_FNOTPROCESSED; + + SuspendDebugger(); // suspend debugger + bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control + + if (!(Chipset.IORam[BITOFFSET]&DON)) // HP off + { + // turn on HP + KeyboardEvent(TRUE,0,0x8000); + KeyboardEvent(FALSE,0,0x8000); + } + + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + { + HeapFree(hHeap,0,lpData); // free memory + hReturn = DDE_FNOTPROCESSED; + goto cancel; + } + + while (nState!=nNextState) Sleep(0); + _ASSERT(nState==SM_SLEEP); + + // fetch data and write to stack + DdeGetData(hData,(LPBYTE) &dwIndex,sizeof(DWORD),0L); + if (dwIndex <= dwSize - sizeof(DWORD)) + dwSize = dwIndex; + dwSize = DdeGetData(hData,lpData+dwSize,dwSize,sizeof(DWORD)); + bSuccess = (WriteStack(nStkLvl,lpData,dwSize) == S_ERR_NO); + HeapFree(hHeap,0,lpData); // free memory + + SwitchToState(SM_RUN); // run state + while (nState!=nNextState) Sleep(0); + _ASSERT(nState==SM_RUN); + + if (bSuccess == FALSE) + { + hReturn = DDE_FNOTPROCESSED; + goto cancel; + } + + KeyboardEvent(TRUE,0,0x8000); + KeyboardEvent(FALSE,0,0x8000); + // wait for sleep mode + while(Chipset.Shutdn == FALSE) Sleep(0); + hReturn = (HDDEDATA) DDE_FACK; + +cancel: + bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control + ResumeDebugger(); + return hReturn; + + case XTYP_REQUEST: + // quit on models without stack or illegal data format or not in running state + if (!bStackEnable || iFmt != uCF_HpObj || nState != SM_RUN) + return NULL; + + // get item name + DdeQueryString(idDdeInst,hsz2,szBuffer,ARRAYSIZEOF(szBuffer),0); + nStkLvl = _tcstoul(szBuffer,&psz,10); + if (*psz != 0 || nStkLvl < 1) // invalid number format + return NULL; + + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + return NULL; + + while (nState!=nNextState) Sleep(0); + _ASSERT(nState==SM_SLEEP); + + dwAddress = RPL_Pick(nStkLvl); // pick address of stack level "item" object + if (dwAddress == 0) + { + SwitchToState(SM_RUN); // run state + return NULL; + } + dwLoop = dwSize = (RPL_SkipOb(dwAddress) - dwAddress + 1) / 2; + + lpHeader = (Chipset.type == 'X' || Chipset.type == 'Q' || Chipset.type == '2') ? BINARYHEADER49 : BINARYHEADER48; + + // length of binary header + dwIndex = (DWORD) strlen(lpHeader); + + // size of objectsize + header + object + dwSize += dwIndex + sizeof(DWORD); + + // reserve memory + if ((lpData = HeapAlloc(hHeap,0,dwSize)) == NULL) + { + SwitchToState(SM_RUN); // run state + return NULL; + } + + // save data length + *(DWORD *)lpData = dwLoop + dwIndex; + + // copy header + memcpy(lpData + sizeof(DWORD),lpHeader,dwIndex); + + // copy data + for (dwIndex += sizeof(DWORD);dwLoop--;++dwIndex,dwAddress += 2) + lpData[dwIndex] = Read2(dwAddress); + + // write data + hReturn = DdeCreateDataHandle(idDdeInst,lpData,dwSize,0,hsz2,iFmt,0); + HeapFree(hHeap,0,lpData); + + SwitchToState(SM_RUN); // run state + while (nState!=nNextState) Sleep(0); + _ASSERT(nState==SM_RUN); + + return hReturn; + } + return NULL; + UNREFERENCED_PARAMETER(hConv); + UNREFERENCED_PARAMETER(dwData1); + UNREFERENCED_PARAMETER(dwData2); +} diff --git a/SOURCE/DEBUGDLL.C b/SOURCE/DEBUGDLL.C new file mode 100644 index 0000000..57eea55 --- /dev/null +++ b/SOURCE/DEBUGDLL.C @@ -0,0 +1,1082 @@ +/* + * DebugDll.c + * + * This file is part of Emu48 + * + * Copyright (C) 2000 Christoph Gießelink + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "Opcodes.h" +#include "ops.h" +#include "debugger.h" + +#include "Emu48Dll.h" + +#define MAXBREAKPOINTS 256 // max. number of breakpoints + +typedef struct // type of breakpoint table +{ + UINT nType; // breakpoint type + DWORD dwAddr; // breakpoint address +} BP_T; + +static DWORD *pdwLinArray; // last instruction linear array + +static DWORD dwRefRstkp; // reference stack pointer content + +static WORD wBreakpointCount = 0; // number of breakpoints +static BP_T sBreakpoint[MAXBREAKPOINTS]; // breakpoint table + +static BOOL bDbgNotify = FALSE; // debugger notify flag +static BOOL bDbgRpl; // Flag for ASM or RPL breakpoint + +// callback function notify debugger breakpoint +static VOID (CALLBACK *pEmuDbgNotify)(INT nBreaktype) = NULL; + +// callback function notify hardware stack changed +static BOOL (CALLBACK *pEmuStackNotify)(VOID) = NULL; + + + +//################ +//# +//# Static functions +//# +//################ + +// +// convert nibble register to DWORD +// +static DWORD RegToDWORD(BYTE *pReg, WORD wNib) +{ + WORD i; + DWORD dwRegister = 0; + + for (i = 0;i < wNib;++i) + { + dwRegister <<= 4; + dwRegister += pReg[wNib-i-1]; + } + return dwRegister; +} + +// +// convert DWORD to nibble register +// +static VOID DWORDToReg(BYTE *pReg, WORD wNib, DWORD dwReg) +{ + int i; + + for (i = 0;i < wNib;++i) + { + pReg[i] = (BYTE) (dwReg & 0xF); + dwReg >>= 4; // next nibble + } + return; +} + + +//################ +//# +//# Public internal functions +//# +//################ + +// +// handle upper 32 bit of cpu cycle counter +// +VOID UpdateDbgCycleCounter(VOID) +{ + return; // not necessary here, cycle counter has 64 bit in DLL version +} + +// +// check for code breakpoints +// +BOOL CheckBreakpoint(DWORD dwAddr, DWORD dwRange, UINT nType) +{ + INT i; + BOOL bBreak = FALSE; // don't break + DWORD dwRomAddr = -1; // no valid ROM address + + // stack changed notify function defined + if (nType == BP_EXEC && pEmuStackNotify != NULL) + { + // hardware stack changed + if (dwRefRstkp != -1 && dwRefRstkp != Chipset.rstkp) + bBreak = pEmuStackNotify(); // inform debugger + + dwRefRstkp = Chipset.rstkp; // save current stack level + } + + // absolute ROM adress breakpoints + if (nType == BP_EXEC) // only on code breakpoints + { + LPBYTE I = FASTPTR(dwAddr); // get opcode stream + + // adress in ROM area + if (I >= pbyRom && I < pbyRom + dwRomSize) + dwRomAddr = I - pbyRom; // linear ROM address + } + + for (i = 0; i < wBreakpointCount; ++i) // scan all breakpoints + { + // check for absolute ROM adress breakpoint + if ( sBreakpoint[i].dwAddr >= dwRomAddr && sBreakpoint[i].dwAddr < dwRomAddr + dwRange + && (sBreakpoint[i].nType & BP_ROM) != 0) + return TRUE; + + // check address range and type + if ( sBreakpoint[i].dwAddr >= dwAddr && sBreakpoint[i].dwAddr < dwAddr + dwRange + && (sBreakpoint[i].nType & nType) != 0) + return TRUE; + } + return bBreak; +} + +// +// notify debugger that emulation stopped +// +VOID NotifyDebugger(INT nType) // update registers +{ + nDbgState = DBG_STEPINTO; // state "step into" + dwDbgStopPC = -1; // disable "cursor stop address" + + _ASSERT(pEmuDbgNotify); // notify function defined from caller + pEmuDbgNotify(nType); // notify caller + bDbgNotify = TRUE; // emulation stopped + return; +} + +// +// disable debugger +// +VOID DisableDebugger(VOID) +{ + nDbgState = DBG_OFF; // disable debugger + bInterrupt = TRUE; // exit opcode loop + SetEvent(hEventDebug); + return; +} + + +//################ +//# +//# Dummy functions for linking +//# +//################ + +// +// entry from message loop +// +LRESULT OnToolDebug(VOID) +{ + return 0; +} + +// +// load breakpoint list +// +VOID LoadBreakpointList(HANDLE hFile) +{ + return; + UNREFERENCED_PARAMETER(hFile); +} + +// +// save breakpoint list +// +VOID SaveBreakpointList(HANDLE hFile) +{ + return; + UNREFERENCED_PARAMETER(hFile); +} + + +//################ +//# +//# Public external functions +//# +//################ + +/**************************************************************************** +* EmuInitLastInstr +***************************************************************************** +* +* @func init a circular buffer area for saving the last instruction +* addresses +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuInitLastInstr( + WORD wNoInstr, // @parm number of saved instructions, + // 0 = frees the memory buffer + DWORD *pdwArray) // @parm pointer to linear array +{ + if (pdwInstrArray) // circular buffer defined + HeapFree(hHeap,0,pdwInstrArray); // free memory + + if (wNoInstr) // new size + { + pdwLinArray = pdwArray; // save address of linear array + wInstrSize = wNoInstr + 1; // size of circular buffer + wInstrWp = wInstrRp = 0; // init write/read pointer + + pdwInstrArray = HeapAlloc(hHeap,0,wInstrSize*sizeof(*pdwInstrArray)); + if (pdwInstrArray == NULL) // allocation error + return TRUE; + } + return FALSE; +} + +/**************************************************************************** +* EmuGetLastInstr +***************************************************************************** +* +* @func return number of valid entries in the last instruction array, +* each entry contents a PC address, array[0] contents the oldest, +* array[*pwNoEntries-1] the last PC address +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuGetLastInstr( + WORD *pwNoEntries) // @parm return number of valid entries in array +{ + WORD i; + + if (pdwInstrArray == NULL) return TRUE; // circular buffer not defined + + // copy data to linear buffer + *pwNoEntries = 0; + for (i = wInstrRp; i != wInstrWp; i = (i + 1) % wInstrSize) + pdwLinArray[(*pwNoEntries)++] = pdwInstrArray[i]; + + return FALSE; +} + +/**************************************************************************** +* EmuRun +***************************************************************************** +* +* @func run emulation +* +* @xref none +* +* @rdesc BOOL: FALSE = OK +* TRUE = Error, Emu48 is running +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuRun(VOID) +{ + BOOL bErr = (nDbgState == DBG_RUN); + if (nDbgState != DBG_RUN) // emulation stopped + { + bDbgNotify = FALSE; // reset debugger notify flag + nDbgState = DBG_RUN; // state "run" + SetEvent(hEventDebug); // run emulation + } + return bErr; +} + +/**************************************************************************** +* EmuRunPC +***************************************************************************** +* +* @func run emulation until stop address +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuRunPC( + DWORD dwAddressPC) // @parm breakpoint address +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + dwDbgStopPC = dwAddressPC; // stop address + EmuRun(); // run emulation + } + return; +} + +/**************************************************************************** +* EmuStep +***************************************************************************** +* +* @func execute one ASM instruction and return to caller +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuStep(VOID) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + bDbgNotify = FALSE; // reset debugger notify flag + nDbgState = DBG_STEPINTO; // state "step into" + SetEvent(hEventDebug); // run emulation + } + return; +} + +/**************************************************************************** +* EmuStepOver +***************************************************************************** +* +* @func execute one ASM instruction but skip GOSUB, GOSUBL, GOSBVL +* subroutines +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuStepOver(VOID) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + LPBYTE I; + + bDbgNotify = FALSE; // reset debugger notify flag + + I = FASTPTR(Chipset.pc); + + dwDbgRstkp = Chipset.rstkp; // save stack level + + nDbgState = DBG_STEPINTO; // state "step into" + if (I[0] == 0x7) // GOSUB 7aaa + { + nDbgState = DBG_STEPOVER; // state "step over" + } + if (I[0] == 0x8) // GOSUBL or GOSBVL + { + if (I[1] == 0xE) // GOSUBL 8Eaaaa + { + nDbgState = DBG_STEPOVER; // state "step over" + } + if (I[1] == 0xF) // GOSBVL 8Eaaaaa + { + nDbgState = DBG_STEPOVER; // state "step over" + } + } + SetEvent(hEventDebug); // run emulation + } + return; +} + +/**************************************************************************** +* EmuStepOut +***************************************************************************** +* +* @func run emulation until a RTI, RTN, RTNC, RTNCC, RTNNC, RTNSC, RTNSXN, +* RTNYES instruction is found above the current stack level +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuStepOut(VOID) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + bDbgNotify = FALSE; // reset debugger notify flag + dwDbgRstkp = (Chipset.rstkp-1)&7; // save stack data + dwDbgRstk = Chipset.rstk[dwDbgRstkp]; + nDbgState = DBG_STEPOUT; // state "step out" + SetEvent(hEventDebug); // run emulation + } + return; +} + +/**************************************************************************** +* EmuStop +***************************************************************************** +* +* @func break emulation +* +* @xref none +* +* @rdesc BOOL: FALSE = OK +* TRUE = Error, no debug notify handler +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuStop(VOID) +{ + if (pEmuDbgNotify && nDbgState != DBG_STEPINTO) // emulation running + { + bDbgNotify = FALSE; // reset debugger notify flag + nDbgState = DBG_STEPINTO; // state "step into" + if (Chipset.Shutdn) // cpu thread stopped + SetEvent(hEventShutdn); // goto debug session + while (!bDbgNotify) Sleep(0); // wait until emulation stopped + } + return nDbgState != DBG_STEPINTO; +} + +/**************************************************************************** +* EmuCallBackDebugNotify +***************************************************************************** +* +* @func init CallBack handler to notify caller on debugger breakpoint +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuCallBackDebugNotify( + VOID (CALLBACK *EmuDbgNotify)(INT nBreaktype)) // @parm CallBack function notify Debug breakpoint +{ + if(EmuDbgNotify != NULL) // debugger enabled + { + dwDbgStopPC = -1; // no stop address for goto cursor + dwDbgRplPC = -1; // no stop address for RPL breakpoint + pEmuDbgNotify = EmuDbgNotify; // set new handler + nDbgState = DBG_RUN; // enable debugger + } + else + { + nDbgState = DBG_OFF; // disable debugger + pEmuDbgNotify = EmuDbgNotify; // remove handler + bInterrupt = TRUE; // exit opcode loop + SetEvent(hEventDebug); + UpdateWindowStatus(); // update menu settings + } + return; +} + +/**************************************************************************** +* EmuCallBackStackNotify +***************************************************************************** +* +* @func init CallBack handler to notify caller on hardware stack change; +* if the CallBack function return TRUE, emulation stops behind the +* opcode changed hardware stack content, otherwise, if the CallBack +* function return FALSE, no breakpoint is set +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuCallBackStackNotify( + BOOL (CALLBACK *EmuStackNotify)(VOID)) // @parm CallBack function notify stack changed +{ + dwRefRstkp = -1; // reference stack pointer not initialized + pEmuStackNotify = EmuStackNotify; // set new handler + return; +} + +/**************************************************************************** +* EmuGetRegister +***************************************************************************** +* +* @func read a 32 bit register +* +* @xref none +* +* @rdesc DWORD: 32 bit value of register +* +****************************************************************************/ + +DECLSPEC DWORD CALLBACK EmuGetRegister( + UINT uRegister) // @parm index of register +{ + DWORD dwResult; + + switch(uRegister) + { + case EMU_REGISTER_PC: + dwResult = Chipset.pc; + break; + case EMU_REGISTER_D0: + dwResult = Chipset.d0; + break; + case EMU_REGISTER_D1: + dwResult = Chipset.d1; + break; + case EMU_REGISTER_DUMMY: + case EMU_REGISTER_R5L: + case EMU_REGISTER_R5H: + case EMU_REGISTER_R6L: + case EMU_REGISTER_R6H: + case EMU_REGISTER_R7L: + case EMU_REGISTER_R7H: + dwResult = 0; // dummy return + break; + case EMU_REGISTER_AL: + dwResult = RegToDWORD(&Chipset.A[0],8); + break; + case EMU_REGISTER_AH: + dwResult = RegToDWORD(&Chipset.A[8],8); + break; + case EMU_REGISTER_BL: + dwResult = RegToDWORD(&Chipset.B[0],8); + break; + case EMU_REGISTER_BH: + dwResult = RegToDWORD(&Chipset.B[8],8); + break; + case EMU_REGISTER_CL: + dwResult = RegToDWORD(&Chipset.C[0],8); + break; + case EMU_REGISTER_CH: + dwResult = RegToDWORD(&Chipset.C[8],8); + break; + case EMU_REGISTER_DL: + dwResult = RegToDWORD(&Chipset.D[0],8); + break; + case EMU_REGISTER_DH: + dwResult = RegToDWORD(&Chipset.D[8],8); + break; + case EMU_REGISTER_R0L: + dwResult = RegToDWORD(&Chipset.R0[0],8); + break; + case EMU_REGISTER_R0H: + dwResult = RegToDWORD(&Chipset.R0[8],8); + break; + case EMU_REGISTER_R1L: + dwResult = RegToDWORD(&Chipset.R1[0],8); + break; + case EMU_REGISTER_R1H: + dwResult = RegToDWORD(&Chipset.R1[8],8); + break; + case EMU_REGISTER_R2L: + dwResult = RegToDWORD(&Chipset.R2[0],8); + break; + case EMU_REGISTER_R2H: + dwResult = RegToDWORD(&Chipset.R2[8],8); + break; + case EMU_REGISTER_R3L: + dwResult = RegToDWORD(&Chipset.R3[0],8); + break; + case EMU_REGISTER_R3H: + dwResult = RegToDWORD(&Chipset.R3[8],8); + break; + case EMU_REGISTER_R4L: + dwResult = RegToDWORD(&Chipset.R4[0],8); + break; + case EMU_REGISTER_R4H: + dwResult = RegToDWORD(&Chipset.R4[8],8); + break; + case EMU_REGISTER_FLAGS: + /** + * "FLAGS" register format : + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ST |S|x|x|x|K|I|C|M| HST | P | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * M : Mode (0:Hex, 1:Dec) + * C : Carry + * I : Interrupt pending + * K : KDN Interrupts Enabled + * S : Shutdn Flag (read only) + * x : reserved + */ + dwResult = RegToDWORD(Chipset.ST,4); + dwResult <<= 1; + dwResult |= Chipset.Shutdn ? 1 : 0; + dwResult <<= (3+1); + dwResult |= Chipset.intk ? 1 : 0; + dwResult <<= 1; + dwResult |= Chipset.inte ? 1 : 0; + dwResult <<= 1; + dwResult |= Chipset.carry ? 1 : 0; + dwResult <<= 1; + dwResult |= Chipset.mode_dec ? 1 : 0; + dwResult <<= 4; + dwResult |= Chipset.HST; + dwResult <<= 4; + dwResult |= Chipset.P; + break; + case EMU_REGISTER_OUT: + dwResult = Chipset.out; + break; + case EMU_REGISTER_IN: + dwResult = Chipset.in; + break; + case EMU_REGISTER_VIEW1: + dwResult = ((Chipset.Bank_FF >> 1) & 0x3F) >> 4; + break; + case EMU_REGISTER_VIEW2: + dwResult = ((Chipset.Bank_FF >> 1) & 0x3F) & 0xF; + break; + case EMU_REGISTER_RSTKP: + dwResult = Chipset.rstkp; + break; + case EMU_REGISTER_RSTK0: + dwResult = Chipset.rstk[0]; + break; + case EMU_REGISTER_RSTK1: + dwResult = Chipset.rstk[1]; + break; + case EMU_REGISTER_RSTK2: + dwResult = Chipset.rstk[2]; + break; + case EMU_REGISTER_RSTK3: + dwResult = Chipset.rstk[3]; + break; + case EMU_REGISTER_RSTK4: + dwResult = Chipset.rstk[4]; + break; + case EMU_REGISTER_RSTK5: + dwResult = Chipset.rstk[5]; + break; + case EMU_REGISTER_RSTK6: + dwResult = Chipset.rstk[6]; + break; + case EMU_REGISTER_RSTK7: + dwResult = Chipset.rstk[7]; + break; + case EMU_REGISTER_CLKL: + dwResult = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + break; + case EMU_REGISTER_CLKH: + dwResult = (DWORD) (Chipset.cycles >> 32); + break; + case EMU_REGISTER_CRC: + dwResult = Chipset.crc; + break; + default: + dwResult = 0; // default return + _ASSERT(FALSE); // illegal entry + } + return dwResult; +} + +/**************************************************************************** +* EmuSetRegister +***************************************************************************** +* +* @func write a 32 bit register +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuSetRegister( + UINT uRegister, // @parm index of register + DWORD dwValue) // @parm new 32 bit value +{ + switch(uRegister) + { + case EMU_REGISTER_PC: + Chipset.pc = dwValue; + break; + case EMU_REGISTER_D0: + Chipset.d0 = dwValue; + break; + case EMU_REGISTER_D1: + Chipset.d1 = dwValue; + break; + case EMU_REGISTER_DUMMY: + case EMU_REGISTER_R5L: + case EMU_REGISTER_R5H: + case EMU_REGISTER_R6L: + case EMU_REGISTER_R6H: + case EMU_REGISTER_R7L: + case EMU_REGISTER_R7H: + break; // dummy return + case EMU_REGISTER_AL: + DWORDToReg(&Chipset.A[0],8,dwValue); + break; + case EMU_REGISTER_AH: + DWORDToReg(&Chipset.A[8],8,dwValue); + break; + case EMU_REGISTER_BL: + DWORDToReg(&Chipset.B[0],8,dwValue); + break; + case EMU_REGISTER_BH: + DWORDToReg(&Chipset.B[8],8,dwValue); + break; + case EMU_REGISTER_CL: + DWORDToReg(&Chipset.C[0],8,dwValue); + break; + case EMU_REGISTER_CH: + DWORDToReg(&Chipset.C[8],8,dwValue); + break; + case EMU_REGISTER_DL: + DWORDToReg(&Chipset.D[0],8,dwValue); + break; + case EMU_REGISTER_DH: + DWORDToReg(&Chipset.D[8],8,dwValue); + break; + case EMU_REGISTER_R0L: + DWORDToReg(&Chipset.R0[0],8,dwValue); + break; + case EMU_REGISTER_R0H: + DWORDToReg(&Chipset.R0[8],8,dwValue); + break; + case EMU_REGISTER_R1L: + DWORDToReg(&Chipset.R1[0],8,dwValue); + break; + case EMU_REGISTER_R1H: + DWORDToReg(&Chipset.R1[8],8,dwValue); + break; + case EMU_REGISTER_R2L: + DWORDToReg(&Chipset.R2[0],8,dwValue); + break; + case EMU_REGISTER_R2H: + DWORDToReg(&Chipset.R2[8],8,dwValue); + break; + case EMU_REGISTER_R3L: + DWORDToReg(&Chipset.R3[0],8,dwValue); + break; + case EMU_REGISTER_R3H: + DWORDToReg(&Chipset.R3[8],8,dwValue); + break; + case EMU_REGISTER_R4L: + DWORDToReg(&Chipset.R4[0],8,dwValue); + break; + case EMU_REGISTER_R4H: + DWORDToReg(&Chipset.R4[8],8,dwValue); + break; + case EMU_REGISTER_FLAGS: + /** + * "FLAGS" register format : + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ST |S|x|x|x|K|I|C|M| HST | P | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * M : Mode (0:Hex, 1:Dec) + * C : Carry + * I : Interrupt pending + * K : KDN Interrupts Enabled + * S : Shutdn Flag (read only) + * x : reserved + */ + Chipset.P = (BYTE) (dwValue & 0xF); + dwValue >>= 4; + Chipset.HST = (BYTE) (dwValue & 0xF); + dwValue >>= 4; + Chipset.mode_dec = (dwValue & 0x1) ? TRUE : FALSE; + dwValue >>= 1; + Chipset.carry = (dwValue & 0x1) ? TRUE : FALSE; + dwValue >>= 1; + Chipset.inte = (dwValue & 0x1) ? TRUE : FALSE; + dwValue >>= 1; + Chipset.intk = (dwValue & 0x1) ? TRUE : FALSE; + dwValue >>= (1+4); + DWORDToReg(Chipset.ST,sizeof(Chipset.ST),dwValue); + PCHANGED; + break; + case EMU_REGISTER_OUT: + Chipset.out = (WORD) (dwValue & 0xFFFF); + break; + case EMU_REGISTER_IN: + Chipset.in = (WORD) (dwValue & 0xFFFF); + break; + case EMU_REGISTER_VIEW1: + Chipset.Bank_FF &= 0x1F; + Chipset.Bank_FF |= (dwValue & 0x03) << (4+1); + RomSwitch(Chipset.Bank_FF); + break; + case EMU_REGISTER_VIEW2: + Chipset.Bank_FF &= 0x61; + Chipset.Bank_FF |= (dwValue & 0x0F) << 1; + RomSwitch(Chipset.Bank_FF); + break; + case EMU_REGISTER_RSTKP: + Chipset.rstkp = dwValue; + break; + case EMU_REGISTER_RSTK0: + Chipset.rstk[0] = dwValue; + break; + case EMU_REGISTER_RSTK1: + Chipset.rstk[1] = dwValue; + break; + case EMU_REGISTER_RSTK2: + Chipset.rstk[2] = dwValue; + break; + case EMU_REGISTER_RSTK3: + Chipset.rstk[3] = dwValue; + break; + case EMU_REGISTER_RSTK4: + Chipset.rstk[4] = dwValue; + break; + case EMU_REGISTER_RSTK5: + Chipset.rstk[5] = dwValue; + break; + case EMU_REGISTER_RSTK6: + Chipset.rstk[6] = dwValue; + break; + case EMU_REGISTER_RSTK7: + Chipset.rstk[7] = dwValue; + break; + case EMU_REGISTER_CLKL: + case EMU_REGISTER_CLKH: + break; // not allowed to change + case EMU_REGISTER_CRC: + Chipset.crc = (WORD) (dwValue & 0xFFFF); + break; + default: + _ASSERT(FALSE); // illegal entry + } + return; +} + +/**************************************************************************** +* EmuGetMem +***************************************************************************** +* +* @func read one nibble from the specified mapped address +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuGetMem( + DWORD dwMapAddr, // @parm mapped address of Saturn CPU + BYTE *pbyValue) // @parm readed nibble +{ + Npeek(pbyValue,dwMapAddr,1); + return; +} + +/**************************************************************************** +* EmuSetMem +***************************************************************************** +* +* @func write one nibble to the specified mapped address +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuSetMem( + DWORD dwMapAddr, // @parm mapped address of Saturn CPU + BYTE byValue) // @parm nibble to write +{ + Nwrite(&byValue,dwMapAddr,1); + return; +} + +/**************************************************************************** +* EmuGetRom +***************************************************************************** +* +* @func return size and base address of mapped ROM +* +* @xref none +* +* @rdesc LPBYTE: base address of ROM (pointer to original data) +* +****************************************************************************/ + +DECLSPEC LPBYTE CALLBACK EmuGetRom( + DWORD *pdwRomSize) // @parm return size of ROM in nibbles +{ + *pdwRomSize = dwRomSize; + return pbyRom; +} + +/**************************************************************************** +* EmuSetBreakpoint +***************************************************************************** +* +* @func set ASM code/data breakpoint +* +* @xref none +* +* @rdesc BOOL: TRUE = Error, Breakpoint table full +* FALSE = OK, Breakpoint set +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuSetBreakpoint( + DWORD dwAddress, // @parm breakpoint address to set + UINT nBreakpointType) // @parm breakpoint type to set +{ + INT i; + + _ASSERT( nBreakpointType == BP_EXEC // illegal breakpoint type + || nBreakpointType == BP_READ + || nBreakpointType == BP_WRITE + || nBreakpointType == BP_RPL + || nBreakpointType == BP_ACCESS + || nBreakpointType == BP_ROM); + + for (i = 0; i < wBreakpointCount; ++i) // search for breakpoint + { + // breakpoint already set + if ( sBreakpoint[i].dwAddr == dwAddress + && sBreakpoint[i].nType == nBreakpointType) + return FALSE; + } + + if (wBreakpointCount >= MAXBREAKPOINTS) // breakpoint buffer full + return TRUE; + + sBreakpoint[wBreakpointCount].dwAddr = dwAddress; + sBreakpoint[wBreakpointCount].nType = nBreakpointType; + ++wBreakpointCount; + return FALSE; +} + +/**************************************************************************** +* EmuClearBreakpoint +***************************************************************************** +* +* @func clear ASM code/data breakpoint +* +* @xref none +* +* @rdesc BOOL: TRUE = Error, Breakpoint not found +* FALSE = OK, Breakpoint cleared +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuClearBreakpoint( + DWORD dwAddress, // @parm breakpoint address to clear + UINT nBreakpointType) // @parm breakpoint type to clear +{ + INT i; + + _ASSERT( nBreakpointType == BP_EXEC // illegal breakpoint type + || nBreakpointType == BP_READ + || nBreakpointType == BP_WRITE + || nBreakpointType == BP_RPL + || nBreakpointType == BP_ACCESS + || nBreakpointType == BP_ROM); + + for (i = 0; i < wBreakpointCount; ++i) // search for breakpoint + { + // breakpoint found + if ( sBreakpoint[i].dwAddr == dwAddress + && sBreakpoint[i].nType == nBreakpointType) + { + // move rest to top + for (++i; i < wBreakpointCount; ++i) + sBreakpoint[i-1] = sBreakpoint[i]; + + --wBreakpointCount; + return FALSE; // breakpoint found + } + } + return TRUE; // breakpoint not found +} + +/**************************************************************************** +* EmuClearAllBreakpoints +***************************************************************************** +* +* @func clear all breakpoints +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuClearAllBreakpoints(VOID) +{ + wBreakpointCount = 0; + return; +} + +/**************************************************************************** +* EmuEnableNop3Breakpoint +***************************************************************************** +* +* @func enable/disable NOP3 breakpoint +* stop emulation at Opcode 420 for GOC + (next line) +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuEnableNop3Breakpoint( + BOOL bEnable) // @parm stop on NOP3 opcode +{ + bDbgNOP3 = bEnable; // change stop on NOP3 breakpoint flag + return; +} + +/**************************************************************************** +* EmuEnableDocodeBreakpoint +***************************************************************************** +* +* @func enable/disable DOCODE breakpoint +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuEnableDoCodeBreakpoint( + BOOL bEnable) // @parm stop on DOCODE entry +{ + bDbgCode = bEnable; // change stop on DOCODE breakpoint flag + return; +} + +/**************************************************************************** +* EmuEnableRplBreakpoint +***************************************************************************** +* +* @func enable/disable RPL breakpoint +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuEnableRplBreakpoint( + BOOL bEnable) // @parm stop on RPL exit +{ + bDbgRPL = bEnable; // change stop on RPL exit flag + return; +} + +/**************************************************************************** +* EmuEnableSkipInterruptCode +***************************************************************************** +* +* @func enable/disable skip single step execution inside the interrupt +* handler, this option has no effect on code and data breakpoints +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuEnableSkipInterruptCode( + BOOL bEnable) // @parm TRUE = skip code instructions + // inside interrupt service routine +{ + bDbgSkipInt = bEnable; // change skip interrupt code flag + return; +} diff --git a/SOURCE/DEBUGGER.C b/SOURCE/DEBUGGER.C new file mode 100644 index 0000000..20e3bb5 --- /dev/null +++ b/SOURCE/DEBUGGER.C @@ -0,0 +1,2927 @@ +/* + * debugger.c + * + * This file is part of Emu48 + * + * Copyright (C) 1999 Christoph Gießelink + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "Opcodes.h" +#include "ops.h" +#include "color.h" +#include "debugger.h" + +#define MAXCODELINES 15 // number of lines in code window +#define MAXMEMLINES 6 // number of lines in memory window +#define MAXMEMITEMS 16 // number of address items in a memory window line +#define MAXBREAKPOINTS 256 // max. number of breakpoints + +#define REG_START IDC_REG_A // first register in register update table +#define REG_STOP IDC_MISC_BS // last register in register update table +#define REG_SIZE (REG_STOP-REG_START+1) // size of register update table + +// assert for register update +#define _ASSERTREG(r) _ASSERT(r >= REG_START && r <= REG_STOP) + +#define INSTRSIZE 256 // size of last instruction buffer + +#define WM_UPDATE (WM_USER+0x1000) // update debugger dialog box + +#define MEMWNDMAX (sizeof(nCol) / sizeof(nCol[0])) + +#define RT_TOOLBAR MAKEINTRESOURCE(241) // MFC toolbar resource type + +typedef struct CToolBarData +{ + WORD wVersion; + WORD wWidth; + WORD wHeight; + WORD wItemCount; + WORD aItems[1]; +} CToolBarData; + +typedef struct // type of breakpoint table +{ + BOOL bEnable; // breakpoint enabled + UINT nType; // breakpoint type + DWORD dwAddr; // breakpoint address +} BP_T; + +typedef struct // type of model memory mapping +{ + CONST BYTE byType; // calculator type + CONST LPBYTE *ppbyNCE1; // NCE1 data + CONST DWORD *pdwNCE1Size; // NCE1 size + CONST LPBYTE *ppbyNCE2; // NCE2 data + CONST DWORD *pdwNCE2Size; // NCE2 size + CONST LPBYTE *ppbyCE1; // CE1 data + CONST DWORD *pdwCE1Size; // CE1 size + CONST LPBYTE *ppbyCE2; // CE2 data + CONST DWORD *pdwCE2Size; // CE2 size + CONST LPBYTE *ppbyNCE3; // NCE3 data + CONST DWORD *pdwNCE3Size; // NCE3 size +} MODEL_MAP_T; + +static CONST int nCol[] = +{ + IDC_DEBUG_MEM_COL0, IDC_DEBUG_MEM_COL1, IDC_DEBUG_MEM_COL2, IDC_DEBUG_MEM_COL3, + IDC_DEBUG_MEM_COL4, IDC_DEBUG_MEM_COL5, IDC_DEBUG_MEM_COL6, IDC_DEBUG_MEM_COL7 +}; + +static CONST TCHAR cHex[] = { _T('0'),_T('1'),_T('2'),_T('3'), + _T('4'),_T('5'),_T('6'),_T('7'), + _T('8'),_T('9'),_T('A'),_T('B'), + _T('C'),_T('D'),_T('E'),_T('F') }; + +static CONST LPBYTE pbyNoMEM = NULL; // no memory module + +static CONST MODEL_MAP_T MemMap[] = +{ + { + 0, // default + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL // nc. + }, + { + '6', // HP38G (64K) + &pbyRom, &dwRomSize, // ROM + &Chipset.Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL // nc. + }, + { + 'A', // HP38G + &pbyRom, &dwRomSize, // ROM + &Chipset.Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL // nc. + }, + { + 'E', // HP39/40G + &pbyRom, &dwRomSize, // ROM + &Chipset.Port0, &Chipset.Port0Size, // RAM part 1 + &pbyNoMEM, NULL, // BS + &pbyNoMEM, NULL, // nc. + &Chipset.Port2, &Chipset.Port2Size // RAM part 2 + }, + { + 'G', // HP48GX + &pbyRom, &dwRomSize, // ROM + &Chipset.Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // BS + &Chipset.Port1, &Chipset.Port1Size, // Card slot 1 + &pbyPort2, &dwPort2Size // Card slot 2 + }, + { + 'S', // HP48SX + &pbyRom, &dwRomSize, // ROM + &Chipset.Port0, &Chipset.Port0Size, // RAM + &Chipset.Port1, &Chipset.Port1Size, // Card slot 1 + &pbyPort2, &dwPort2Size, // Card slot 2 + &pbyNoMEM, NULL // nc. + }, + { + 'X', // HP49G + &pbyRom, &dwRomSize, // Flash + &Chipset.Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // BS + &Chipset.Port1, &Chipset.Port1Size, // Port 1 part 1 + &Chipset.Port2, &Chipset.Port2Size // Port 1 part 2 + }, + { // CdB for HP: add Q type + 'Q', // HP49G+ + &pbyRom, &dwRomSize, // Flash + &Chipset.Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // BS + &Chipset.Port1, &Chipset.Port1Size, // Port 1 part 1 + &Chipset.Port2, &Chipset.Port2Size // Port 1 part 2 + }, + { // CdB for HP: add 2 type + '2', // HP48Gii + &pbyRom, &dwRomSize, // ROM + &Chipset.Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // BS + &pbyNoMEM, NULL, // Port 1 part 1 + &pbyNoMEM, NULL, // Port 1 part 2 + }, + { // CdB for HP: add P type + 'P', // HP39G+ + &pbyRom, &dwRomSize, // ROM + &Chipset.Port0, &Chipset.Port0Size, // RAM part 1 + &pbyNoMEM, NULL, // BS + &pbyNoMEM, NULL, // nc. + &Chipset.Port2, &Chipset.Port2Size // RAM part 2 + } +}; + +static INT nDbgPosX = 0; // position of debugger window +static INT nDbgPosY = 0; + +static WORD wBreakpointCount = 0; // number of breakpoints +static BP_T sBreakpoint[MAXBREAKPOINTS]; // breakpoint table + +static INT nRplBreak; // RPL breakpoint + +static DWORD dwAdrLine[MAXCODELINES]; // addresses of disassember lines in code window +static DWORD dwAdrMem = 0; // start address of memory window + +static UINT uIDFol = ID_DEBUG_MEM_FNONE; // follow mode +static DWORD dwAdrMemFol = 0; // follow address memory window + +static UINT uIDMap = ID_DEBUG_MEM_MAP; // current memory view mode +static LPBYTE lbyMapData; // data +static DWORD dwMapDataSize; // data size + +static LONG lCharWidth; // width of a character (is a fix font) + +static HMENU hMenuCode,hMenuMem,hMenuStack;// handle of context menues +static HWND hWndToolbar; // toolbar handle + +static MODEL_MAP_T CONST *pMapping; // model specific memory mapping + +static DWORD dwDbgRefCycles; // cpu cycles counter from last opcode + +static CHIPSET OldChipset; // old chipset content +static BOOL bRegUpdate[REG_SIZE]; // register update table + +static HBITMAP hBmpCheckBox; // checked and unchecked bitmap + +// function prototypes +static BOOL OnMemFind(HWND hDlg); +static BOOL OnProfile(HWND hDlg); +static INT_PTR OnNewValue(LPTSTR lpszValue); +static VOID OnEnterAddress(HWND hDlg, DWORD *dwValue); +static BOOL OnEditBreakpoint(HWND hDlg); +static BOOL OnInfoIntr(HWND hDlg); +static BOOL OnInfoWoRegister(HWND hDlg); + +static VOID UpdateProfileWnd(HWND hDlg); + +//################ +//# +//# Low level subroutines +//# +//################ + +// +// disable menu keys +// +static VOID DisableMenuKeys(HWND hDlg) +{ + HMENU hMenu = GetMenu(hDlg); + + EnableMenuItem(hMenu,ID_DEBUG_RUN,MF_GRAYED); + EnableMenuItem(hMenu,ID_DEBUG_RUNCURSOR,MF_GRAYED); + EnableMenuItem(hMenu,ID_DEBUG_STEP,MF_GRAYED); + EnableMenuItem(hMenu,ID_DEBUG_STEPOVER,MF_GRAYED); + EnableMenuItem(hMenu,ID_DEBUG_STEPOUT,MF_GRAYED); + EnableMenuItem(hMenu,ID_INFO_LASTINSTRUCTIONS,MF_GRAYED); + EnableMenuItem(hMenu,ID_INFO_WRITEONLYREG,MF_GRAYED); + + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_RUN,MAKELONG((FALSE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_BREAK,MAKELONG((TRUE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_RUNCURSOR,MAKELONG((FALSE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_STEP,MAKELONG((FALSE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_STEPOVER,MAKELONG((FALSE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_STEPOUT,MAKELONG((FALSE),0)); + return; +} + +// +// set mapping menu +// +static VOID SetMappingMenu(HWND hDlg,UINT uID) +{ + LPTSTR szCaption; + + CheckMenuItem(hMenuMem,uIDMap,MF_UNCHECKED); + + switch (uID) + { + case ID_DEBUG_MEM_MAP: + szCaption = _T("Memory"); + lbyMapData = NULL; // data + dwMapDataSize = 512; // data size + break; + case ID_DEBUG_MEM_NCE1: + szCaption = _T("Memory (NCE1)"); + lbyMapData = *pMapping->ppbyNCE1; + dwMapDataSize = *pMapping->pdwNCE1Size / 2048; // ROM size is always in nibbles + break; + case ID_DEBUG_MEM_NCE2: + szCaption = _T("Memory (NCE2)"); + lbyMapData = *pMapping->ppbyNCE2; + dwMapDataSize = *pMapping->pdwNCE2Size; + break; + case ID_DEBUG_MEM_CE1: + szCaption = _T("Memory (CE1)"); + lbyMapData = *pMapping->ppbyCE1; + dwMapDataSize = *pMapping->pdwCE1Size; + break; + case ID_DEBUG_MEM_CE2: + szCaption = _T("Memory (CE2)"); + lbyMapData = *pMapping->ppbyCE2; + dwMapDataSize = *pMapping->pdwCE2Size; + break; + case ID_DEBUG_MEM_NCE3: + szCaption = _T("Memory (NCE3)"); + lbyMapData = *pMapping->ppbyNCE3; + dwMapDataSize = *pMapping->pdwNCE3Size; + break; + default: _ASSERT(0); + } + + dwMapDataSize *= 2048; // size in nibble + + if (uIDMap != uID) dwAdrMem = 0; // view from address 0 + + uIDMap = uID; + CheckMenuItem(hMenuMem,uIDMap,MF_CHECKED); + + SetDlgItemText(hDlg,IDC_STATIC_MEMORY,szCaption); + return; +}; + +// +// set/reset breakpoint +// +static __inline VOID ToggleBreakpoint(DWORD dwAddr) +{ + INT i; + + for (i = 0; i < wBreakpointCount; ++i) // scan all breakpoints + { + // code breakpoint found + if (sBreakpoint[i].nType == BP_EXEC && sBreakpoint[i].dwAddr == dwAddr) + { + if (!sBreakpoint[i].bEnable) // breakpoint disabled + { + sBreakpoint[i].bEnable = TRUE; + return; + } + + // purge breakpoint + for (++i; i < wBreakpointCount; ++i) + sBreakpoint[i-1] = sBreakpoint[i]; + --wBreakpointCount; + return; + } + } + + // breakpoint not found + if (wBreakpointCount >= MAXBREAKPOINTS) // breakpoint buffer full + { + AbortMessage(_T("Reached maximum number of breakpoints !")); + return; + } + + sBreakpoint[wBreakpointCount].bEnable = TRUE; + sBreakpoint[wBreakpointCount].nType = BP_EXEC; + sBreakpoint[wBreakpointCount].dwAddr = dwAddr; + ++wBreakpointCount; + return; +} + +// +// init memory mapping table and mapping menu entry +// +static __inline VOID InitMemMap(HWND hDlg) +{ + BOOL bActive; + INT i; + + pMapping = MemMap; // init default mapping + + // scan for all table entries + for (i = 0; i < ARRAYSIZEOF(MemMap); ++i) + { + if (MemMap[i].byType == cCurrentRomType) + { + pMapping = &MemMap[i]; // found entry + break; + } + } + + _ASSERT(hMenuMem); + + // enable menu mappings + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_NCE1,(*pMapping->ppbyNCE1) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_NCE2,(*pMapping->ppbyNCE2) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_CE1, (*pMapping->ppbyCE1) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_CE2, (*pMapping->ppbyCE2) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_NCE3,(*pMapping->ppbyNCE3) ? MF_ENABLED : MF_GRAYED); + + bActive = (ID_DEBUG_MEM_NCE1 == uIDMap && *pMapping->ppbyNCE1 != NULL) + || (ID_DEBUG_MEM_NCE2 == uIDMap && *pMapping->ppbyNCE2 != NULL) + || (ID_DEBUG_MEM_CE1 == uIDMap && *pMapping->ppbyCE1 != NULL) + || (ID_DEBUG_MEM_CE2 == uIDMap && *pMapping->ppbyCE2 != NULL) + || (ID_DEBUG_MEM_NCE3 == uIDMap && *pMapping->ppbyNCE3 != NULL); + + SetMappingMenu(hDlg,bActive ? uIDMap : ID_DEBUG_MEM_MAP); + return; +} + +// +// init bank switcher area +// +static __inline VOID InitBsArea(HWND hDlg) +{ + // not 38 / 48S // CdB for HP: add apples type + if (cCurrentRomType!='A' && cCurrentRomType!='S') + { + EnableWindow(GetDlgItem(hDlg,IDC_MISC_BS_TXT),TRUE); + EnableWindow(GetDlgItem(hDlg,IDC_MISC_BS),TRUE); + } + return; +} + +// +// convert nibble register to string +// +static LPTSTR RegToStr(BYTE *pReg, WORD wNib) +{ + static TCHAR szBuffer[32]; + + WORD i; + + for (i = 0;i < wNib;++i) + szBuffer[i] = cHex[pReg[wNib-i-1]]; + szBuffer[i] = 0; + + return szBuffer; +} + +// +// convert string to nibble register +// +static VOID StrToReg(BYTE *pReg, WORD wNib, LPTSTR lpszValue) +{ + int i,nValuelen; + + nValuelen = lstrlen(lpszValue); + for (i = wNib - 1;i >= 0;--i) + { + if (i >= nValuelen) // no character in string + { + pReg[i] = 0; // fill with zero + } + else + { + // convert to number + pReg[i] = _totupper(*lpszValue) - _T('0'); + if (pReg[i] > 9) pReg[i] -= _T('A') - _T('9') - 1; + ++lpszValue; + } + } + return; +} + +// +// write code window +// +static VOID ViewCodeWnd(HWND hWnd, DWORD dwAddress) +{ + INT i,j; + TCHAR szAddress[64]; + + _ASSERT(disassembler_map == MEM_MAP); // disassemble in mapped mode + SendMessage(hWnd,WM_SETREDRAW,FALSE,0); + SendMessage(hWnd,LB_RESETCONTENT,0,0); + for (i = 0; i < MAXCODELINES; ++i) + { + dwAdrLine[i] = dwAddress; + j = wsprintf(szAddress, + (dwAddress == Chipset.pc) ? _T("%05lX-%c ") : _T("%05lX "), + dwAddress,nRplBreak ? _T('R') : _T('>')); + dwAddress = disassemble(dwAddress,&szAddress[j],VIEW_SHORT); + SendMessage(hWnd,LB_ADDSTRING,0,(LPARAM) szAddress); + } + SendMessage(hWnd,WM_SETREDRAW,TRUE,0); + return; +} + +// +// write memory window +// +static VOID ViewMemWnd(HWND hDlg, DWORD dwAddress) +{ + #define TEXTOFF 32 + + INT i,j,k; + TCHAR szBuffer[16],szItem[4]; + BYTE cChar; + + szItem[2] = 0; // end of string + dwAdrMem = dwAddress; // save start address of memory window + + // purge all list boxes + SendDlgItemMessage(hDlg,IDC_DEBUG_MEM_ADDR,LB_RESETCONTENT,0,0); + SendDlgItemMessage(hDlg,IDC_DEBUG_MEM_TEXT,LB_RESETCONTENT,0,0); + for (j = 0; j < MEMWNDMAX; ++j) + SendDlgItemMessage(hDlg,nCol[j],LB_RESETCONTENT,0,0); + + for (i = 0; i < MAXMEMLINES; ++i) + { + BYTE byLineData[MAXMEMITEMS]; + + if (ID_DEBUG_MEM_MAP == uIDMap) // mapped memory content + { + // fetch mapping data line + Npeek(byLineData, dwAddress, MAXMEMITEMS); + + wsprintf(szBuffer,_T("%05lX"),dwAddress); + } + else // module memory content + { + INT i; + + _ASSERT(lbyMapData); // valid module + + // fetch modul data line + for (i = 0; i < MAXMEMITEMS; ++i) + { + byLineData[i] = lbyMapData[(dwAddress + i) & (dwMapDataSize - 1)]; + } + + wsprintf(szBuffer,_T("%06lX"),dwAddress); + } + SendDlgItemMessage(hDlg,IDC_DEBUG_MEM_ADDR,LB_ADDSTRING,0,(LPARAM) szBuffer); + + for (k = 0, j = 0; j < MAXMEMITEMS; ++j) + { + // read from fetched data line + szItem[j&0x1] = cHex[byLineData[j]]; + // characters are saved in LBS, MSB order + cChar = (cChar >> 4) | (byLineData[j] << 4); + + if ((j&0x1) != 0) + { + // byte field + _ASSERT(j/2 < MEMWNDMAX); + SendDlgItemMessage(hDlg,nCol[j/2],LB_ADDSTRING,0,(LPARAM) szItem); + + // text field + szBuffer[j/2] = (isprint(cChar) != 0) ? cChar : _T('.'); + } + } + szBuffer[j/2] = 0; // end of text string + SendDlgItemMessage(hDlg,IDC_DEBUG_MEM_TEXT,LB_ADDSTRING,0,(LPARAM) szBuffer); + + dwAddress = (dwAddress + MAXMEMITEMS) & (dwMapDataSize - 1); + } + return; + #undef TEXTOFF +} + + +//################ +//# +//# High level Window draw routines +//# +//################ + +// +// update code window with scrolling +// +static VOID UpdateCodeWnd(HWND hDlg) +{ + DWORD dwAddress; + INT i,j; + + HWND hWnd = GetDlgItem(hDlg,IDC_DEBUG_CODE); + + j = (INT) SendMessage(hWnd,LB_GETCOUNT,0,0); // no. of items in table + + // seach for actual address in code area + for (i = 0; i < j; ++i) + { + if (dwAdrLine[i] == Chipset.pc) // found new pc address line + break; + } + + // redraw code window + dwAddress = dwAdrLine[0]; // redraw list box with modified pc + if (i == j) // address not found + { + dwAddress = Chipset.pc; // begin with actual pc + i = 0; // set cursor on top + } + if (i > 10) // cursor near bottom line + { + dwAddress = dwAdrLine[i-10]; // move that pc is in line 11 + i = 10; // set cursor to actual pc + } + ViewCodeWnd(hWnd,dwAddress); // init code area + SendMessage(hWnd,LB_SETCURSEL,i,0); // set + return; +} + +// +// update register window +// +static VOID UpdateRegisterWnd(HWND hDlg) +{ + TCHAR szBuffer[64]; + + _ASSERTREG(IDC_REG_A); + bRegUpdate[IDC_REG_A-REG_START] = memcmp(Chipset.A, OldChipset.A, sizeof(Chipset.A)) != 0; + wsprintf(szBuffer,_T("A= %s"),RegToStr(Chipset.A,16)); + SetDlgItemText(hDlg,IDC_REG_A,szBuffer); + _ASSERTREG(IDC_REG_B); + bRegUpdate[IDC_REG_B-REG_START] = memcmp(Chipset.B, OldChipset.B, sizeof(Chipset.B)) != 0; + wsprintf(szBuffer,_T("B= %s"),RegToStr(Chipset.B,16)); + SetDlgItemText(hDlg,IDC_REG_B,szBuffer); + _ASSERTREG(IDC_REG_C); + bRegUpdate[IDC_REG_C-REG_START] = memcmp(Chipset.C, OldChipset.C, sizeof(Chipset.C)) != 0; + wsprintf(szBuffer,_T("C= %s"),RegToStr(Chipset.C,16)); + SetDlgItemText(hDlg,IDC_REG_C,szBuffer); + _ASSERTREG(IDC_REG_D); + bRegUpdate[IDC_REG_D-REG_START] = memcmp(Chipset.D, OldChipset.D, sizeof(Chipset.D)) != 0; + wsprintf(szBuffer,_T("D= %s"),RegToStr(Chipset.D,16)); + SetDlgItemText(hDlg,IDC_REG_D,szBuffer); + _ASSERTREG(IDC_REG_R0); + bRegUpdate[IDC_REG_R0-REG_START] = memcmp(Chipset.R0, OldChipset.R0, sizeof(Chipset.R0)) != 0; + wsprintf(szBuffer,_T("R0=%s"),RegToStr(Chipset.R0,16)); + SetDlgItemText(hDlg,IDC_REG_R0,szBuffer); + _ASSERTREG(IDC_REG_R1); + bRegUpdate[IDC_REG_R1-REG_START] = memcmp(Chipset.R1, OldChipset.R1, sizeof(Chipset.R1)) != 0; + wsprintf(szBuffer,_T("R1=%s"),RegToStr(Chipset.R1,16)); + SetDlgItemText(hDlg,IDC_REG_R1,szBuffer); + _ASSERTREG(IDC_REG_R2); + bRegUpdate[IDC_REG_R2-REG_START] = memcmp(Chipset.R2, OldChipset.R2, sizeof(Chipset.R2)) != 0; + wsprintf(szBuffer,_T("R2=%s"),RegToStr(Chipset.R2,16)); + SetDlgItemText(hDlg,IDC_REG_R2,szBuffer); + _ASSERTREG(IDC_REG_R3); + bRegUpdate[IDC_REG_R3-REG_START] = memcmp(Chipset.R3, OldChipset.R3, sizeof(Chipset.R3)) != 0; + wsprintf(szBuffer,_T("R3=%s"),RegToStr(Chipset.R3,16)); + SetDlgItemText(hDlg,IDC_REG_R3,szBuffer); + _ASSERTREG(IDC_REG_R4); + bRegUpdate[IDC_REG_R4-REG_START] = memcmp(Chipset.R4, OldChipset.R4, sizeof(Chipset.R4)) != 0; + wsprintf(szBuffer,_T("R4=%s"),RegToStr(Chipset.R4,16)); + SetDlgItemText(hDlg,IDC_REG_R4,szBuffer); + _ASSERTREG(IDC_REG_D0); + bRegUpdate[IDC_REG_D0-REG_START] = Chipset.d0 != OldChipset.d0; + wsprintf(szBuffer,_T("D0=%05X"),Chipset.d0); + SetDlgItemText(hDlg,IDC_REG_D0,szBuffer); + _ASSERTREG(IDC_REG_D1); + bRegUpdate[IDC_REG_D1-REG_START] = Chipset.d1 != OldChipset.d1; + wsprintf(szBuffer,_T("D1=%05X"),Chipset.d1); + SetDlgItemText(hDlg,IDC_REG_D1,szBuffer); + _ASSERTREG(IDC_REG_P); + bRegUpdate[IDC_REG_P-REG_START] = Chipset.P != OldChipset.P; + wsprintf(szBuffer,_T("P=%X"),Chipset.P); + SetDlgItemText(hDlg,IDC_REG_P,szBuffer); + _ASSERTREG(IDC_REG_PC); + bRegUpdate[IDC_REG_PC-REG_START] = Chipset.pc != OldChipset.pc; + wsprintf(szBuffer,_T("PC=%05X"),Chipset.pc); + SetDlgItemText(hDlg,IDC_REG_PC,szBuffer); + _ASSERTREG(IDC_REG_OUT); + bRegUpdate[IDC_REG_OUT-REG_START] = Chipset.out != OldChipset.out; + wsprintf(szBuffer,_T("OUT=%03X"),Chipset.out); + SetDlgItemText(hDlg,IDC_REG_OUT,szBuffer); + _ASSERTREG(IDC_REG_IN); + bRegUpdate[IDC_REG_IN-REG_START] = Chipset.in != OldChipset.in; + wsprintf(szBuffer,_T("IN=%04X"),Chipset.in); + SetDlgItemText(hDlg,IDC_REG_IN,szBuffer); + _ASSERTREG(IDC_REG_ST); + bRegUpdate[IDC_REG_ST-REG_START] = memcmp(Chipset.ST, OldChipset.ST, sizeof(Chipset.ST)) != 0; + wsprintf(szBuffer,_T("ST=%s"),RegToStr(Chipset.ST,4)); + SetDlgItemText(hDlg,IDC_REG_ST,szBuffer); + _ASSERTREG(IDC_REG_CY); + bRegUpdate[IDC_REG_CY-REG_START] = Chipset.carry != OldChipset.carry; + wsprintf(szBuffer,_T("CY=%d"),Chipset.carry); + SetDlgItemText(hDlg,IDC_REG_CY,szBuffer); + _ASSERTREG(IDC_REG_MODE); + bRegUpdate[IDC_REG_MODE-REG_START] = Chipset.mode_dec != OldChipset.mode_dec; + wsprintf(szBuffer,_T("Mode=%c"),Chipset.mode_dec ? _T('D') : _T('H')); + SetDlgItemText(hDlg,IDC_REG_MODE,szBuffer); + _ASSERTREG(IDC_REG_MP); + bRegUpdate[IDC_REG_MP-REG_START] = ((Chipset.HST ^ OldChipset.HST) & MP) != 0; + wsprintf(szBuffer,_T("MP=%d"),(Chipset.HST & MP) != 0); + SetDlgItemText(hDlg,IDC_REG_MP,szBuffer); + _ASSERTREG(IDC_REG_SR); + bRegUpdate[IDC_REG_SR-REG_START] = ((Chipset.HST ^ OldChipset.HST) & SR) != 0; + wsprintf(szBuffer,_T("SR=%d"),(Chipset.HST & SR) != 0); + SetDlgItemText(hDlg,IDC_REG_SR,szBuffer); + _ASSERTREG(IDC_REG_SB); + bRegUpdate[IDC_REG_SB-REG_START] = ((Chipset.HST ^ OldChipset.HST) & SB) != 0; + wsprintf(szBuffer,_T("SB=%d"),(Chipset.HST & SB) != 0); + SetDlgItemText(hDlg,IDC_REG_SB,szBuffer); + _ASSERTREG(IDC_REG_XM); + bRegUpdate[IDC_REG_XM-REG_START] = ((Chipset.HST ^ OldChipset.HST) & XM) != 0; + wsprintf(szBuffer,_T("XM=%d"),(Chipset.HST & XM) != 0); + SetDlgItemText(hDlg,IDC_REG_XM,szBuffer); + return; +} + +// +// update memory window +// +static VOID UpdateMemoryWnd(HWND hDlg) +{ + // check follow mode setting for memory window + switch(uIDFol) + { + case ID_DEBUG_MEM_FNONE: break; + case ID_DEBUG_MEM_FADDR: dwAdrMem = Read5(dwAdrMemFol); break; + case ID_DEBUG_MEM_FPC: dwAdrMem = Chipset.pc; break; + case ID_DEBUG_MEM_FD0: dwAdrMem = Chipset.d0; break; + case ID_DEBUG_MEM_FD1: dwAdrMem = Chipset.d1; break; + default: _ASSERT(FALSE); + } + + ViewMemWnd(hDlg,dwAdrMem); + return; +} + +// +// update stack window +// +static VOID UpdateStackWnd(HWND hDlg) +{ + INT i; + LONG nPos; + TCHAR szBuffer[64]; + + HWND hWnd = GetDlgItem(hDlg,IDC_DEBUG_STACK); + + SendMessage(hWnd,WM_SETREDRAW,FALSE,0); + nPos = (LONG) SendMessage(hWnd,LB_GETTOPINDEX,0,0); + SendMessage(hWnd,LB_RESETCONTENT,0,0); + for (i = 1; i <= ARRAYSIZEOF(Chipset.rstk); ++i) + { + INT j; + + wsprintf(szBuffer,_T("%d: %05X"), i, Chipset.rstk[(Chipset.rstkp-i)&7]); + j = (INT) SendMessage(hWnd,LB_ADDSTRING,0,(LPARAM) szBuffer); + SendMessage(hWnd,LB_SETITEMDATA,j,Chipset.rstk[(Chipset.rstkp-i)&7]); + } + SendMessage(hWnd,LB_SETTOPINDEX,nPos,0); + SendMessage(hWnd,WM_SETREDRAW,TRUE,0); + return; +} + +// +// update MMU window +// +static VOID UpdateMmuWnd(HWND hDlg) +{ + TCHAR szBuffer[64]; + + if (Chipset.IOCfig) + wsprintf(szBuffer,_T("%05X"),Chipset.IOBase); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,IDC_MMU_IO_A,szBuffer); + if (Chipset.P0Cfig) + wsprintf(szBuffer,_T("%05X"),Chipset.P0Base<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,IDC_MMU_NCE2_A,szBuffer); + if (Chipset.P0Cfg2) + wsprintf(szBuffer,_T("%05X"),(Chipset.P0Size^0xFF)<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,IDC_MMU_NCE2_S,szBuffer); + if (Chipset.P1Cfig) + wsprintf(szBuffer,_T("%05X"),Chipset.P1Base<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,(cCurrentRomType=='S') ? IDC_MMU_CE1_A : IDC_MMU_CE2_A,szBuffer); + if (Chipset.P1Cfg2) + wsprintf(szBuffer,_T("%05X"),(Chipset.P1Size^0xFF)<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,(cCurrentRomType=='S') ? IDC_MMU_CE1_S : IDC_MMU_CE2_S,szBuffer); + if (Chipset.P2Cfig) + wsprintf(szBuffer,_T("%05X"),Chipset.P2Base<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,(cCurrentRomType=='S') ? IDC_MMU_CE2_A : IDC_MMU_NCE3_A,szBuffer); + if (Chipset.P2Cfg2) + wsprintf(szBuffer,_T("%05X"),(Chipset.P2Size^0xFF)<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,(cCurrentRomType=='S') ? IDC_MMU_CE2_S : IDC_MMU_NCE3_S,szBuffer); + if (Chipset.BSCfig) + wsprintf(szBuffer,_T("%05X"),Chipset.BSBase<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,(cCurrentRomType=='S') ? IDC_MMU_NCE3_A : IDC_MMU_CE1_A,szBuffer); + if (Chipset.BSCfg2) + wsprintf(szBuffer,_T("%05X"),(Chipset.BSSize^0xFF)<<12); + else + lstrcpy(szBuffer,_T("-----")); + SetDlgItemText(hDlg,(cCurrentRomType=='S') ? IDC_MMU_NCE3_S : IDC_MMU_CE1_S,szBuffer); + return; +} + +// +// update miscellaneous window +// +static VOID UpdateMiscWnd(HWND hDlg) +{ + _ASSERTREG(IDC_MISC_INT); + bRegUpdate[IDC_MISC_INT-REG_START] = Chipset.inte != OldChipset.inte; + SetDlgItemText(hDlg,IDC_MISC_INT,Chipset.inte ? _T("On ") : _T("Off")); + + _ASSERTREG(IDC_MISC_KEY); + bRegUpdate[IDC_MISC_KEY-REG_START] = Chipset.intk != OldChipset.intk; + SetDlgItemText(hDlg,IDC_MISC_KEY,Chipset.intk ? _T("On ") : _T("Off")); + + _ASSERTREG(IDC_MISC_BS); + bRegUpdate[IDC_MISC_BS-REG_START] = FALSE; + + // not 38/48S // CdB for HP: add Apples type + if (cCurrentRomType!='A' && cCurrentRomType!='S') + { + TCHAR szBuffer[64]; + + bRegUpdate[IDC_MISC_BS-REG_START] = (Chipset.Bank_FF & 0x7F) != (OldChipset.Bank_FF & 0x7F); + wsprintf(szBuffer,_T("%02X"),Chipset.Bank_FF & 0x7F); + SetDlgItemText(hDlg,IDC_MISC_BS,szBuffer); + } + return; +} + +// +// update complete debugger dialog +// +VOID OnUpdate(HWND hDlg) +{ + nDbgState = DBG_STEPINTO; // state "step into" + dwDbgStopPC = -1; // disable "cursor stop address" + + // enable debug buttons + EnableMenuItem(GetMenu(hDlg),ID_DEBUG_RUN,MF_ENABLED); + EnableMenuItem(GetMenu(hDlg),ID_DEBUG_RUNCURSOR,MF_ENABLED); + EnableMenuItem(GetMenu(hDlg),ID_DEBUG_STEP,MF_ENABLED); + EnableMenuItem(GetMenu(hDlg),ID_DEBUG_STEPOVER,MF_ENABLED); + EnableMenuItem(GetMenu(hDlg),ID_DEBUG_STEPOUT,MF_ENABLED); + EnableMenuItem(GetMenu(hDlg),ID_INFO_LASTINSTRUCTIONS,MF_ENABLED); + EnableMenuItem(GetMenu(hDlg),ID_INFO_WRITEONLYREG,MF_ENABLED); + + // enable toolbar buttons + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_RUN,MAKELONG((TRUE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_BREAK,MAKELONG((FALSE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_RUNCURSOR,MAKELONG((TRUE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_STEP,MAKELONG((TRUE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_STEPOVER,MAKELONG((TRUE),0)); + SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_STEPOUT,MAKELONG((TRUE),0)); + + // update windows + UpdateCodeWnd(hDlg); // update code window + UpdateRegisterWnd(hDlg); // update registers window + UpdateMemoryWnd(hDlg); // update memory window + UpdateStackWnd(hDlg); // update stack window + UpdateMmuWnd(hDlg); // update MMU window + UpdateMiscWnd(hDlg); // update bank switcher window + UpdateProfileWnd(hDlgProfile); // update profiler dialog + ShowWindow(hDlg,SW_RESTORE); // pop up if minimized + SetFocus(hDlg); // set focus to debugger + return; +} + + +//################ +//# +//# Virtual key handler +//# +//################ + +// +// toggle breakpoint key handler (F2) +// +static BOOL OnKeyF2(HWND hDlg) +{ + HWND hWnd; + RECT rc; + LONG i; + + hWnd = GetDlgItem(hDlg,IDC_DEBUG_CODE); + i = (LONG) SendMessage(hWnd,LB_GETCURSEL,0,0); // get selected item + ToggleBreakpoint(dwAdrLine[i]); // toggle breakpoint at address + // update region of toggled item + SendMessage(hWnd,LB_GETITEMRECT,i,(LPARAM)&rc); + InvalidateRect(hWnd,&rc,TRUE); + return -1; // call windows default handler +} + +// +// run key handler (F5) +// +static BOOL OnKeyF5(HWND hDlg) +{ + HWND hWnd; + INT i,nPos; + TCHAR szBuf[64]; + + if (nDbgState != DBG_RUN) // emulation stopped + { + DisableMenuKeys(hDlg); // disable menu keys + + hWnd = GetDlgItem(hDlg,IDC_DEBUG_CODE); + nPos = (INT) SendMessage(hWnd,LB_GETCURSEL,0,0); + + // clear "->" in code window + for (i = 0; i < MAXCODELINES; ++i) + { + SendMessage(hWnd,LB_GETTEXT,i,(LPARAM) szBuf); + if (szBuf[5] != _T(' ')) // PC in window + { + szBuf[5] = szBuf[6] = _T(' '); + SendMessage(hWnd,LB_DELETESTRING,i,0); + SendMessage(hWnd,LB_INSERTSTRING,i,(LPARAM) szBuf); + break; + } + } + SendMessage(hWnd,LB_SETCURSEL,nPos,0); + + nDbgState = DBG_RUN; // state "run" + OldChipset = Chipset; // save chipset values + SetEvent(hEventDebug); // run emulation + } + return -1; // call windows default handler + UNREFERENCED_PARAMETER(hDlg); +} + +// +// step cursor key handler (F6) +// +static BOOL OnKeyF6(HWND hDlg) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + // get address of selected item + INT nPos = (INT) SendDlgItemMessage(hDlg,IDC_DEBUG_CODE,LB_GETCURSEL,0,0); + dwDbgStopPC = dwAdrLine[nPos]; + + OnKeyF5(hDlg); // run emulation + } + return -1; // call windows default handler +} + +// +// step into key handler (F7) +// +static BOOL OnKeyF7(HWND hDlg) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + if (bDbgSkipInt) // skip code in interrupt handler + DisableMenuKeys(hDlg); // disable menu keys + + nDbgState = DBG_STEPINTO; // state "step into" + OldChipset = Chipset; // save chipset values + SetEvent(hEventDebug); // run emulation + } + return -1; // call windows default handler + UNREFERENCED_PARAMETER(hDlg); +} + +// +// step over key handler (F8) +// +static BOOL OnKeyF8(HWND hDlg) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + LPBYTE I = FASTPTR(Chipset.pc); + + if (bDbgSkipInt) // skip code in interrupt handler + DisableMenuKeys(hDlg); // disable menu keys + + dwDbgRstkp = Chipset.rstkp; // save stack level + + // GOSUB 7aaa, GOSUBL 8Eaaaa, GOSBVL 8Faaaaa + if (I[0] == 0x7 || (I[0] == 0x8 && (I[1] == 0xE || I[1] == 0xF))) + { + nDbgState = DBG_STEPOVER; // state "step over" + } + else + { + nDbgState = DBG_STEPINTO; // state "step into" + } + OldChipset = Chipset; // save chipset values + SetEvent(hEventDebug); // run emulation + } + return -1; // call windows default handler + UNREFERENCED_PARAMETER(hDlg); +} + +// +// step out key handler (F9) +// +static BOOL OnKeyF9(HWND hDlg) +{ + if (nDbgState != DBG_RUN) // emulation stopped + { + DisableMenuKeys(hDlg); // disable menu keys + dwDbgRstkp = (Chipset.rstkp-1)&7; // save stack data + dwDbgRstk = Chipset.rstk[dwDbgRstkp]; + nDbgState = DBG_STEPOUT; // state "step out" + OldChipset = Chipset; // save chipset values + SetEvent(hEventDebug); // run emulation + } + return -1; // call windows default handler + UNREFERENCED_PARAMETER(hDlg); +} + +// +// break key handler (F11) +// +static BOOL OnKeyF11(HWND hDlg) +{ + nDbgState = DBG_STEPINTO; // state "step into" + if (Chipset.Shutdn) // cpu thread stopped + SetEvent(hEventShutdn); // goto debug session + return -1; // call windows default handler + UNREFERENCED_PARAMETER(hDlg); +} + +// +// view of given address in disassembler window +// +static BOOL OnCodeGoAdr(HWND hDlg) +{ + DWORD dwAddress = -1; // no address given + + OnEnterAddress(hDlg, &dwAddress); + if (dwAddress != -1) + { + HWND hWnd = GetDlgItem(hDlg,IDC_DEBUG_CODE); + ViewCodeWnd(hWnd,dwAddress & 0xFFFFF); + SendMessage(hWnd,LB_SETCURSEL,0,0); + } + return -1; // call windows default handler +} + +// +// view pc in disassembler window +// +static BOOL OnCodeGoPC(HWND hDlg) +{ + UpdateCodeWnd(hDlg); + return 0; +} + +// +// set pc to selection +// +static BOOL OnCodeSetPcToSelection(HWND hDlg) +{ + Chipset.pc = dwAdrLine[SendDlgItemMessage(hDlg,IDC_DEBUG_CODE,LB_GETCURSEL,0,0)]; + return OnCodeGoPC(hDlg); +} + +// +// view from address in memory window +// +static BOOL OnMemGoDx(HWND hDlg, DWORD dwAddress) +{ + HWND hWnd = GetDlgItem(hDlg,IDC_DEBUG_MEM_COL0); + + ViewMemWnd(hDlg, dwAddress); + SendMessage(hWnd,LB_SETCURSEL,0,0); + SetFocus(hWnd); + return -1; // call windows default handler +} + +// +// view of given address in memory window +// +static BOOL OnMemGoAdr(HWND hDlg) +{ + DWORD dwAddress = -1; // no address given + + OnEnterAddress(hDlg, &dwAddress); + if (dwAddress != -1) // not Cancel key + OnMemGoDx(hDlg,dwAddress & (dwMapDataSize - 1)); + return -1; // call windows default handler +} + +// +// view from address in memory window +// +static BOOL OnMemFollow(HWND hDlg,UINT uID) +{ + if (ID_DEBUG_MEM_FADDR == uID) // ask for follow address + { + DWORD dwAddress = -1; // no address given + + OnEnterAddress(hDlg, &dwAddress); + if (dwAddress == -1) return -1; // return at cancel button + + dwAdrMemFol = dwAddress; + } + + CheckMenuItem(hMenuMem,uIDFol,MF_UNCHECKED); + uIDFol = uID; + CheckMenuItem(hMenuMem,uIDFol,MF_CHECKED); + UpdateMemoryWnd(hDlg); // update memory window + return -1; // call windows default handler +} + +// +// clear all breakpoints +// +static BOOL OnClearAll(HWND hDlg) +{ + wBreakpointCount = 0; + // redraw code window + InvalidateRect(GetDlgItem(hDlg,IDC_DEBUG_CODE),NULL,TRUE); + return 0; +} + +// +// toggle menu selection +// +static BOOL OnToggleMenuItem(HWND hDlg,UINT uIDCheckItem,BOOL *bCheck) +{ + *bCheck = !*bCheck; // toggle flag + CheckMenuItem(GetMenu(hDlg),uIDCheckItem,*bCheck ? MF_CHECKED : MF_UNCHECKED); + return 0; +} + +// +// change memory window mapping style +// +static BOOL OnMemMapping(HWND hDlg,UINT uID) +{ + if (uID == uIDMap) return -1; // same view, call windows default handler + + SetMappingMenu(hDlg,uID); // update menu settings + UpdateMemoryWnd(hDlg); // update memory window + return 0; +} + +// +// push value on hardware stack +// +static BOOL OnStackPush(HWND hDlg) +{ + TCHAR szBuffer[] = _T("00000"); + DWORD dwAddr; + HWND hWnd; + INT i,j; + + if (nDbgState != DBG_STEPINTO) // not in single step mode + return TRUE; + + hWnd = GetDlgItem(hDlg,IDC_DEBUG_STACK); + + i = (INT) SendMessage(hWnd,LB_GETCURSEL,0,0); + if (LB_ERR == i) return TRUE; // no selection + + if (IDOK != OnNewValue(szBuffer)) // canceled function + return TRUE; + _stscanf(szBuffer,_T("%5X"),&dwAddr); + + // push stack element + for (j = ARRAYSIZEOF(Chipset.rstk); j > i + 1; --j) + { + Chipset.rstk[(Chipset.rstkp-j)&7] = Chipset.rstk[(Chipset.rstkp-j+1)&7]; + } + Chipset.rstk[(Chipset.rstkp-j)&7] = dwAddr; + + UpdateStackWnd(hDlg); // update stack window + SendMessage(hWnd,LB_SETCURSEL,i,0); // restore cursor postion + return 0; +} + +// +// pop value from hardware stack +// +static BOOL OnStackPop(HWND hDlg) +{ + HWND hWnd; + INT i,j; + + if (nDbgState != DBG_STEPINTO) // not in single step mode + return TRUE; + + hWnd = GetDlgItem(hDlg,IDC_DEBUG_STACK); + + i = (INT) SendMessage(hWnd,LB_GETCURSEL,0,0); + if (LB_ERR == i) return TRUE; // no selection + + // pop stack element + for (j = i + 1; j < ARRAYSIZEOF(Chipset.rstk); ++j) + { + Chipset.rstk[(Chipset.rstkp-j)&7] = Chipset.rstk[(Chipset.rstkp-j-1)&7]; + } + Chipset.rstk[Chipset.rstkp] = 0; + + UpdateStackWnd(hDlg); // update stack window + SendMessage(hWnd,LB_SETCURSEL,i,0); // restore cursor postion + return 0; +} + +// modify value on hardware stack +static BOOL OnStackModify(HWND hDlg) +{ + TCHAR szBuffer[16]; + HWND hWnd; + INT i; + + if (nDbgState != DBG_STEPINTO) // not in single step mode + return TRUE; + + hWnd = GetDlgItem(hDlg,IDC_DEBUG_STACK); + + i = (INT) SendMessage(hWnd,LB_GETCURSEL,0,0); + if (LB_ERR == i) return TRUE; // no selection + + SendMessage(hWnd,LB_GETTEXT,i,(LPARAM) szBuffer); + OnNewValue(&szBuffer[3]); + _stscanf(&szBuffer[3],_T("%5X"),&Chipset.rstk[(Chipset.rstkp-i-1)&7]); + + UpdateStackWnd(hDlg); // update stack window + SendMessage(hWnd,LB_SETCURSEL,i,0); // restore cursor postion + return 0; +} + +// +// new register setting +// +static BOOL OnLButtonUp(HWND hDlg, LPARAM lParam) +{ + TCHAR szBuffer[64]; + POINT pt; + HWND hWnd; + INT nId; + + if (nDbgState != DBG_STEPINTO) // not in single step mode + return TRUE; + + POINTSTOPOINT(pt,MAKEPOINTS(lParam)); + + // handle of selected window + hWnd = ChildWindowFromPointEx(hDlg,pt,CWP_SKIPDISABLED); + nId = GetDlgCtrlID(hWnd); // control ID of window + + SendMessage(hWnd,WM_GETTEXT,ARRAYSIZEOF(szBuffer),(LPARAM)szBuffer); + switch (nId) + { + case IDC_REG_A: // A + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.A,16,&szBuffer[3]); + break; + case IDC_REG_B: // B + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.B,16,&szBuffer[3]); + break; + case IDC_REG_C: // C + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.C,16,&szBuffer[3]); + break; + case IDC_REG_D: // D + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.D,16,&szBuffer[3]); + break; + case IDC_REG_R0: // R0 + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.R0,16,&szBuffer[3]); + break; + case IDC_REG_R1: // R1 + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.R1,16,&szBuffer[3]); + break; + case IDC_REG_R2: // R2 + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.R2,16,&szBuffer[3]); + break; + case IDC_REG_R3: // R3 + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.R3,16,&szBuffer[3]); + break; + case IDC_REG_R4: // R4 + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.R4,16,&szBuffer[3]); + break; + case IDC_REG_D0: // D0 + OnNewValue(&szBuffer[3]); + _stscanf(&szBuffer[3],_T("%5X"),&Chipset.d0); + break; + case IDC_REG_D1: // D1 + OnNewValue(&szBuffer[3]); + _stscanf(&szBuffer[3],_T("%5X"),&Chipset.d1); + break; + case IDC_REG_P: // P + OnNewValue(&szBuffer[2]); + Chipset.P = _totupper(szBuffer[2]) - _T('0'); + if (Chipset.P > 9) Chipset.P -= 7; + PCHANGED; + break; + case IDC_REG_PC: // PC + OnNewValue(&szBuffer[3]); + _stscanf(&szBuffer[3],_T("%5X"),&Chipset.pc); + break; + case IDC_REG_OUT: // OUT + OnNewValue(&szBuffer[4]); + _stscanf(&szBuffer[4],_T("%3X"),&Chipset.out); + break; + case IDC_REG_IN: // IN + OnNewValue(&szBuffer[3]); + _stscanf(&szBuffer[3],_T("%4X"),&Chipset.in); + break; + case IDC_REG_ST: // ST + OnNewValue(&szBuffer[3]); + StrToReg(Chipset.ST,4,&szBuffer[3]); + break; + case IDC_REG_CY: // CY + Chipset.carry = !Chipset.carry; + break; + case IDC_REG_MODE: // MODE + Chipset.mode_dec = !Chipset.mode_dec; + break; + case IDC_REG_MP: // MP + Chipset.HST ^= MP; + break; + case IDC_REG_SR: // SR + Chipset.HST ^= SR; + break; + case IDC_REG_SB: // SB + Chipset.HST ^= SB; + break; + case IDC_REG_XM: // XM + Chipset.HST ^= XM; + break; + case IDC_MISC_INT: // interrupt status + Chipset.inte = !Chipset.inte; + UpdateMiscWnd(hDlg); // update miscellaneous window + break; + case IDC_MISC_KEY: // 1ms keyboard scan + Chipset.intk = !Chipset.intk; + UpdateMiscWnd(hDlg); // update miscellaneous window + break; + case IDC_MISC_BS: // Bank switcher setting + OnNewValue(szBuffer); + _stscanf(szBuffer,_T("%2X"),&Chipset.Bank_FF); + Chipset.Bank_FF &= 0x7F; + RomSwitch(Chipset.Bank_FF); // update memory mapping + + UpdateCodeWnd(hDlg); // update code window + UpdateMemoryWnd(hDlg); // update memory window + UpdateMiscWnd(hDlg); // update miscellaneous window + break; + } + UpdateRegisterWnd(hDlg); // update register + return TRUE; +} + +// +// double click in list box area +// +static BOOL OnDblClick(HWND hWnd, WORD wId) +{ + TCHAR szBuffer[4]; + BYTE byData; + INT i; + DWORD dwAddress; + + if (wId == IDC_DEBUG_STACK) // stack list box + { + // get cpu address of selected item + i = (INT) SendMessage(hWnd,LB_GETCURSEL,0,0); + dwAddress = (DWORD) SendMessage(hWnd,LB_GETITEMDATA,i,0); + + // show code of this address + ViewCodeWnd(GetDlgItem(GetParent(hWnd),IDC_DEBUG_CODE),dwAddress); + return TRUE; + } + + for (i = 0; i < MEMWNDMAX; ++i) // scan all Id's + if (nCol[i] == wId) // found ID + break; + + // not IDC_DEBUG_MEM window or module mode -> default handler + if (i == MEMWNDMAX || ID_DEBUG_MEM_MAP != uIDMap) + return FALSE; + + // calculate address of byte + dwAddress = i * 2; + i = (INT) SendMessage(hWnd,LB_GETCARETINDEX,0,0); + dwAddress += MAXMEMITEMS * i + dwAdrMem; + + // enter new value + SendMessage(hWnd,LB_GETTEXT,i,(LPARAM) szBuffer); + OnNewValue(szBuffer); + _stscanf(szBuffer,_T("%2X"), &byData); + byData = (byData >> 4) | (byData << 4); // change nibbles for writing + + Write2(dwAddress, byData); // write data + ViewMemWnd(GetParent(hWnd),dwAdrMem); // update memory window + SendMessage(hWnd,LB_SETCURSEL,i,0); + return FALSE; +} + +// +// request for context menu +// +static VOID OnContextMenu(HWND hDlg, LPARAM lParam, WPARAM wParam) +{ + POINT pt; + INT nId; + + POINTSTOPOINT(pt,MAKEPOINTS(lParam)); // mouse position + nId = GetDlgCtrlID((HWND) wParam); // control ID of window + + switch(nId) + { + case IDC_DEBUG_CODE: // handle code window + TrackPopupMenu(hMenuCode,0,pt.x,pt.y,0,hDlg,NULL); + break; + + case IDC_DEBUG_MEM_COL0: + case IDC_DEBUG_MEM_COL1: + case IDC_DEBUG_MEM_COL2: + case IDC_DEBUG_MEM_COL3: + case IDC_DEBUG_MEM_COL4: + case IDC_DEBUG_MEM_COL5: + case IDC_DEBUG_MEM_COL6: + case IDC_DEBUG_MEM_COL7: // handle memory window + TrackPopupMenu(hMenuMem,0,pt.x,pt.y,0,hDlg,NULL); + break; + + case IDC_DEBUG_STACK: // handle code window + TrackPopupMenu(hMenuStack,0,pt.x,pt.y,0,hDlg,NULL); + break; + } + return; +} + +//################ +//# +//# Dialog handler +//# +//################ + +// +// handle right/left keys in memory window +// +static __inline BOOL OnKeyRightLeft(HWND hWnd, WPARAM wParam) +{ + HWND hWndNew; + WORD wX; + INT nId; + + nId = GetDlgCtrlID(hWnd); // control ID of window + + for (wX = 0; wX < MEMWNDMAX; ++wX) // scan all Id's + if (nCol[wX] == nId) // found ID + break; + + if (wX == MEMWNDMAX) return -1; // not IDC_DEBUG_MEM window, default handler + + // new position + wX = ((LOWORD(wParam) == VK_RIGHT) ? (wX + 1) : (wX + MEMWNDMAX - 1)) % MEMWNDMAX; + + // set new focus + hWndNew = GetDlgItem(GetParent(hWnd),nCol[wX]); + SendMessage(hWndNew,LB_SETCURSEL,HIWORD(wParam),0); + SetFocus(hWndNew); + return -2; +} + +// +// handle (page) up/down keys in memory window +// +static __inline BOOL OnKeyUpDown(HWND hWnd, WPARAM wParam) +{ + INT wX, wY; + INT nId; + + nId = GetDlgCtrlID(hWnd); // control ID of window + + for (wX = 0; wX < MEMWNDMAX; ++wX) // scan all Id's + if (nCol[wX] == nId) // found ID + break; + + if (wX == MEMWNDMAX) return -1; // not IDC_DEBUG_MEM window, default handler + + wY = HIWORD(wParam); // get old focus + + switch(LOWORD(wParam)) + { + case VK_NEXT: + dwAdrMem = (dwAdrMem + MAXMEMITEMS * MAXMEMLINES) & (dwMapDataSize - 1); + ViewMemWnd(GetParent(hWnd),dwAdrMem); + SendMessage(hWnd,LB_SETCURSEL,wY,0); + return -2; + + case VK_PRIOR: + dwAdrMem = (dwAdrMem - MAXMEMITEMS * MAXMEMLINES) & (dwMapDataSize - 1); + ViewMemWnd(GetParent(hWnd),dwAdrMem); + SendMessage(hWnd,LB_SETCURSEL,wY,0); + return -2; + + case VK_DOWN: + if (wY+1 >= MAXMEMLINES) + { + dwAdrMem = (dwAdrMem + MAXMEMITEMS) & (dwMapDataSize - 1); + ViewMemWnd(GetParent(hWnd),dwAdrMem); + SendMessage(hWnd,LB_SETCURSEL,wY,0); + return -2; + } + break; + + case VK_UP: + if (wY == 0) + { + dwAdrMem = (dwAdrMem - MAXMEMITEMS) & (dwMapDataSize - 1); + ViewMemWnd(GetParent(hWnd),dwAdrMem); + SendMessage(hWnd,LB_SETCURSEL,wY,0); + return -2; + } + break; + } + return -1; +} + +// +// handle keys in code window +// +static __inline BOOL OnKeyCodeWnd(HWND hDlg, WPARAM wParam) +{ + HWND hWnd = GetDlgItem(hDlg,IDC_DEBUG_CODE); + WORD wKey = LOWORD(wParam); + WORD wItem = HIWORD(wParam); + + // down key on last line + if ((wKey == VK_DOWN || wKey == VK_NEXT) && wItem == MAXCODELINES - 1) + { + ViewCodeWnd(hWnd,dwAdrLine[1]); + SendMessage(hWnd,LB_SETCURSEL,wItem,0); + } + // up key on first line + if ((wKey == VK_UP || wKey == VK_PRIOR) && wItem == 0) + { + if (dwAdrLine[0] > 0) ViewCodeWnd(hWnd,dwAdrLine[0]-1); + } + + if (wKey == _T('G')) return OnCodeGoAdr(GetParent(hWnd)); // goto new address + + return -1; +} + +// +// handle drawing in code window +// +static __inline BOOL OnDrawCodeWnd(LPDRAWITEMSTRUCT lpdis) +{ + TCHAR szBuf[64]; + COLORREF crBkColor; + COLORREF crTextColor; + BOOL bBrk,bPC; + + if (lpdis->itemID == -1) // no item in list box + return TRUE; + + // get item text + SendMessage(lpdis->hwndItem,LB_GETTEXT,lpdis->itemID,(LPARAM) szBuf); + + // check for codebreakpoint + bBrk = CheckBreakpoint(dwAdrLine[lpdis->itemID],1,BP_EXEC); + bPC = szBuf[5] != _T(' '); // check if line of program counter + + crTextColor = COLOR_WHITE; // standard text color + + if (lpdis->itemState & ODS_SELECTED) // cursor line + { + if (bPC) // PC line + { + crBkColor = bBrk ? COLOR_DKGRAY : COLOR_TEAL; + } + else // normal line + { + crBkColor = bBrk ? COLOR_PURPLE : COLOR_NAVY; + } + } + else // not cursor line + { + if (bPC) // PC line + { + crBkColor = bBrk ? COLOR_OLIVE : COLOR_GREEN; + } + else // normal line + { + if (bBrk) + { + crBkColor = COLOR_MAROON; + } + else + { + crBkColor = COLOR_WHITE; + crTextColor = COLOR_BLACK; + } + } + } + + // write Text + crBkColor = SetBkColor(lpdis->hDC,crBkColor); + crTextColor = SetTextColor(lpdis->hDC,crTextColor); + + ExtTextOut(lpdis->hDC,(int)(lpdis->rcItem.left)+2,(int)(lpdis->rcItem.top), + ETO_OPAQUE,(LPRECT)&lpdis->rcItem,szBuf,lstrlen(szBuf),NULL); + + SetBkColor(lpdis->hDC,crBkColor); + SetTextColor(lpdis->hDC,crTextColor); + + if (lpdis->itemState & ODS_FOCUS) // redraw focus + DrawFocusRect(lpdis->hDC,&lpdis->rcItem); + + return TRUE; // focus handled here +} + +// +// detect changed register +// +static __inline BOOL OnCtlColorStatic(HWND hWnd) +{ + BOOL bError = FALSE; // not changed + + int nId = GetDlgCtrlID(hWnd); + if (nId >= REG_START && nId <= REG_STOP) // in register area + bError = bRegUpdate[nId-REG_START]; // register changed? + return bError; +} + + +//################ +//# +//# Public functions +//# +//################ + +// +// handle upper 32 bit of cpu cycle counter +// +VOID UpdateDbgCycleCounter(VOID) +{ + // update 64 bit cpu cycle counter + if (Chipset.cycles < dwDbgRefCycles) ++Chipset.cycles_reserved; + dwDbgRefCycles = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + return; +} + +// +// check for breakpoints +// +BOOL CheckBreakpoint(DWORD dwAddr, DWORD dwRange, UINT nType) +{ + INT i; + + for (i = 0; i < wBreakpointCount; ++i) // scan all breakpoints + { + // check address range and type + if ( sBreakpoint[i].bEnable + && sBreakpoint[i].dwAddr >= dwAddr && sBreakpoint[i].dwAddr < dwAddr + dwRange + && (sBreakpoint[i].nType & nType) != 0) + return TRUE; + } + return FALSE; +} + +// +// notify debugger that emulation stopped +// +VOID NotifyDebugger(INT nType) // update registers +{ + nRplBreak = nType; // save breakpoint type + _ASSERT(hDlgDebug); // debug dialog box open + PostMessage(hDlgDebug,WM_UPDATE,0,0); + return; +} + +// +// disable debugger +// +VOID DisableDebugger(VOID) +{ + if (hDlgDebug) // debugger running + DestroyWindow(hDlgDebug); // then close debugger to renter emulation + return; +} + + +//################ +//# +//# Debugger Message loop +//# +//################ + +// +// ID_TOOL_DEBUG +// +static __inline HWND CreateToolbar(HWND hWnd) +{ + HRSRC hRes; + HGLOBAL hGlobal; + CToolBarData *pData; + TBBUTTON *ptbb; + INT i,j; + + HWND hWndToolbar = NULL; // toolbar window + + InitCommonControls(); // ensure that common control DLL is loaded + + if ((hRes = FindResource(hApp,MAKEINTRESOURCE(IDR_DEBUG_TOOLBAR),RT_TOOLBAR)) == NULL) + goto quit; + + if ((hGlobal = LoadResource(hApp,hRes)) == NULL) + goto quit; + + if ((pData = (CToolBarData*) LockResource(hGlobal)) == NULL) + goto unlock; + + _ASSERT(pData->wVersion == 1); // toolbar resource version + + // alloc memory for TBBUTTON stucture + if (!(ptbb = HeapAlloc(hHeap,0,pData->wItemCount*sizeof(TBBUTTON)))) + goto unlock; + + // fill TBBUTTON stucture with resource data + for (i = j = 0; i < pData->wItemCount; ++i) + { + if (pData->aItems[i]) + { + ptbb[i].iBitmap = j++; + ptbb[i].fsStyle = TBSTYLE_BUTTON; + } + else + { + ptbb[i].iBitmap = 5; // separator width + ptbb[i].fsStyle = TBSTYLE_SEP; + } + ptbb[i].idCommand = pData->aItems[i]; + ptbb[i].fsState = TBSTATE_ENABLED; + ptbb[i].dwData = 0; + ptbb[i].iString = j; + } + + hWndToolbar = CreateToolbarEx(hWnd,WS_CHILD | WS_VISIBLE | TBSTYLE_TOOLTIPS, + IDR_DEBUG_TOOLBAR,j,hApp,IDR_DEBUG_TOOLBAR,ptbb,pData->wItemCount, + pData->wWidth,pData->wHeight,pData->wWidth,pData->wHeight, + sizeof(TBBUTTON)); + + HeapFree(hHeap,0,ptbb); + +unlock: + FreeResource(hGlobal); +quit: + return hWndToolbar; +} + +static INT_PTR CALLBACK Debugger(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static HMENU hMenuMainCode,hMenuMainMem,hMenuMainStack; + + WINDOWPLACEMENT wndpl; + TEXTMETRIC tm; + HDC hDC; + HFONT hFont; + INT i; + + switch (message) + { + case WM_INITDIALOG: + SetWindowLocation(hDlg,nDbgPosX,nDbgPosY); + SendMessage(hDlg,WM_SETICON,ICON_BIG,(LPARAM) LoadIcon(hApp,MAKEINTRESOURCE(IDI_EMU48))); + hWndToolbar = CreateToolbar(hDlg); // add toolbar + CheckMenuItem(GetMenu(hDlg),ID_BREAKPOINTS_NOP3, bDbgNOP3 ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(GetMenu(hDlg),ID_BREAKPOINTS_DOCODE,bDbgCode ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(GetMenu(hDlg),ID_BREAKPOINTS_RPL, bDbgRPL ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(GetMenu(hDlg),ID_INTR_STEPOVERINT, bDbgSkipInt ? MF_CHECKED : MF_UNCHECKED); + hDlgDebug = hDlg; // handle for debugger dialog + hEventDebug = CreateEvent(NULL,FALSE,FALSE,NULL); + if (hEventDebug == NULL) + { + AbortMessage(_T("Event creation failed !")); + return TRUE; + } + hMenuMainCode = LoadMenu(hApp,MAKEINTRESOURCE(IDR_DEBUG_CODE)); + _ASSERT(hMenuMainCode); + hMenuCode = GetSubMenu(hMenuMainCode, 0); + _ASSERT(hMenuCode); + hMenuMainMem = LoadMenu(hApp,MAKEINTRESOURCE(IDR_DEBUG_MEM)); + _ASSERT(hMenuMainMem); + hMenuMem = GetSubMenu(hMenuMainMem, 0); + _ASSERT(hMenuMem); + hMenuMainStack = LoadMenu(hApp,MAKEINTRESOURCE(IDR_DEBUG_STACK)); + _ASSERT(hMenuMainStack); + hMenuStack = GetSubMenu(hMenuMainStack, 0); + _ASSERT(hMenuStack); + + // font settings + SendDlgItemMessage(hDlg,IDC_STATIC_CODE, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_STATIC_REGISTERS,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_STATIC_MEMORY, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_STATIC_STACK, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_STATIC_MMU, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_STATIC_MISC, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + + // init last instruction circular buffer + pdwInstrArray = HeapAlloc(hHeap,0,INSTRSIZE*sizeof(*pdwInstrArray)); + wInstrSize = INSTRSIZE; // size of last instruction array + wInstrWp = wInstrRp = 0; // write/read pointer + + // init "Follow" menu entry in debugger "Memory" context menu + CheckMenuItem(hMenuMem,uIDFol,MF_CHECKED); + + InitMemMap(hDlg); // init memory mapping table + InitBsArea(hDlg); // init bank switcher list box + DisableMenuKeys(hDlg); // set debug menu keys into run state + + disassembler_map = MEM_MAP; // disassemble with mapped modules + + dwDbgStopPC = -1; // no stop address for goto cursor + dwDbgRplPC = -1; // no stop address for RPL breakpoint + + // init reference cpu cycle counter for 64 bit debug cycle counter + dwDbgRefCycles = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + + nDbgState = DBG_STEPINTO; // state "step into" + if (Chipset.Shutdn) // cpu thread stopped + SetEvent(hEventShutdn); // goto debug session + + UpdateWindowStatus(); // disable application menu items + OldChipset = Chipset; // save chipset values + return TRUE; + + case WM_DESTROY: + // SetHP48Time(); // update time & date + nDbgState = DBG_OFF; // debugger inactive + bInterrupt = TRUE; // exit opcode loop + SetEvent(hEventDebug); + if (pdwInstrArray) // free last instruction circular buffer + { + HeapFree(hHeap,0,pdwInstrArray); + pdwInstrArray = NULL; + } + CloseHandle(hEventDebug); + wndpl.length = sizeof(wndpl); // save debugger window position + GetWindowPlacement(hDlg, &wndpl); + nDbgPosX = wndpl.rcNormalPosition.left; + nDbgPosY = wndpl.rcNormalPosition.top; + DestroyMenu(hMenuMainCode); + DestroyMenu(hMenuMainMem); + DestroyMenu(hMenuMainStack); + _ASSERT(hWnd); + UpdateWindowStatus(); // enable application menu items + hDlgDebug = NULL; // debugger windows closed + break; + + case WM_CLOSE: + DestroyWindow(hDlg); + break; + + case WM_UPDATE: + OnUpdate(hDlg); + return TRUE; + + case WM_COMMAND: + switch (HIWORD(wParam)) + { + case LBN_DBLCLK: + return OnDblClick((HWND) lParam, LOWORD(wParam)); + + case LBN_SETFOCUS: + i = (INT) SendMessage((HWND) lParam,LB_GETCARETINDEX,0,0); + SendMessage((HWND) lParam,LB_SETCURSEL,i,0); + return TRUE; + + case LBN_KILLFOCUS: + SendMessage((HWND) lParam,LB_SETCURSEL,-1,0); + return TRUE; + } + + switch (LOWORD(wParam)) + { + case ID_BREAKPOINTS_SETBREAK: return OnKeyF2(hDlg); + case ID_DEBUG_RUN: return OnKeyF5(hDlg); + case ID_DEBUG_RUNCURSOR: return OnKeyF6(hDlg); + case ID_DEBUG_STEP: return OnKeyF7(hDlg); + case ID_DEBUG_STEPOVER: return OnKeyF8(hDlg); + case ID_DEBUG_STEPOUT: return OnKeyF9(hDlg); + case ID_DEBUG_BREAK: return OnKeyF11(hDlg); + case ID_DEBUG_CODE_GOADR: return OnCodeGoAdr(hDlg); + case ID_DEBUG_CODE_GOPC: return OnCodeGoPC(hDlg); + case ID_DEBUG_CODE_SETPCTOSELECT: return OnCodeSetPcToSelection(hDlg); + case ID_BREAKPOINTS_CODEEDIT: return OnEditBreakpoint(hDlg); + case ID_BREAKPOINTS_CLEARALL: return OnClearAll(hDlg); + case ID_BREAKPOINTS_NOP3: return OnToggleMenuItem(hDlg,LOWORD(wParam),&bDbgNOP3); + case ID_BREAKPOINTS_DOCODE: return OnToggleMenuItem(hDlg,LOWORD(wParam),&bDbgCode); + case ID_BREAKPOINTS_RPL: return OnToggleMenuItem(hDlg,LOWORD(wParam),&bDbgRPL); + case ID_INFO_LASTINSTRUCTIONS: return OnInfoIntr(hDlg); + case ID_INFO_PROFILE: return OnProfile(hDlg); + case ID_INFO_WRITEONLYREG: return OnInfoWoRegister(hDlg); + case ID_INTR_STEPOVERINT: return OnToggleMenuItem(hDlg,LOWORD(wParam),&bDbgSkipInt); + case ID_DEBUG_MEM_GOADR: return OnMemGoAdr(hDlg); + case ID_DEBUG_MEM_GOPC: return OnMemGoDx(hDlg,Chipset.pc); + case ID_DEBUG_MEM_GOD0: return OnMemGoDx(hDlg,Chipset.d0); + case ID_DEBUG_MEM_GOD1: return OnMemGoDx(hDlg,Chipset.d1); + case ID_DEBUG_MEM_GOSTACK: return OnMemGoDx(hDlg,Chipset.rstk[(Chipset.rstkp-1)&7]); + case ID_DEBUG_MEM_FNONE: + case ID_DEBUG_MEM_FADDR: + case ID_DEBUG_MEM_FPC: + case ID_DEBUG_MEM_FD0: + case ID_DEBUG_MEM_FD1: return OnMemFollow(hDlg,LOWORD(wParam)); + case ID_DEBUG_MEM_FIND: return OnMemFind(hDlg); + case ID_DEBUG_MEM_MAP: + case ID_DEBUG_MEM_NCE1: + case ID_DEBUG_MEM_NCE2: + case ID_DEBUG_MEM_CE1: + case ID_DEBUG_MEM_CE2: + case ID_DEBUG_MEM_NCE3: return OnMemMapping(hDlg,LOWORD(wParam)); + case ID_DEBUG_STACK_PUSH: return OnStackPush(hDlg); + case ID_DEBUG_STACK_POP: return OnStackPop(hDlg); + case ID_DEBUG_STACK_MODIFY: return OnStackModify(hDlg); + + case ID_DEBUG_CANCEL: DestroyWindow(hDlg); return TRUE; + } + break; + + case WM_VKEYTOITEM: + switch (LOWORD(wParam)) // always valid + { + case VK_F2: return OnKeyF2(hDlg); // toggle breakpoint + case VK_F5: return OnKeyF5(hDlg); // key run + case VK_F6: return OnKeyF6(hDlg); // key step cursor + case VK_F7: return OnKeyF7(hDlg); // key step into + case VK_F8: return OnKeyF8(hDlg); // key step over + case VK_F9: return OnKeyF9(hDlg); // key step out + case VK_F11: return OnKeyF11(hDlg); // key break + } + + switch(GetDlgCtrlID((HWND) lParam)) // calling window + { + // handle code window + case IDC_DEBUG_CODE: + return OnKeyCodeWnd(hDlg, wParam); + + // handle memory window + case IDC_DEBUG_MEM_COL0: + case IDC_DEBUG_MEM_COL1: + case IDC_DEBUG_MEM_COL2: + case IDC_DEBUG_MEM_COL3: + case IDC_DEBUG_MEM_COL4: + case IDC_DEBUG_MEM_COL5: + case IDC_DEBUG_MEM_COL6: + case IDC_DEBUG_MEM_COL7: + switch (LOWORD(wParam)) + { + case _T('G'): return OnMemGoAdr(GetParent((HWND) lParam)); + case _T('F'): return OnMemFind(GetParent((HWND) lParam)); + case VK_RIGHT: + case VK_LEFT: return OnKeyRightLeft((HWND) lParam, wParam); + case VK_NEXT: + case VK_PRIOR: + case VK_DOWN: + case VK_UP: return OnKeyUpDown((HWND) lParam, wParam); + } + break; + } + return -1; // default action + + case WM_LBUTTONUP: + return OnLButtonUp(hDlg, lParam); + + case WM_CONTEXTMENU: + OnContextMenu(hDlg, lParam, wParam); + break; + + case WM_CTLCOLORSTATIC: // register color highlighting + // highlight text? + if (OnCtlColorStatic((HWND) lParam)) + { + SetTextColor((HDC) wParam, COLOR_RED); + SetBkColor((HDC) wParam, GetSysColor(COLOR_BTNFACE)); + return (INT_PTR) GetStockObject(NULL_BRUSH); // transparent brush + } + break; + + case WM_NOTIFY: + // tooltip for toolbar + if(((LPNMHDR) lParam)->code == TTN_GETDISPINFO) + { + ((LPTOOLTIPTEXT) lParam)->hinst = hApp; + ((LPTOOLTIPTEXT) lParam)->lpszText = MAKEINTRESOURCE(((LPTOOLTIPTEXT) lParam)->hdr.idFrom); + break; + } + break; + + case WM_DRAWITEM: + if (wParam == IDC_DEBUG_CODE) return OnDrawCodeWnd((LPDRAWITEMSTRUCT) lParam); + break; + + case WM_MEASUREITEM: + hDC = GetDC(hDlg); + + // GetTextMetrics from "Courier New 8" font + hFont = CreateFont(-MulDiv(8,GetDeviceCaps(hDC, LOGPIXELSY),72),0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET, + OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FF_DONTCARE,_T("Courier New")); + + hFont = SelectObject(hDC,hFont); + GetTextMetrics(hDC,&tm); + hFont = SelectObject(hDC,hFont); + DeleteObject(hFont); + + ((LPMEASUREITEMSTRUCT) lParam)->itemHeight = tm.tmHeight; + lCharWidth = tm.tmAveCharWidth; + + ReleaseDC(hDlg,hDC); + return TRUE; + } + return FALSE; +} + +LRESULT OnToolDebug() // debugger dialogbox call +{ + if ((hDlgDebug = CreateDialog(hApp,MAKEINTRESOURCE(IDD_DEBUG),GetParent(hWnd), + (DLGPROC)Debugger)) == NULL) + AbortMessage(_T("Debugger Dialog Box Creation Error !")); + return 0; +} + + +//################ +//# +//# Find dialog box +//# +//################ + +static __inline BOOL OnFindOK(HWND hDlg,BOOL bASCII,DWORD *pdwAddrLast) +{ + BYTE *lpbySearch; + INT i,j; + DWORD dwAddr; + BOOL bMatch; + + HWND hWnd = GetDlgItem(hDlg,IDC_FIND_DATA); + + i = GetWindowTextLength(hWnd) + 1; // text length incl. EOS + j = bASCII ? 2 : sizeof(*(LPTSTR)0); // buffer width + + // allocate search buffer + if ((lpbySearch = HeapAlloc(hHeap,0,i * j)) != NULL) + { + // get search text and real length + i = GetWindowText(hWnd,(LPTSTR) lpbySearch,i); + + // add string to combo box + if (SendMessage(hWnd,CB_FINDSTRINGEXACT,0,(LPARAM) lpbySearch) == CB_ERR) + SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) lpbySearch); + + #if defined _UNICODE + { + // Unicode to byte translation + LPTSTR szTmp = DuplicateString((LPTSTR) lpbySearch); + if (szTmp != NULL) + { + WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, + szTmp, -1, + lpbySearch, i+1, NULL, NULL); + HeapFree(hHeap,0,szTmp); + } + } + #endif + + // convert input format to nibble based format + if (bASCII) // ASCII input + { + // convert ASCII to number + for (j = i - 1; j >= 0; --j) + { + // order LSB, MSB + lpbySearch[j * 2 + 1] = lpbySearch[j] >> 4; + lpbySearch[j * 2] = lpbySearch[j] & 0xF; + } + i *= 2; // no. of nibbles + } + else // hex number input + { + // convert HEX to number + for (i = 0, j = 0; lpbySearch[j] != 0; ++j) + { + if (lpbySearch[j] == ' ') // skip space + continue; + + if (isxdigit(lpbySearch[j])) + { + lpbySearch[i] = toupper(lpbySearch[j]) - '0'; + if (lpbySearch[i] > 9) lpbySearch[i] -= 'A' - '9' - 1; + } + else + { + i = 0; // wrong format, no match + break; + } + ++i; // inc, no. of nibbles + } + } + + bMatch = FALSE; // no match + dwAddr = dwAdrMem; // calculate search start address + if (*pdwAddrLast == dwAddr) + ++dwAddr; + + // scan mapping/module until match + for (; i && !bMatch && dwAddr <= dwAdrMem + dwMapDataSize; ++dwAddr) + { + BYTE byC; + + // i = no. of nibbles that have to match + for (bMatch = TRUE, j = 0;bMatch && j < i; ++j) + { + if (ID_DEBUG_MEM_MAP == uIDMap) // mapped memory content + { + Npeek(&byC,(dwAddr + j) & 0xFFFFF,1); + } + else // module memory content + { + byC = lbyMapData[(dwAddr + j) & (dwMapDataSize - 1)]; + } + bMatch = (byC == lpbySearch[j]); + } + } + dwAddr = (dwAddr - 1) & (dwMapDataSize - 1); // possible matching address + HeapFree(hHeap,0,lpbySearch); + + // check match result + if (bMatch) + { + OnMemGoDx(GetParent(hDlg),dwAddr); + *pdwAddrLast = dwAddr; // latest written address + } + else + { + MessageBox(hDlg,_T("Search string not found!"),_T("Find"), + MB_APPLMODAL|MB_OK|MB_ICONEXCLAMATION|MB_SETFOREGROUND); + } + } + return TRUE; +} + +// +// enter find dialog +// +static INT_PTR CALLBACK Find(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static DWORD dwAddrEntry; + static BOOL bASCII = FALSE; + + switch (message) + { + case WM_INITDIALOG: + CheckDlgButton(hDlg,IDC_FIND_ASCII,bASCII); + dwAddrEntry = -1; + return TRUE; + + case WM_DESTROY: + hDlgFind = NULL; + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_FIND_ASCII: bASCII = !bASCII; return TRUE; + case IDOK: return OnFindOK(hDlg,bASCII,&dwAddrEntry); + case IDCANCEL: DestroyWindow(hDlg); return TRUE; + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnMemFind(HWND hDlg) +{ + if (hDlgFind == NULL) // no find dialog, create it + { + if ((hDlgFind = CreateDialog(hApp,MAKEINTRESOURCE(IDD_FIND),hDlg, + (DLGPROC)Find)) == NULL) + AbortMessage(_T("Find Dialog Box Creation Error !")); + } + else + { + SetFocus(hDlgFind); // set focus on find dialog + } + return -1; // call windows default handler +} + + +//################ +//# +//# Profile dialog box +//# +//################ + +// +// update profiler dialog content +// +static VOID UpdateProfileWnd(HWND hDlg) +{ + #define CPU_FREQ 524288 // base CPU frequency + #define SX_RATE 0x0E + #define GX_RATE 0x1B + #define GP_RATE 0x1B*3 // CdB for HP: add high speed apples + #define G2_RATE 0x1B*2 // CdB for HP: add low speed apples + + LPCTSTR pcUnit[] = { _T("s"),_T("ms"),_T("us"),_T("ns") }; + + QWORD lVar; + TCHAR szBuffer[64]; + INT i; + DWORD dwFreq, dwEndFreq; + + if (hDlg == NULL) return; // dialog not open + + // 64 bit cpu cycle counter + lVar = *(QWORD *)&Chipset.cycles - *(QWORD *)&OldChipset.cycles; + + // cycle counts + _sntprintf(szBuffer,ARRAYSIZEOF(szBuffer),_T("%I64u"),lVar); + SetDlgItemText(hDlg,IDC_PROFILE_LASTCYCLES,szBuffer); + + // CPU frequency + switch (cCurrentRomType) // CdB for HP: add apples speed selection + { + case 'S': dwFreq= ((SX_RATE + 1) * CPU_FREQ / 4); break; + case 'X': case 'G': case 'E': case 'A': dwFreq= ((GX_RATE + 1) * CPU_FREQ / 4); break; + case 'P': case 'Q': dwFreq= ((GP_RATE + 1) * CPU_FREQ / 4); break; + case '2': dwFreq= ((G2_RATE + 1) * CPU_FREQ / 4); break; + } + dwEndFreq = ((999 * 2 - 1) * dwFreq) / (2 * 1000); + + // search for unit + for (i = 0; i < ARRAYSIZEOF(pcUnit) - 1; ++i) + { + if (lVar > dwEndFreq) break; // found ENG unit + lVar *= 1000; // next ENG unit + } + + // calculate rounded time + lVar = (2 * lVar + dwFreq) / (2 * dwFreq); + + _ASSERT(i < ARRAYSIZEOF(pcUnit)); + _sntprintf(szBuffer,ARRAYSIZEOF(szBuffer),_T("%I64u %s"),lVar,pcUnit[i]); + SetDlgItemText(hDlg,IDC_PROFILE_LASTTIME,szBuffer); + return; + #undef SX_CLK + #undef GX_CLK + #undef GP_RATE + #undef G2_RATE + #undef CPU_FREQ +} + +// +// enter profile dialog +// +static INT_PTR CALLBACK Profile(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + UpdateProfileWnd(hDlg); + return TRUE; + + case WM_DESTROY: + hDlgProfile = NULL; + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDCANCEL: DestroyWindow(hDlg); return TRUE; + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnProfile(HWND hDlg) +{ + if (hDlgProfile == NULL) // no profile dialog, create it + { + if ((hDlgProfile = CreateDialog(hApp,MAKEINTRESOURCE(IDD_PROFILE),hDlg, + (DLGPROC)Profile)) == NULL) + AbortMessage(_T("Profile Dialog Box Creation Error !")); + } + else + { + SetFocus(hDlgProfile); // set focus on profile dialog + } + return -1; // call windows default handler +} + +//################ +//# +//# New Value dialog box +//# +//################ + +// +// enter new value dialog +// +static INT_PTR CALLBACK NewValue(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static LPTSTR lpszBuffer; // handle of buffer + static int nBufferlen; // length of buffer + + HWND hWnd; + TCHAR szBuffer[64]; + LONG i; + + switch (message) + { + case WM_INITDIALOG: + lpszBuffer = (LPTSTR) lParam; + // length with zero string terminator + nBufferlen = lstrlen(lpszBuffer)+1; + _ASSERT(ARRAYSIZEOF(szBuffer) >= nBufferlen); + SetDlgItemText(hDlg,IDC_NEWVALUE,lpszBuffer); + return TRUE; + case WM_COMMAND: + wParam = LOWORD(wParam); + switch(wParam) + { + case IDOK: + hWnd = GetDlgItem(hDlg,IDC_NEWVALUE); + SendMessage(hWnd,WM_GETTEXT,(WPARAM)nBufferlen,(LPARAM)szBuffer); + // test if valid hex address + for (i = 0; i < (LONG) lstrlen(szBuffer); ++i) + { + if (_istxdigit(szBuffer[i]) == 0) + { + SendMessage(hWnd,EM_SETSEL,0,-1); + SetFocus(hWnd); // focus to edit control + return FALSE; + } + } + lstrcpy(lpszBuffer,szBuffer); // copy valid value + // no break + case IDCANCEL: + EndDialog(hDlg,wParam); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(wParam); +} + +static INT_PTR OnNewValue(LPTSTR lpszValue) +{ + INT_PTR nResult; + + if ((nResult = DialogBoxParam(hApp, + MAKEINTRESOURCE(IDD_NEWVALUE), + hDlgDebug, + (DLGPROC)NewValue, + (LPARAM)lpszValue) + ) == -1) + AbortMessage(_T("Input Dialog Box Creation Error !")); + return nResult; +} + + +//################ +//# +//# Goto Address dialog box +//# +//################ + +// +// enter goto address dialog +// +static INT_PTR CALLBACK EnterAddr(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static DWORD *dwAddress; + + HWND hWnd; + TCHAR szBuffer[8]; + LONG i; + + switch (message) + { + case WM_INITDIALOG: + dwAddress = (DWORD *) lParam; + return TRUE; + case WM_COMMAND: + wParam = LOWORD(wParam); + switch(wParam) + { + case IDOK: + hWnd = GetDlgItem(hDlg,IDC_ENTERADR); + SendMessage(hWnd,WM_GETTEXT,8,(LPARAM)szBuffer); + // test if valid hex address + for (i = 0; i < (LONG) lstrlen(szBuffer); ++i) + { + if (_istxdigit(szBuffer[i]) == 0) + { + SendMessage(hWnd,EM_SETSEL,0,-1); + SetFocus(hWnd); // focus to edit control + return FALSE; + } + } + if (*szBuffer) _stscanf(szBuffer,_T("%6X"),dwAddress); + // no break + case IDCANCEL: + EndDialog(hDlg,wParam); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(wParam); +} + +static VOID OnEnterAddress(HWND hDlg, DWORD *dwValue) +{ + if (DialogBoxParam(hApp, MAKEINTRESOURCE(IDD_ENTERADR), hDlg, (DLGPROC)EnterAddr, (LPARAM)dwValue) == -1) + AbortMessage(_T("Address Dialog Box Creation Error !")); +} + + +//################ +//# +//# Breakpoint dialog box +//# +//################ + +// +// enter breakpoint dialog +// +static INT_PTR CALLBACK EnterBreakpoint(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static BP_T *sBp; + + HWND hWnd; + TCHAR szBuffer[8]; + LONG i; + + switch (message) + { + case WM_INITDIALOG: + sBp = (BP_T *) lParam; + sBp->bEnable = TRUE; + sBp->nType = BP_EXEC; + SendDlgItemMessage(hDlg,IDC_BPCODE,BM_SETCHECK,1,0); + return TRUE; + case WM_COMMAND: + wParam = LOWORD(wParam); + switch(wParam) + { + case IDC_BPCODE: sBp->nType = BP_EXEC; return TRUE; + case IDC_BPRPL: sBp->nType = BP_RPL; return TRUE; + case IDC_BPACCESS: sBp->nType = BP_ACCESS; return TRUE; + case IDC_BPREAD: sBp->nType = BP_READ; return TRUE; + case IDC_BPWRITE: sBp->nType = BP_WRITE; return TRUE; + case IDOK: + hWnd = GetDlgItem(hDlg,IDC_ENTERADR); + SendMessage(hWnd,WM_GETTEXT,8,(LPARAM)szBuffer); + // test if valid hex address + for (i = 0; i < (LONG) lstrlen(szBuffer); ++i) + { + if (_istxdigit(szBuffer[i]) == 0) + { + SendMessage(hWnd,EM_SETSEL,0,-1); + SetFocus(hWnd); + return FALSE; + } + } + if (*szBuffer) _stscanf(szBuffer,_T("%5X"),&sBp->dwAddr); + // no break + case IDCANCEL: + EndDialog(hDlg,wParam); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(wParam); +} + +static VOID OnEnterBreakpoint(HWND hDlg, BP_T *sValue) +{ + if (DialogBoxParam(hApp, MAKEINTRESOURCE(IDD_ENTERBREAK), hDlg, (DLGPROC)EnterBreakpoint, (LPARAM)sValue) == -1) + AbortMessage(_T("Breakpoint Dialog Box Creation Error !")); +} + + +//################ +//# +//# Edit breakpoint dialog box +//# +//################ + +// +// handle drawing in breakpoint window +// +static __inline BOOL OnDrawBreakWnd(LPDRAWITEMSTRUCT lpdis) +{ + TCHAR szBuf[64]; + COLORREF crBkColor,crTextColor; + HDC hdcMem; + HBITMAP hBmpOld; + INT i; + + if (lpdis->itemID == -1) // no item in list box + return TRUE; + + if (lpdis->itemState & ODS_SELECTED) // cursor line + { + crBkColor = COLOR_NAVY; + crTextColor = COLOR_WHITE; + } + else + { + crBkColor = COLOR_WHITE; + crTextColor = COLOR_BLACK; + } + + // write Text + crBkColor = SetBkColor(lpdis->hDC,crBkColor); + crTextColor = SetTextColor(lpdis->hDC,crTextColor); + + SendMessage(lpdis->hwndItem,LB_GETTEXT,lpdis->itemID,(LPARAM) szBuf); + ExtTextOut(lpdis->hDC,(int)(lpdis->rcItem.left)+17,(int)(lpdis->rcItem.top), + ETO_OPAQUE,(LPRECT)&lpdis->rcItem,szBuf,lstrlen(szBuf),NULL); + + SetBkColor(lpdis->hDC,crBkColor); + SetTextColor(lpdis->hDC,crTextColor); + + // draw checkbox + i = (INT) SendMessage(lpdis->hwndItem,LB_GETITEMDATA,lpdis->itemID,0); + hdcMem = CreateCompatibleDC(lpdis->hDC); + _ASSERT(hBmpCheckBox); + hBmpOld = SelectObject(hdcMem,hBmpCheckBox); + + BitBlt(lpdis->hDC,lpdis->rcItem.left+2,lpdis->rcItem.top+2, + 11,lpdis->rcItem.bottom - lpdis->rcItem.top, + hdcMem,sBreakpoint[i].bEnable ? 0 : 10,0,SRCCOPY); + + SelectObject(hdcMem,hBmpOld); + DeleteDC(hdcMem); + + if (lpdis->itemState & ODS_FOCUS) // redraw focus + DrawFocusRect(lpdis->hDC,&lpdis->rcItem); + + return TRUE; // focus handled here +} + +// +// toggle breakpoint drawing +// +static BOOL ToggleBreakpointItem(HWND hWnd, INT nItem) +{ + RECT rc; + + // get breakpoint number + INT i = (INT) SendMessage(hWnd,LB_GETITEMDATA,nItem,0); + + sBreakpoint[i].bEnable = !sBreakpoint[i].bEnable; + // update region of toggled item + SendMessage(hWnd,LB_GETITEMRECT,nItem,(LPARAM)&rc); + InvalidateRect(hWnd,&rc,TRUE); + return TRUE; +} + +// +// draw breakpoint type +// +static VOID DrawBreakpoint(HWND hWnd, INT i) +{ + TCHAR *szText,szBuffer[32]; + LPARAM nItem; + + switch(sBreakpoint[i].nType) + { + case BP_EXEC: // code breakpoint + szText = _T("Code"); + break; + case BP_RPL: // RPL breakpoint + szText = _T("RPL"); + break; + case BP_READ: // read memory breakpoint + szText = _T("Memory Read"); + break; + case BP_WRITE: // write memory breakpoint + szText = _T("Memory Write"); + break; + case BP_ACCESS: // memory breakpoint + szText = _T("Memory Access"); + break; + default: // unknown breakpoint type + szText = _T("unknown"); + _ASSERT(0); + } + wsprintf(szBuffer,_T("%05X (%s)"),sBreakpoint[i].dwAddr,szText); + nItem = (INT) SendMessage(hWnd,LB_ADDSTRING,0,(LPARAM) szBuffer); + SendMessage(hWnd,LB_SETITEMDATA,nItem,i); + return; +} + +// +// enter edit breakpoint dialog +// +static INT_PTR CALLBACK EditBreakpoint(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + TEXTMETRIC tm; + + HWND hWnd; + HDC hDC; + HFONT hFont; + BP_T sBp; + INT i,nItem; + + switch (message) + { + case WM_INITDIALOG: + // font settings + SendDlgItemMessage(hDlg,IDC_STATIC_BREAKPOINT,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_BREAKEDIT_ADD, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_BREAKEDIT_DELETE, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDCANCEL, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + + hBmpCheckBox = LoadBitmap(hApp,MAKEINTRESOURCE(IDB_CHECKBOX)); + _ASSERT(hBmpCheckBox); + + hWnd = GetDlgItem(hDlg,IDC_BREAKEDIT_WND); + SendMessage(hWnd,WM_SETREDRAW,FALSE,0); + SendMessage(hWnd,LB_RESETCONTENT,0,0); + for (i = 0; i < wBreakpointCount; ++i) + DrawBreakpoint(hWnd,i); + SendMessage(hWnd,WM_SETREDRAW,TRUE,0); + return TRUE; + + case WM_DESTROY: + DeleteObject(hBmpCheckBox); + return TRUE; + + case WM_COMMAND: + hWnd = GetDlgItem(hDlg,IDC_BREAKEDIT_WND); + switch (HIWORD(wParam)) + { + case LBN_DBLCLK: + if (LOWORD(wParam) == IDC_BREAKEDIT_WND) + { + if ((nItem = (INT) SendMessage(hWnd,LB_GETCURSEL,0,0)) == LB_ERR) + return FALSE; + + return ToggleBreakpointItem(hWnd,nItem); + } + } + + switch(LOWORD(wParam)) + { + case IDC_BREAKEDIT_ADD: + sBp.dwAddr = -1; // no breakpoint given + OnEnterBreakpoint(hDlg, &sBp); + if (sBp.dwAddr != -1) + { + for (i = 0; i < wBreakpointCount; ++i) + { + if (sBreakpoint[i].dwAddr == sBp.dwAddr) + { + // tried to add used code breakpoint + if (sBreakpoint[i].bEnable && (sBreakpoint[i].nType & sBp.nType & (BP_EXEC | BP_RPL)) != 0) + return FALSE; + + // only modify memory breakpoints + if ( ( sBreakpoint[i].bEnable == FALSE + && (sBreakpoint[i].nType & sBp.nType & (BP_EXEC | BP_RPL)) != 0) + || ((sBreakpoint[i].nType & BP_ACCESS) && (sBp.nType & BP_ACCESS))) + { + // replace breakpoint type + sBreakpoint[i].bEnable = TRUE; + sBreakpoint[i].nType = sBp.nType; + + // redaw breakpoint list + SendMessage(hWnd,WM_SETREDRAW,FALSE,0); + SendMessage(hWnd,LB_RESETCONTENT,0,0); + for (i = 0; i < wBreakpointCount; ++i) + DrawBreakpoint(hWnd,i); + SendMessage(hWnd,WM_SETREDRAW,TRUE,0); + return FALSE; + } + } + } + + // check for breakpoint buffer full + if (wBreakpointCount >= MAXBREAKPOINTS) + { + AbortMessage(_T("Reached maximum number of breakpoints !")); + return FALSE; + } + + sBreakpoint[wBreakpointCount].bEnable = sBp.bEnable; + sBreakpoint[wBreakpointCount].nType = sBp.nType; + sBreakpoint[wBreakpointCount].dwAddr = sBp.dwAddr; + + DrawBreakpoint(hWnd,wBreakpointCount); + + ++wBreakpointCount; + } + return TRUE; + + case IDC_BREAKEDIT_DELETE: + // scan all breakpoints from top + for (nItem = wBreakpointCount-1; nItem >= 0; --nItem) + { + // item selected + if (SendMessage(hWnd,LB_GETSEL,nItem,0) > 0) + { + INT j; + + // get breakpoint index + i = (INT) SendMessage(hWnd,LB_GETITEMDATA,nItem,0); + SendMessage(hWnd,LB_DELETESTRING,nItem,0); + --wBreakpointCount; + + // update remaining list box references + for (j = 0; j < wBreakpointCount; ++j) + { + INT k = (INT) SendMessage(hWnd,LB_GETITEMDATA,j,0); + if (k > i) SendMessage(hWnd,LB_SETITEMDATA,j,k-1); + } + + // remove breakpoint from breakpoint table + for (++i; i <= wBreakpointCount; ++i) + sBreakpoint[i-1] = sBreakpoint[i]; + } + } + return TRUE; + + case IDCANCEL: + EndDialog(hDlg,IDCANCEL); + return TRUE; + } + + case WM_VKEYTOITEM: + if(LOWORD(wParam) == VK_SPACE) + { + hWnd = GetDlgItem(hDlg,IDC_BREAKEDIT_WND); + for (nItem = 0; nItem < wBreakpointCount; ++nItem) + { + // item selected + if (SendMessage(hWnd,LB_GETSEL,nItem,0) > 0) + ToggleBreakpointItem(hWnd,nItem); + } + return -2; + } + return -1; // default action + + case WM_DRAWITEM: + if (wParam == IDC_BREAKEDIT_WND) return OnDrawBreakWnd((LPDRAWITEMSTRUCT) lParam); + break; + + case WM_MEASUREITEM: + hDC = GetDC(hDlg); + + // GetTextMetrics from "Courier New 8" font + hFont = CreateFont(-MulDiv(8,GetDeviceCaps(hDC, LOGPIXELSY),72),0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET, + OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FF_DONTCARE,_T("Courier New")); + + hFont = SelectObject(hDC,hFont); + GetTextMetrics(hDC,&tm); + hFont = SelectObject(hDC,hFont); + DeleteObject(hFont); + + ((LPMEASUREITEMSTRUCT) lParam)->itemHeight = tm.tmHeight; + + ReleaseDC(hDlg,hDC); + return TRUE; + } + return FALSE; + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnEditBreakpoint(HWND hDlg) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_BREAKEDIT), hDlg, (DLGPROC)EditBreakpoint) == -1) + AbortMessage(_T("Edit Breakpoint Dialog Box Creation Error !")); + + // update code window + InvalidateRect(GetDlgItem(hDlg,IDC_DEBUG_CODE),NULL,TRUE); + return -1; +} + + +//################ +//# +//# Last Instruction dialog box +//# +//################ + +// +// view last instructions +// +static INT_PTR CALLBACK InfoIntr(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + HWND hWnd; + TCHAR szBuffer[64]; + LONG lIndex; + WORD i,j; + + switch (message) + { + case WM_INITDIALOG: + // font settings + SendDlgItemMessage(hDlg,IDC_INSTR_TEXT, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_INSTR_CLEAR, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_INSTR_COPY, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDCANCEL, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + + lIndex = 0; // init lIndex + hWnd = GetDlgItem(hDlg,IDC_INSTR_CODE); + SendMessage(hWnd,WM_SETREDRAW,FALSE,0); + SendMessage(hWnd,LB_RESETCONTENT,0,0); + for (i = wInstrRp; i != wInstrWp; i = (i + 1) % wInstrSize) + { + j = wsprintf(szBuffer,_T("%05lX "),pdwInstrArray[i]); + disassemble(pdwInstrArray[i],&szBuffer[j],VIEW_SHORT); + lIndex = (LONG) SendMessage(hWnd,LB_ADDSTRING,0,(LPARAM) szBuffer); + } + SendMessage(hWnd,WM_SETREDRAW,TRUE,0); + SendMessage(hWnd,LB_SETCARETINDEX,lIndex,TRUE); + return TRUE; + case WM_COMMAND: + hWnd = GetDlgItem(hDlg,IDC_INSTR_CODE); + switch(LOWORD(wParam)) + { + case IDC_INSTR_COPY: + CopyItemsToClipboard(hWnd); // copy selected items to clipboard + return TRUE; + case IDC_INSTR_CLEAR: // clear instruction buffer + wInstrRp = wInstrWp; + SendMessage(hWnd,LB_RESETCONTENT,0,0); + return TRUE; + case IDCANCEL: + EndDialog(hDlg,IDCANCEL); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnInfoIntr(HWND hDlg) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_INSTRUCTIONS), hDlg, (DLGPROC)InfoIntr) == -1) + AbortMessage(_T("Last Instructions Dialog Box Creation Error !")); + return 0; +} + +// +// view write only I/O registers +// +static BOOL CALLBACK InfoWoRegister(HWND hDlg, UINT message, DWORD wParam, LONG lParam) +{ + TCHAR szBuffer[8]; + + switch (message) + { + case WM_INITDIALOG: + wsprintf(szBuffer,_T("%05X"),Chipset.start1); + SetDlgItemText(hDlg,IDC_ADDR20_24,szBuffer); + wsprintf(szBuffer,_T("%03X"),Chipset.loffset); + SetDlgItemText(hDlg,IDC_ADDR25_27,szBuffer); + wsprintf(szBuffer,_T("%02X"),Chipset.lcounter); + SetDlgItemText(hDlg,IDC_ADDR28_29,szBuffer); + wsprintf(szBuffer,_T("%05X"),Chipset.start2); + SetDlgItemText(hDlg,IDC_ADDR30_34,szBuffer); + return TRUE; + case WM_COMMAND: + if ((LOWORD(wParam) == IDOK)) + { + EndDialog(hDlg,IDOK); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnInfoWoRegister(HWND hDlg) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_WRITEONLYREG), hDlg, (DLGPROC)InfoWoRegister) == -1) + AbortMessage(_T("Write-Only Register Dialog Box Creation Error !")); + return 0; +} + + +//################ +//# +//# File operations +//# +//################ + +// +// load breakpoint list +// +VOID LoadBreakpointList(HANDLE hFile) // NULL = clear breakpoint list +{ + DWORD lBytesRead = 0; + + // read number of breakpoints + if (hFile) ReadFile(hFile, &wBreakpointCount, sizeof(wBreakpointCount), &lBytesRead, NULL); + if(lBytesRead) // breakpoints found + { + WORD wBreakpointSize; + + // read size of one breakpoint + ReadFile(hFile, &wBreakpointSize, sizeof(wBreakpointSize), &lBytesRead, NULL); + if (lBytesRead == sizeof(wBreakpointSize) && wBreakpointSize == sizeof(sBreakpoint[0])) + { + // read breakpoints + ReadFile(hFile, sBreakpoint, wBreakpointCount * sizeof(sBreakpoint[0]), &lBytesRead, NULL); + _ASSERT(lBytesRead == wBreakpointCount * sizeof(sBreakpoint[0])); + } + else // changed breakpoint structure + { + wBreakpointCount = 0; // clear breakpoint list + } + } + else + { + wBreakpointCount = 0; // clear breakpoint list + } + return; +} + +// +// save breakpoint list +// +VOID SaveBreakpointList(HANDLE hFile) +{ + if (wBreakpointCount) // defined breakpoints + { + DWORD lBytesWritten; + + WORD wBreakpointSize = sizeof(sBreakpoint[0]); + + _ASSERT(hFile); // valid file pointer? + + // write number of breakpoints + WriteFile(hFile, &wBreakpointCount, sizeof(wBreakpointCount), &lBytesWritten, NULL); + _ASSERT(lBytesWritten == sizeof(wBreakpointCount)); + + // write size of one breakpoint + WriteFile(hFile, &wBreakpointSize, sizeof(wBreakpointSize), &lBytesWritten, NULL); + _ASSERT(lBytesWritten == sizeof(wBreakpointSize)); + + // write breakpoints + WriteFile(hFile, sBreakpoint, wBreakpointCount * sizeof(sBreakpoint[0]), &lBytesWritten, NULL); + _ASSERT(lBytesWritten == wBreakpointCount * sizeof(sBreakpoint[0])); + } + return; +} diff --git a/SOURCE/DEBUGGER.H b/SOURCE/DEBUGGER.H new file mode 100644 index 0000000..df3ecf3 --- /dev/null +++ b/SOURCE/DEBUGGER.H @@ -0,0 +1,37 @@ +/* + * debugger.h + * + * This file is part of Emu48 + * + * Copyright (C) 1999 Christoph Gießelink + * + */ + +// breakpoint type definitions +#define BP_EXEC 0x01 // code breakpoint +#define BP_READ 0x02 // read memory breakpoint +#define BP_WRITE 0x04 // write memory breakpoint +#define BP_RPL 0x08 // RPL breakpoint +#define BP_ACCESS (BP_READ|BP_WRITE) // read/write memory breakpoint + +// breakpoint notify definitions +#define BN_ASM 0 // ASM breakpoint +#define BN_RPL 1 // RPL breakpoint +#define BN_ASM_BT 2 // ASM and RPL breakpoint + +// debugger state definitions +#define DBG_SUSPEND -1 +#define DBG_OFF 0 +#define DBG_RUN 1 +#define DBG_STEPINTO 2 +#define DBG_STEPOVER 3 +#define DBG_STEPOUT 4 + +// debugger.c +extern VOID UpdateDbgCycleCounter(VOID); +extern BOOL CheckBreakpoint(DWORD dwAddr, DWORD wRange, UINT nType); +extern VOID NotifyDebugger(INT nType); +extern VOID DisableDebugger(VOID); +extern LRESULT OnToolDebug(VOID); +extern VOID LoadBreakpointList(HANDLE hFile); +extern VOID SaveBreakpointList(HANDLE hFile); diff --git a/SOURCE/DISASM.C b/SOURCE/DISASM.C new file mode 100644 index 0000000..52ce4f6 --- /dev/null +++ b/SOURCE/DISASM.C @@ -0,0 +1,1937 @@ +/* + * Disasm.c + * + * This file is part of Emu48, a ported version of x48 + * + * Copyright (C) 1994 Eddie C. Dost + * Copyright (C) 1998 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" + +#define TAB_SKIP 8 + +BOOL disassembler_mode = HP_MNEMONICS; +WORD disassembler_map = MEM_MAP; + +static LPCTSTR hex[] = +{ + _T("0123456789ABCDEF"), + _T("0123456789abcdef") +}; + +static LPCTSTR opcode_0_tbl[32] = +{ + /* + * HP Mnemonics + */ + _T("RTNSXM"), _T("RTN"), _T("RTNSC"), _T("RTNCC"), + _T("SETHEX"), _T("SETDEC"), _T("RSTK=C"), _T("C=RSTK"), + _T("CLRST"), _T("C=ST"), _T("ST=C"), _T("CSTEX"), + _T("P=P+1"), _T("P=P-1"), _T("(NULL)"), _T("RTI"), + /* + * Class Mnemonics + */ + _T("rtnsxm"), _T("rtn"), _T("rtnsc"), _T("rtncc"), + _T("sethex"), _T("setdec"), _T("push"), _T("pop"), + _T("clr.3 st"), _T("move.3 st, c"), _T("move.3 c, st"), _T("exg.3 c, st"), + _T("inc.1 p"), _T("dec.1 p"), _T("(null)"), _T("rti") +}; + +static LPCTSTR op_str_0[16] = +{ + /* + * HP Mnemonics + */ + _T("A=A%cB"), _T("B=B%cC"), _T("C=C%cA"), _T("D=D%cC"), + _T("B=B%cA"), _T("C=C%cB"), _T("A=A%cC"), _T("C=C%cD"), + /* + * Class Mnemonics + */ + _T("b, a"), _T("c, b"), _T("a, c"), _T("c, d"), + _T("a, b"), _T("b, c"), _T("c, a"), _T("d, c") +}; + +static LPCTSTR op_str_1[16] = +{ + /* + * HP Mnemonics + */ + _T("DAT0=A"), _T("DAT1=A"), _T("A=DAT0"), _T("A=DAT1"), + _T("DAT0=C"), _T("DAT1=C"), _T("C=DAT0"), _T("C=DAT1"), + /* + * Class Mnemonics + */ + _T("a, (d0)"), _T("a, (d1)"), _T("(d0), a"), _T("(d1), a"), + _T("c, (d0)"), _T("c, (d1)"), _T("(d0), c"), _T("(d1), c") +}; + +static LPCTSTR in_str_80[32] = +{ + /* + * HP Mnemonics + */ + _T("OUT=CS"), _T("OUT=C"), _T("A=IN"), _T("C=IN"), + _T("UNCNFG"), _T("CONFIG"), _T("C=ID"), _T("SHUTDN"), + NULL, _T("C+P+1"), _T("RESET"), _T("BUSCC"), + NULL, NULL, _T("SREQ?"), NULL, + /* + * Class Mnemonics + */ + _T("move.s c, out"), _T("move.3 c, out"), _T("move.4 in, a"), _T("move.4 in, c"), + _T("uncnfg"), _T("config"), _T("c=id"), _T("shutdn"), + NULL, _T("add.a p+1, c"), _T("reset"), _T("buscc"), + NULL, NULL, _T("sreq?"), NULL +}; + +static LPCTSTR in_str_808[32] = +{ + /* + * HP Mnemonics + */ + _T("INTON"), NULL, NULL, _T("BUSCB"), + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + _T("PC=(A)"), _T("BUSCD"), _T("PC=(C)"), _T("INTOFF"), + /* + * Class Mnemonics + */ + _T("inton"), NULL, NULL, _T("buscb"), + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + _T("jmp (a)"), _T("buscd"), _T("jmp (c)"), _T("intoff") +}; + +static LPCTSTR op_str_81[8] = +{ + /* + * HP Mnemonics + */ + _T("A"), _T("B"), _T("C"), _T("D"), + /* + * Class Mnemonics + */ + _T("a"), _T("b"), _T("c"), _T("d") +}; + +static LPCTSTR in_str_81b[32] = +{ + /* + * HP Mnemonics + */ + NULL, NULL, _T("PC=A"), _T("PC=C"), + _T("A=PC"), _T("C=PC"), _T("APCEX"), _T("CPCEX"), + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + /* + * Class Mnemonics + */ + NULL, NULL, _T("jmp a"), _T("jmp c"), + _T("move.a pc, a"), _T("move.a pc, c"), _T("exg.a a, pc"), _T("exg.a c, pc"), + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL +}; + +static LPCTSTR in_str_9[16] = +{ + /* + * HP Mnemonics + */ + _T("="), _T("#"), _T("="), _T("#"), + _T(">"), _T("<"), _T(">="), _T("<="), + /* + * Class Mnemonics + */ + _T("eq"), _T("ne"), _T("eq"), _T("ne"), + _T("gt"), _T("lt"), _T("ge"), _T("le") +}; + +static LPCTSTR op_str_9[16] = +{ + /* + * HP Mnemonics + */ + _T("?A%sB"), _T("?B%sC"), _T("?C%sA"), _T("?D%sC"), + _T("?A%s0"), _T("?B%s0"), _T("?C%s0"), _T("?D%s0"), + /* + * Class Mnemonics + */ + _T("a, b"), _T("b, c"), _T("c, a"), _T("d, c"), + _T("a, 0"), _T("b, 0"), _T("c, 0"), _T("d, 0") +}; + +static LPCTSTR op_str_af[32] = +{ + /* + * HP Mnemonics + */ + _T("A=A%sB"), _T("B=B%sC"), _T("C=C%sA"), _T("D=D%sC"), + _T("A=A%sA"), _T("B=B%sB"), _T("C=C%sC"), _T("D=D%sD"), + _T("B=B%sA"), _T("C=C%sB"), _T("A=A%sC"), _T("C=C%sD"), + _T("A=B%sA"), _T("B=C%sB"), _T("C=A%sC"), _T("D=C%sD"), + /* + * Class Mnemonics + */ + _T("b, a"), _T("c, b"), _T("a, c"), _T("c, d"), + _T("a, a"), _T("b, b"), _T("c, c"), _T("d, d"), + _T("a, b"), _T("b, c"), _T("c, a"), _T("d, c"), + _T("b, a"), _T("c, b"), _T("a, c"), _T("c, d") +}; + +static LPCTSTR hp_reg_1_af = _T("ABCDABCDBCACABAC"); +static LPCTSTR hp_reg_2_af = _T("0000BCACABCDBCCD"); + +static LPCTSTR field_tbl[32] = +{ + /* + * HP Mnemonics + */ + _T("P"), _T("WP"), _T("XS"), _T("X"), + _T("S"), _T("M"), _T("B"), _T("W"), + _T("P"), _T("WP"), _T("XS"), _T("X"), + _T("S"), _T("M"), _T("B"), _T("A"), + /* + * Class Mnemonics + */ + _T(".p"), _T(".wp"), _T(".xs"), _T(".x"), + _T(".s"), _T(".m"), _T(".b"), _T(".w"), + _T(".p"), _T(".wp"), _T(".xs"), _T(".x"), + _T(".s"), _T(".m"), _T(".b"), _T(".a") +}; + +static LPCTSTR hst_bits[8] = +{ + /* + * HP Mnemonics + */ + _T("XM"), _T("SB"), _T("SR"), _T("MP"), + /* + * Class Mnemonics + */ + _T("xm"), _T("sb"), _T("sr"), _T("mp"), +}; + + +// static functions + +static BYTE rn_map (DWORD *p) +{ + BYTE byVal; + + Npeek(&byVal, *p, 1); + *p = ++(*p) & 0xFFFFF; + return byVal; +} + +static BYTE rn_rom (DWORD *p) +{ + DWORD d = *p; + + *p = ++(*p) & (dwRomSize - 1); + + _ASSERT(d < dwRomSize); + return *(pbyRom + d); +} + +static BYTE rn_ram (DWORD *p) +{ + DWORD d = *p; + + *p = ++(*p) & (Chipset.Port0Size * 2048 - 1); + + _ASSERT(d < Chipset.Port0Size * 2048); + return *(Chipset.Port0 + d); +} + +static BYTE rn_port1 (DWORD *p) +{ + DWORD d = *p; + + *p = ++(*p) & (Chipset.Port1Size * 2048 - 1); + + _ASSERT(d < Chipset.Port1Size * 2048); + return *(Chipset.Port1 + d); +} + +static BYTE rn_port2 (DWORD *p) +{ + BYTE *pbyVal; + DWORD d = *p; + + if (Chipset.Port2Size) // HP39/40G, HP49G + { + *p = ++(*p) & (Chipset.Port2Size * 2048 - 1); + + _ASSERT(d < Chipset.Port2Size * 2048); + pbyVal = Chipset.Port2; + } + else // HP48SX/GX + { + *p = ++(*p) & (((dwPort2Mask + 1) << 18) - 1); + + _ASSERT(d < ((dwPort2Mask + 1) << 18)); + pbyVal = pbyPort2; + } + return *(pbyVal + d); +} + +static BYTE read_nibble (DWORD *p) +{ + BYTE (*pnread[])(DWORD *) = { rn_map, rn_rom, rn_ram, rn_port1, rn_port2 }; + + _ASSERT(disassembler_map < ARRAYSIZEOF(pnread)); + return pnread[disassembler_map](p); +} + +// general functions + +static int read_int (DWORD *addr, int n) +{ + int i, t; + + for (i = 0, t = 0; i < n; i++) + t |= read_nibble (addr) << (i * 4); + + return t; +} + +static LPTSTR append_str (LPTSTR buf, LPCTSTR str) +{ + while ((*buf = *str++)) + buf++; + return buf; +} + +static LPTSTR append_tab (LPTSTR buf) +{ + int n; + LPTSTR p; + + n = lstrlen (buf); + p = &buf[n]; + n = TAB_SKIP - (n % TAB_SKIP); + while (n--) + *p++ = _T(' '); + *p = 0; + return p; +} + +static LPTSTR append_field (LPTSTR buf, BYTE fn) +{ + return append_str (buf, field_tbl[fn + 16 * disassembler_mode]); +} + +static LPTSTR append_imm_nibble (LPTSTR buf, DWORD *addr, int n) +{ + int i; + TCHAR t[16]; + + if (disassembler_mode == CLASS_MNEMONICS) + { + *buf++ = _T('#'); + if (n > 1) + *buf++ = _T('$'); + } + else // HP Mnemonics + { + if (n > 1) // hex mode + *buf++ = _T('#'); // insert hex header + } + if (n > 1) + { + for (i = 0; i < n; i++) + t[i] = hex[disassembler_mode][read_nibble (addr)]; + for (i = n - 1; i >= 0; i--) + { + *buf++ = t[i]; + } + *buf = 0; + } + else + { + wsprintf (t, _T("%d"), read_nibble (addr)); + buf = append_str (buf, t); + } + return buf; +} + +static LPTSTR append_addr (LPTSTR buf, DWORD addr) +{ + int shift; + long mask; + + if (disassembler_mode == CLASS_MNEMONICS) + { + *buf++ = _T('$'); + } + for (mask = 0xf0000, shift = 16; mask != 0; mask >>= 4, shift -= 4) + *buf++ = hex[disassembler_mode][(addr & mask) >> shift]; + *buf = 0; + return buf; +} + +static LPTSTR append_r_addr (LPTSTR buf, DWORD * pc, long disp, int n, int offset) +{ + long sign; + + sign = 1 << (n * 4 - 1); + if (disp & sign) + disp |= ~(sign - 1); + *pc += disp; + + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (disp < 0) + { + buf = append_str(buf, _T("-")); + disp = -disp - offset; + } + else + { + buf = append_str(buf, _T("+")); + disp += offset; + } + buf = append_addr(buf, disp); + break; + case CLASS_MNEMONICS: + if (disp < 0) + { + buf = append_str(buf, _T("-")); + disp = -disp - offset; + } + else + { + buf = append_str(buf, _T("+")); + disp += offset; + } + buf = append_addr(buf, disp); + break; + default: + buf = append_str (buf, _T("Unknown disassembler mode")); + break; + } + return buf; +} + +static LPTSTR append_pc_comment (LPTSTR buf, DWORD pc, BOOL view) +{ + LPTSTR p = buf; + + if (view == VIEW_LONG) // output of address in remarks + { + while (lstrlen (buf) < 4 * TAB_SKIP) + p = append_tab (buf); + + switch (disassembler_mode) + { + case HP_MNEMONICS: + p = append_str (p, _T("# Address: ")); + p = append_addr (p, pc); + break; + case CLASS_MNEMONICS: + p = append_str (p, _T("; address: ")); + p = append_addr (p, pc); + break; + default: + p = append_str (p, _T("Unknown disassembler mode")); + break; + } + } + else // output of address in brackets + { + while (*p) ++p; + p = append_str (p, _T(" [")); + p = append_addr (p, pc); + p = append_str (p, _T("]")); + } + return p; +} + +static LPTSTR append_hst_bits (LPTSTR buf, int n) +{ + int i; + LPTSTR p = buf; + + switch (disassembler_mode) + { + case HP_MNEMONICS: + for (i = 0; i < 4; i++) + if (n & (1 << i)) + { + p = append_str (p, hst_bits[i + 4 * disassembler_mode]); + } + break; + case CLASS_MNEMONICS: + while (lstrlen (buf) < 4 * TAB_SKIP) + p = append_tab (buf); + p = &buf[lstrlen (buf)]; + p = append_str (p, _T("; hst bits: ")); + + for (buf = p, i = 0; i < 4; i++) + if (n & (1 << i)) + { + if (p != buf) + p = append_str (p, _T(", ")); + p = append_str (p, hst_bits[i + 4 * disassembler_mode]); + } + break; + default: + p = append_str (p, _T("Unknown disassembler mode")); + break; + } + return p; +} + +static LPTSTR disasm_1 (DWORD *addr, LPTSTR out) +{ + BYTE n; + BYTE fn; + LPTSTR p; + TCHAR buf[20]; + TCHAR c; + + p = out; + switch (n = read_nibble (addr)) + { + case 0: + case 1: + fn = read_nibble (addr); + c = (fn < 8); // flag for operand register + fn = (fn & 7); // get register number + if (fn > 4) // illegal opcode + break; // no output + switch (disassembler_mode) + { + case HP_MNEMONICS: + c = (TCHAR) (c ? _T('A') : _T('C')); + if (n == 0) + wsprintf (buf, _T("R%d=%c"), fn, c); + else + wsprintf (buf, _T("%c=R%d"), c, fn); + p = append_str (out, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("move.w")); + p = append_tab (out); + c = (TCHAR) (c ? _T('a') : _T('c')); + if (n == 0) + wsprintf (buf, _T("%c, r%d"), c, fn); + else + wsprintf (buf, _T("r%d, %c"), fn, c); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 2: + fn = read_nibble (addr); + c = (fn < 8); // flag for operand register + fn = (fn & 7); // get register number + if (fn > 4) // illegal opcode + break; // no output + switch (disassembler_mode) + { + case HP_MNEMONICS: + c = (TCHAR) (c ? _T('A') : _T('C')); + wsprintf (buf, _T("%cR%dEX"), c, fn); + p = append_str (out, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("exg.w")); + p = append_tab (out); + c = (TCHAR) (c ? _T('a') : _T('c')); + wsprintf (buf, _T("%c, r%d"), c, fn); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 3: + n = read_nibble (addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + c = (n & 4) ? _T('C') : _T('A'); + if (n & 2) + { + if (n < 8) + { + wsprintf (buf, _T("%cD%dEX"), c, (n & 1)); + } + else + { + wsprintf (buf, _T("%cD%dXS"), c, (n & 1)); + } + } + else + { + if (n < 8) + { + wsprintf (buf, _T("D%d=%c"), (n & 1), c); + } + else + { + wsprintf (buf, _T("D%d=%cS"), (n & 1), c); + } + } + p = append_str (out, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, (n & 2) ? _T("exg.") : _T("move.")); + p = append_str (p, (n < 8) ? _T("a") : _T("4")); + p = append_tab (out); + c = (n & 4) ? _T('c') : _T('a'); + wsprintf (buf, _T("%c, d%d"), c, (n & 1)); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 4: + case 5: + fn = read_nibble (addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + p = append_str (out, op_str_1[(fn & 7) + 8 * disassembler_mode]); + p = append_tab (out); + if (n == 4) + { + p = append_str (p, (fn < 8) ? _T("A") : _T("B")); + } + else + { + n = read_nibble (addr); + if (fn < 8) + { + p = append_field (p, n); + } + else + { + wsprintf (buf, _T("%d"), n + 1); + p = append_str (p, buf); + } + } + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("move")); + if (n == 4) + { + p = append_str (p, _T(".")); + p = append_str (p, (fn < 8) ? _T("a") : _T("b")); + } + else + { + n = read_nibble (addr); + if (fn < 8) + { + p = append_field (p, n); + } + else + { + wsprintf (buf, _T(".%d"), n + 1); + p = append_str (p, buf); + } + } + p = append_tab (out); + p = append_str (p, op_str_1[(fn & 7) + 8 * disassembler_mode]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 6: + case 7: + case 8: + case 0xc: + fn = read_nibble (addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (n == 6 || n == 8) + p = append_str (out, _T("D0=D0")); + else + p = append_str (out, _T("D1=D1")); + if (n < 8) + p = append_str (p, _T("+")); + else + p = append_str (p, _T("-")); + p = append_tab (out); + wsprintf (buf, _T("%d"), fn + 1); + p = append_str (p, buf); + break; + case CLASS_MNEMONICS: + if (n < 8) + p = append_str (out, _T("add.a")); + else + p = append_str (out, _T("sub.a")); + p = append_tab (out); + wsprintf (buf, _T("#%d, "), fn + 1); + p = append_str (p, buf); + if (n == 6 || n == 8) + p = append_str (p, _T("d0")); + else + p = append_str (p, _T("d1")); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 9: + case 0xa: + case 0xb: + case 0xd: + case 0xe: + case 0xf: + c = (TCHAR) ((n < 0xd) ? _T('0') : _T('1')); + switch (n & 3) + { + case 1: + n = 2; + break; + case 2: + n = 4; + break; + case 3: + n = 5; + break; + } + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("D%c=(%d)"), c, n); + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, addr, n); + break; + case CLASS_MNEMONICS: + if (n == 5) + { + wsprintf (buf, _T("move.a")); + } + else + if (n == 4) + { + wsprintf (buf, _T("move.as")); + } + else + { + wsprintf (buf, _T("move.b")); + } + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, addr, n); + wsprintf (buf, _T(", d%c"), c); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + default: + break; + } + return p; +} + +static LPTSTR disasm_8 (DWORD *addr, LPTSTR out, BOOL view) +{ + BYTE n; + BYTE fn; + LPTSTR p = out; + TCHAR c; + TCHAR buf[20]; + DWORD disp, pc; + + fn = read_nibble (addr); + switch (fn) + { + case 0: + n = read_nibble (addr); + if (NULL != (p = (LPTSTR) in_str_80[n + 16 * disassembler_mode])) + { + p = append_str (out, p); + return p; + } + switch (n) + { + case 8: + fn = read_nibble (addr); + if (NULL != (p = (LPTSTR) in_str_808[fn + 16 * disassembler_mode])) + { + p = append_str (out, p); + return p; + } + switch (fn) + { + case 1: + n = read_nibble (addr); + if (n == 0) + { + switch (disassembler_mode) + { + case HP_MNEMONICS: + p = append_str (out, _T("RSI")); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("rsi")); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + } + else + p = out; // illegal opcode, no output + break; + case 2: + n = read_nibble (addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (n < 5) + { + wsprintf (buf, _T("LA(%d)"), n + 1); + } + else + { + wsprintf (buf, _T("LAHEX")); + } + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, addr, n + 1); + break; + case CLASS_MNEMONICS: + wsprintf (buf, _T("move.%d"), n + 1); + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, addr, n + 1); + wsprintf (buf, _T(", a.p")); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 4: + case 5: + case 8: + case 9: + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("%cBIT=%d"), (fn & 8) ? _T('C') : _T('A'), + (fn & 1) ? 1 : 0); + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, addr, 1); + break; + case CLASS_MNEMONICS: + p = append_str (out, (fn & 1) ? _T("bset") : _T("bclr")); + p = append_tab (out); + p = append_imm_nibble (p, addr, 1); + p = append_str (p, (fn & 8) ? _T(", c") : _T(", a")); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 6: + case 7: + case 0xa: + case 0xb: + n = read_nibble (addr); + pc = *addr; + disp = read_int (addr, 2); + + switch (disassembler_mode) + { + case HP_MNEMONICS: + c = (TCHAR) ((fn < 0xa) ? _T('A') : _T('C')); + wsprintf (buf, _T("?%cBIT=%d"), c, (fn & 1) ? 1 : 0); + p = append_str (out, buf); + p = append_tab (out); + wsprintf (buf, _T("%d"), n); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", GOYES ")); + p = append_r_addr (p, &pc, disp, 2, 5); + p = append_pc_comment (out, pc, view); + } + else + p = append_str (p, _T(", RTNYES")); + break; + case CLASS_MNEMONICS: + c = (TCHAR) ((fn < 0xa) ? _T('a') : _T('c')); + p = append_str (out, (disp == 0) ? _T("rt") : _T("b")); + p = append_str (p, (fn & 1) ? _T("bs") : _T("bc")); + p = append_tab (out); + wsprintf (buf, _T("#%d, %c"), n, c); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", ")); + p = append_r_addr (p, &pc, disp, 2, 5); + p = append_pc_comment (out, pc, view); + } + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + default: + break; + } + break; + + case 0xc: + case 0xd: + case 0xf: + fn = read_nibble (addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, (n == 0xf) ? _T("%c%cEX") : _T("%c=%c"), + (n == 0xd) ? _T('P') : _T('C'), (n == 0xd) ? _T('C') : _T('P')); + p = append_str (out, buf); + p = append_tab (out); + wsprintf (buf, _T("%d"), fn); + p = append_str (p, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, (n == 0xf) ? _T("exg.1") : _T("move.1")); + p = append_tab (out); + wsprintf (buf, (n == 0xd) ? _T("p, c.%d") : _T("c.%d, p"), fn); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + default: + break; + } + break; + + case 1: + switch (n = read_nibble (addr)) + { + case 0: + case 1: + case 2: + case 3: + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("%sSLC"), op_str_81[(n & 3) + 4 * disassembler_mode]); + p = append_str (out, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("rol.w")); + p = append_tab (out); + p = append_str (p, _T("#4, ")); + p = append_str (p, op_str_81[(n & 3) + 4 * disassembler_mode]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 4: + case 5: + case 6: + case 7: + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("%sSRC"), op_str_81[(n & 3) + 4 * disassembler_mode]); + p = append_str (out, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("ror.w")); + p = append_tab (out); + p = append_str (p, _T("#4, ")); + p = append_str (p, op_str_81[(n & 3) + 4 * disassembler_mode]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 8: + fn = read_nibble (addr); // get number + n = read_nibble (addr); // get register selector + if ((n & 7) > 3) // illegal opcode + break; // no output + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("%s=%s%cCON"), + op_str_81[(n & 3) + 4 * disassembler_mode], + op_str_81[(n & 3) + 4 * disassembler_mode], + (n < 8) ? _T('+') : _T('-')); + p = append_str (out, buf); + p = append_tab (out); + p = append_field (p, fn); + fn = read_nibble (addr); + wsprintf (buf, _T(", %d"), fn + 1); + p = append_str (p, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, (n < 8) ? _T("add") : _T("sub")); + p = append_field (p, fn); + p = append_tab (out); + fn = read_nibble (addr); + wsprintf (buf, _T("#%d, "), fn + 1); + p = append_str (p, buf); + p = append_str (p, op_str_81[(n & 3) + 4 * disassembler_mode]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 9: + fn = read_nibble (addr); // get field selector + n = read_nibble (addr); // get register selector + if (n > 3) // illegal opcode + break; // no output + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("%sSRB.F"), op_str_81[n + 4 * disassembler_mode]); + p = append_str (out, buf); + p = append_tab (out); + p = append_field (p, fn); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("lsr")); + p = append_field (p, fn); + p = append_tab (out); + p = append_str (p, _T("#1, ")); + p = append_str (p, op_str_81[n + 4 * disassembler_mode]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 0xa: + fn = read_nibble (addr); + n = read_nibble (addr); + if (n > 2) // illegal opcode + break; // no output + c = (TCHAR) read_nibble (addr); + if (((int) c & 7) > 4) // illegal opcode + break; // no output + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (n == 2) + { + wsprintf (buf, _T("%cR%dEX.F"), ((int) c < 8) ? _T('A') : _T('C'), + (int) c & 7); + } + else + if (n == 1) + { + wsprintf (buf, _T("%c=R%d.F"), ((int) c < 8) ? _T('A') : _T('C'), + (int) c & 7); + } + else + { + wsprintf (buf, _T("R%d=%c.F"), (int) c & 7, + ((int) c < 8) ? _T('A') : _T('C')); + } + p = append_str (out, buf); + p = append_tab (out); + p = append_field (p, fn); + break; + case CLASS_MNEMONICS: + p = append_str (out, (n == 2) ? _T("exg") : _T("move")); + p = append_field (p, fn); + p = append_tab (out); + if (n == 1) + { + wsprintf (buf, _T("r%d"), (int) c & 7); + p = append_str (p, buf); + } + else + p = append_str (p, ((int) c < 8) ? _T("a") : _T("c")); + p = append_str (p, _T(", ")); + if (n == 1) + p = append_str (p, ((int) c < 8) ? _T("a") : _T("c")); + else + { + wsprintf (buf, _T("r%d"), (int) c & 7); + p = append_str (p, buf); + } + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 0xb: + n = read_nibble (addr); + if ((n < 2) || (n > 7)) // illegal opcode + break; // no output + + p = append_str (out, in_str_81b[n + 16 * disassembler_mode]); + break; + + case 0xc: + case 0xd: + case 0xe: + case 0xf: + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("%sSRB"), op_str_81[(n & 3) + 4 * disassembler_mode]); + p = append_str (out, buf); + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("lsr.w")); + p = append_tab (out); + p = append_str (p, _T("#1, ")); + p = append_str (p, op_str_81[(n & 3) + 4 * disassembler_mode]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + default: + break; + } + break; + + case 2: + n = read_nibble (addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (n == 0xf) + { + p = append_str (out, _T("CLRHST")); + } + else + { + // when not only one bit is set the HS=0 opcode is used + if (n != 1 && n != 2 && n != 4 && n != 8) + { + p = append_str (out, _T("HS=0")); + p = append_tab (out); + wsprintf (buf, _T("%d"), n); + p = append_str (p, buf); + } + else + { + p = append_hst_bits (out, n); + p = append_str (p, _T("=0")); + } + } + break; + case CLASS_MNEMONICS: + p = append_str (out, _T("clr.1")); + p = append_tab (out); + wsprintf (buf, _T("#%d, hst"), n); + p = append_str (p, buf); + p = append_hst_bits (out, n); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 3: + n = read_nibble (addr); + pc = *addr; + disp = read_int (addr, 2); + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (n != 1 && n != 2 && n != 4 && n != 8) + { + p = append_str (out, _T("?HS=0")); + p = append_tab (out); + wsprintf (buf, _T("%d, "), n); + p = append_str (p, buf); + } + else + { + p = append_str (out, _T("?")); + p = append_hst_bits (p, n); + p = append_str (p, _T("=0")); + p = append_tab (out); + } + if (disp != 0) + { + p = append_str (p, _T("GOYES ")); + p = append_r_addr (p, &pc, disp, 2, 3); + p = append_pc_comment (out, pc, view); + } + else + p = append_str (p, _T("RTNYES")); + break; + case CLASS_MNEMONICS: + p = append_str (out, (disp == 0) ? _T("rt") : _T("b")); + p = append_str (p, _T("eq.1")); + p = append_tab (out); + wsprintf (buf, _T("#%d, hst"), n); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", ")); + p = append_r_addr (p, &pc, disp, 2, 3); + p = append_pc_comment (out, pc, view); + } + p = append_hst_bits (out, n); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 4: + case 5: + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("ST=%d"), (fn == 4) ? 0 : 1); + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, addr, 1); + break; + case CLASS_MNEMONICS: + p = append_str (out, (fn == 4) ? _T("bclr") : _T("bset")); + p = append_tab (out); + p = append_imm_nibble (p, addr, 1); + p = append_str (p, _T(", st")); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 6: + case 7: + n = read_nibble (addr); + pc = *addr; + disp = read_int (addr, 2); + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("?ST=%d"), (fn == 6) ? 0 : 1); + p = append_str (out, buf); + p = append_tab (out); + wsprintf (buf, _T("%d"), n); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", GOYES ")); + p = append_r_addr (p, &pc, disp, 2, 3); + p = append_pc_comment (out, pc, view); + } + else + p = append_str (p, _T(", RTNYES")); + break; + case CLASS_MNEMONICS: + p = append_str (out, (disp == 0) ? _T("rt") : _T("b")); + p = append_str (p, (fn == 6) ? _T("bc") : _T("bs")); + p = append_tab (out); + wsprintf (buf, _T("#%d, st"), n); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", ")); + p = append_r_addr (p, &pc, disp, 2, 3); + p = append_pc_comment (out, pc, view); + } + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 8: + case 9: + n = read_nibble (addr); + pc = *addr; + disp = read_int (addr, 2); + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, _T("?P%c"), (fn == 8) ? _T('#') : _T('=')); + p = append_str (out, buf); + p = append_tab (out); + wsprintf (buf, _T("%d"), n); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", GOYES ")); + p = append_r_addr (p, &pc, disp, 2, 3); + p = append_pc_comment (out, pc, view); + } + else + p = append_str (p, _T(", RTNYES")); + break; + case CLASS_MNEMONICS: + p = append_str (out, (disp == 0) ? _T("rt") : _T("b")); + p = append_str (p, (fn == 8) ? _T("ne.1") : _T("eq.1")); + p = append_tab (out); + wsprintf (buf, _T("#%d, p"), n); + p = append_str (p, buf); + if (disp != 0) + { + p = append_str (p, _T(", ")); + p = append_r_addr (p, &pc, disp, 2, 3); + p = append_pc_comment (out, pc, view); + } + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 0xc: + case 0xe: + pc = *addr; + if (fn == 0xe) + pc += 4; + disp = read_int (addr, 4); + switch (disassembler_mode) + { + case HP_MNEMONICS: + p = append_str (out, (fn == 0xc) ? _T("GOLONG") : _T("GOSUBL")); + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 4, (fn == 0xc) ? 2 : 6); + p = append_pc_comment (out, pc, view); + break; + case CLASS_MNEMONICS: + p = append_str (out, (fn == 0xc) ? _T("bra.4") : _T("bsr.4")); + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 4, (fn == 0xc) ? 2 : 6); + p = append_pc_comment (out, pc, view); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 0xd: + case 0xf: + pc = read_int (addr, 5); + switch (disassembler_mode) + { + case HP_MNEMONICS: + p = append_str (out, (fn == 0xd) ? _T("GOVLNG") : _T("GOSBVL")); + p = append_tab (out); + p = append_addr (p, pc); + break; + case CLASS_MNEMONICS: + p = append_str (out, (fn == 0xd) ? _T("jmp") : _T("jsr")); + p = append_tab (out); + p = append_addr (p, pc); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + default: + break; + } + return p; +} + + +// public functions + +DWORD disassemble (DWORD addr, LPTSTR out, BOOL view) +{ + BYTE n; + BYTE fn; + LPTSTR p = out; + TCHAR c; + TCHAR buf[20]; + DWORD disp, pc; + + switch (n = read_nibble (&addr)) + { + case 0: + if ((n = read_nibble (&addr)) != 0xe) + { + p = append_str (out, opcode_0_tbl[n + 16 * disassembler_mode]); + break; + } + fn = read_nibble (&addr); + n = read_nibble (&addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + wsprintf (buf, op_str_0[(n & 7) + 8 * HP_MNEMONICS], + (n < 8) ? _T('&') : _T('!')); + p = append_str (out, buf); + p = append_tab (out); + p = append_field (p, fn); + break; + case CLASS_MNEMONICS: + p = append_str (out, (n < 8) ? _T("and") : _T("or")); + p = append_field (p, fn); + p = append_tab (out); + p = append_str (p, op_str_0[(n & 7) + 8 * CLASS_MNEMONICS]); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 1: + p = disasm_1 (&addr, out); + break; + + case 2: + n = read_nibble (&addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + p = append_str (out, _T("P=")); + p = append_tab (out); + wsprintf (buf, _T("%d"), n); + p = append_str (p, buf); + break; + case CLASS_MNEMONICS: + wsprintf (buf, _T("move.1 #%d, p"), n); + p = append_str (out, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 3: + fn = read_nibble (&addr); + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (fn < 5) + { + wsprintf (buf, _T("LC(%d)"), fn + 1); + } + else + { + wsprintf (buf, _T("LCHEX")); + } + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, &addr, fn + 1); + break; + case CLASS_MNEMONICS: + wsprintf (buf, _T("move.%d"), fn + 1); + p = append_str (out, buf); + p = append_tab (out); + p = append_imm_nibble (p, &addr, fn + 1); + wsprintf (buf, _T(", c.p")); + p = append_str (p, buf); + break; + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 4: + case 5: + pc = addr; + disp = read_int (&addr, 2); + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (disp == 2) + { + p = append_str (out, _T("NOP3")); + break; + } + wsprintf (buf, (disp == 0) ? _T("RTN%sC") : _T("GO%sC"), (n == 4) ? _T("") : _T("N")); + p = append_str (out, buf); + if (disp != 0) + { + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 2, 1); + p = append_pc_comment (out, pc, view); + } + break; + + case CLASS_MNEMONICS: + if (disp == 2) + { + p = append_str (out, _T("nop3")); + break; + } + p = append_str (out, (disp == 0) ? _T("rtc") : _T("bc")); + p = append_str (p, (n == 4) ? _T("s") : _T("c")); + if (disp != 0) + { + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 2, 1); + p = append_pc_comment (out, pc, view); + } + break; + + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 6: + pc = addr; + disp = read_int (&addr, 3); + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (disp == 3) + { + p = append_str (out, _T("NOP4")); + break; + } + if (disp == 4) + { + p = append_str (out, _T("NOP5")); + break; + } + p = append_str (out, _T("GOTO")); + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 3, 1); + p = append_pc_comment (out, pc, view); + break; + + case CLASS_MNEMONICS: + if (disp == 3) + { + p = append_str (out, _T("nop4")); + break; + } + if (disp == 4) + { + p = append_str (out, _T("nop5")); + break; + } + p = append_str (out, _T("bra.3")); + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 3, 1); + p = append_pc_comment (out, pc, view); + break; + + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 7: + pc = addr + 3; + disp = read_int (&addr, 3); + switch (disassembler_mode) + { + case HP_MNEMONICS: + p = append_str (out, _T("GOSUB")); + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 3, 4); + p = append_pc_comment (out, pc, view); + break; + + case CLASS_MNEMONICS: + p = append_str (out, _T("bsr.3")); + p = append_tab (out); + p = append_r_addr (p, &pc, disp, 3, 4); + p = append_pc_comment (out, pc, view); + break; + + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + case 8: + fn = read_nibble (&addr); /* PEEK */ + --addr; + if (fn != 0xa && fn != 0xb) + { + p = disasm_8 (&addr, out, view); + break; + } + /* Fall through */ + + case 9: + fn = read_nibble (&addr); + if (n == 8) + { + c = (TCHAR) ((fn == 0xa) ? 0 : 1); + fn = 0xf; + } + else + { + c = (TCHAR) ((fn < 8) ? 0 : 1); + fn &= 7; + } + + n = read_nibble (&addr); + pc = addr; + disp = read_int (&addr, 2); + + switch (disassembler_mode) + { + case HP_MNEMONICS: + if ((c == 0) && (n >= 8)) + wsprintf (buf, op_str_9[(n & 3) + 8 * HP_MNEMONICS + 4], + in_str_9[((n >> 2) & 3) + 4 * c + 8 * HP_MNEMONICS]); + else + wsprintf (buf, op_str_9[(n & 3) + 8 * HP_MNEMONICS], + in_str_9[((n >> 2) & 3) + 4 * c + 8 * HP_MNEMONICS]); + p = append_str (out, buf); + p = append_tab (out); + p = append_field (p, fn); + p = append_str (p, _T(", ")); + p = append_str (p, (disp == 0) ? _T("RTNYES") : _T("GOYES ")); + if (disp != 0) + { + p = append_r_addr (p, &pc, disp, 2, 3); + p = append_pc_comment (out, pc, view); + } + break; + + case CLASS_MNEMONICS: + p = append_str (out, (disp == 0) ? _T("rt") : _T("b")); + p = append_str (p, in_str_9[((n >> 2) & 3) + 4 * c + 8 * CLASS_MNEMONICS]); + p = append_field (p, fn); + p = append_tab (out); + if ((c == 0) && (n >= 8)) + p = append_str (p, op_str_9[(n & 3) + 8 * CLASS_MNEMONICS + 4]); + else + p = append_str (p, op_str_9[(n & 3) + 8 * CLASS_MNEMONICS]); + if (disp != 0) + { + p = append_str (p, _T(", ")); + p = append_r_addr (p, &pc, disp, 2, 3); + p = append_pc_comment (out, pc, view); + } + break; + + default: + p = append_str (out, _T("Unknown disassembler mode")); + break; + } + break; + + default: + switch (n) + { + case 0xa: + fn = read_nibble (&addr); + c = (TCHAR) ((fn < 8) ? 0 : 1); + fn &= 7; + disp = 0xa; + break; + case 0xb: + fn = read_nibble (&addr); + c = (TCHAR) ((fn < 8) ? 0 : 1); + fn &= 7; + disp = 0xb; + break; + case 0xc: + case 0xd: + fn = 0xf; + c = (TCHAR) (n & 1); + disp = 0xa; + break; + case 0xe: + case 0xf: + fn = 0xf; + c = (TCHAR) (n & 1); + disp = 0xb; + break; + default: + fn = 0; + disp = 0; + c = 0; + break; + } + + n = read_nibble (&addr); + pc = 0; + + switch (disp) + { + case 0xa: + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (c == 0) + { + if (n < 0xc) + { + p = _T("+"); + } + else + { + p = _T("%c=%c-1"); + pc = 2; + } + } + else + { + if (n < 4) + { + p = _T("%c=0"); + pc = 1; + } + else + if (n >= 0xc) + { + p = _T("%c%cEX"); + pc = 3; + } + else + { + p = _T("%c=%c"); + pc = 3; + } + } + break; + + case CLASS_MNEMONICS: + if (c == 0) + { + if (n < 0xc) + { + p = _T("add"); + } + else + { + p = _T("dec"); + pc = 1; + } + } + else + { + if (n < 4) + { + p = _T("clr"); + pc = 1; + } + else + if (n >= 0xc) + { + p = _T("exg"); + } + else + { + p = _T("move"); + if (n < 8) + n -= 4; + } + } + break; + + default: + p = append_str (out, _T("Unknown disassembler mode")); + return addr; + } + break; + + case 0xb: + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (c == 0) + { + if (n >= 0xc) + { + p = _T("-"); + } + else + if ((n >= 4) && (n <= 7)) + { + p = _T("%c=%c+1"); + pc = 2; + n -= 4; + } + else + { + p = _T("-"); + } + } + else + { + if (n < 4) + { + p = _T("%cSL"); + pc = 1; + } + else + if (n < 8) + { + p = _T("%cSR"); + pc = 1; + } + else + if (n < 0xc) + { + p = _T("%c=-%c"); + pc = 2; + } + else + { + p = _T("%c=-%c-1"); + pc = 2; + } + } + break; + + case CLASS_MNEMONICS: + if (c == 0) + { + if (n >= 0xc) + { + p = _T("subr"); + } + else + if ((n >= 4) && (n <= 7)) + { + p = _T("inc"); + pc = 1; + n -= 4; + } + else + { + p = _T("sub"); + } + } + else + { + pc = 1; + if (n < 4) + { + p = _T("lsl"); + } + else + if (n < 8) + { + p = _T("lsr"); + } + else + if (n < 0xc) + { + p = _T("neg"); + } + else + { + p = _T("not"); + } + } + break; + + default: + p = append_str (out, _T("Unknown disassembler mode")); + return addr; + } + break; + + } + + switch (disassembler_mode) + { + case HP_MNEMONICS: + if (pc == 0) + { + wsprintf (buf, op_str_af[n + 16 * HP_MNEMONICS], p); + } + else + if (pc == 1) + { + wsprintf (buf, p, (n & 3) + _T('A')); + } + else + if (pc == 2) + { + wsprintf (buf, p, (n & 3) + _T('A'), (n & 3) + _T('A')); + } + else + { + wsprintf (buf, p, hp_reg_1_af[n], hp_reg_2_af[n]); + } + p = append_str (out, buf); + p = append_tab (out); + p = append_field (p, fn); + break; + + case CLASS_MNEMONICS: + p = append_str (out, p); + p = append_field (p, fn); + p = append_tab (out); + if (pc == 1) + { + wsprintf (buf, _T("%c"), (n & 3) + _T('a')); + p = append_str (p, buf); + } + else + { + p = append_str (p, op_str_af[n + 16 * CLASS_MNEMONICS]); + } + break; + + default: + p = append_str (p, _T("Unknown disassembler mode")); + break; + } + break; + } + *p = 0; + + return addr; +} + diff --git a/SOURCE/DISPLAY.C b/SOURCE/DISPLAY.C new file mode 100644 index 0000000..a374fa9 --- /dev/null +++ b/SOURCE/DISPLAY.C @@ -0,0 +1,674 @@ +/* + * display.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * Copyright (C) 2002 Christoph Gießelink + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "io.h" +#include "kml.h" + +// #define DEBUG_DISPLAY // switch for DISPLAY debug purpose + +#define NOCOLORSGRAY 8 +#define NOCOLORSBW 2 + +#define B 0x00000000 // black +#define W 0x00FFFFFF // white +#define I 0xFFFFFFFF // ignore + +#define LCD_ROW (36*4) // max. pixel per line + +#define GRAYMASK(c) (((((c)-1)>>1)<<24) \ + |((((c)-1)>>1)<<16) \ + |((((c)-1)>>1)<<8) \ + |((((c)-1)>>1))) + +#define DIBPIXEL(d,p) *(((DWORD*)(d))++) = ((*((DWORD*)(d)) & dwGrayMask) << 1) | (p) + +BOOL bGrayscale = FALSE; // Default is to not emulate grayscale +UINT nBackgroundX = 0; +UINT nBackgroundY = 0; +UINT nBackgroundW = 0; +UINT nBackgroundH = 0; +UINT nLcdX = 0; +UINT nLcdY = 0; +UINT nLcdZoom = 1; +LPBYTE pbyLcd; +HDC hLcdDC = NULL; +HDC hMainDC = NULL; + +static HBITMAP hLcdBitmap; +static HBITMAP hMainBitmap; + +static DWORD Pattern[16]; +static BYTE Buf[36]; + +static DWORD dwGrayMask; + +static DWORD dwKMLColor[64] = // color table loaded by KML script +{ + W,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B, + B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B, + I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, + I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I +}; + +static struct +{ + BITMAPINFOHEADER Lcd_bmih; + RGBQUAD bmiColors[NOCOLORSGRAY]; +} bmiLcd = +{ + {0x28,0/*x*/,0/*y*/,1,8,BI_RGB,0,0,0,NOCOLORSGRAY,0} +}; + +static __inline VOID BuildPattern(VOID) +{ + WORD i,j; + for (i=0; i<16; ++i) + { + Pattern[i] = 0; + for (j=8; j>0; j>>=1) + { + Pattern[i] = (Pattern[i] << 8) | ((i&j) != 0); + } + } + return; +} + +VOID UpdateContrast(BYTE byContrast) +{ + RGBQUAD c,b; + INT i,nColors; + + // table for max. 8 colors + const INT nCAdj[] = { 0, 1, 1, 2, 1, 2, 2, 3 }; + + // when display is off use contrast 0 + if ((Chipset.IORam[BITOFFSET] & DON) == 0) byContrast = 0; + + c = *(RGBQUAD*)&dwKMLColor[byContrast]; // pixel on color + b = *(RGBQUAD*)&dwKMLColor[byContrast+32]; // pixel off color + + // if background color is undefined, use color 0 for compatibility + if (I == *(DWORD*)&b) b = *(RGBQUAD*)&dwKMLColor[0]; + + nColors = bGrayscale ? (NOCOLORSGRAY-1) : (NOCOLORSBW-1); + + _ASSERT(nColors <= ARRAYSIZEOF(nCAdj)); // no. of colors must be smaller than entries in the gray color table + + // fill color palette of bitmap + for (i = 0; i <= nColors; ++i) + { + bmiLcd.bmiColors[i] = b; + bmiLcd.bmiColors[i].rgbRed += ((INT) c.rgbRed - (INT) b.rgbRed) * nCAdj[i] / nCAdj[nColors]; + bmiLcd.bmiColors[i].rgbGreen += ((INT) c.rgbGreen - (INT) b.rgbGreen) * nCAdj[i] / nCAdj[nColors]; + bmiLcd.bmiColors[i].rgbBlue += ((INT) c.rgbBlue - (INT) b.rgbBlue) * nCAdj[i] / nCAdj[nColors]; + } + + // update palette information + _ASSERT(hLcdDC); + SetDIBColorTable(hLcdDC,0,ARRAYSIZEOF(bmiLcd.bmiColors),bmiLcd.bmiColors); + + // recalculate update mask for online gray <-> bw switching + dwGrayMask = bGrayscale ? GRAYMASK(NOCOLORSGRAY) : GRAYMASK(NOCOLORSBW); + return; +} + +VOID SetLcdColor(UINT nId, UINT nRed, UINT nGreen, UINT nBlue) +{ + dwKMLColor[nId&0x3F] = ((nRed&0xFF)<<16)|((nGreen&0xFF)<<8)|(nBlue&0xFF); + return; +} + +VOID CreateLcdBitmap(VOID) +{ + // create LCD bitmap + bmiLcd.Lcd_bmih.biWidth = LCD_ROW; + bmiLcd.Lcd_bmih.biHeight = -SCREENHEIGHT; // CdB for HP: add 64/80 ligne display for apples + _ASSERT(hLcdDC == NULL); + hLcdDC = CreateCompatibleDC(hWindowDC); + _ASSERT(hLcdDC != NULL); + hLcdBitmap = CreateDIBSection(hWindowDC,(BITMAPINFO*)&bmiLcd,DIB_RGB_COLORS,(VOID **)&pbyLcd,NULL,0); + hLcdBitmap = SelectObject(hLcdDC,hLcdBitmap); + _ASSERT(hPalette != NULL); + SelectPalette(hLcdDC,hPalette,FALSE); // set palette for LCD DC + RealizePalette(hLcdDC); // realize palette + BuildPattern(); // build Nibble -> DIB mask pattern + dwGrayMask = bGrayscale ? GRAYMASK(NOCOLORSGRAY) : GRAYMASK(NOCOLORSBW); + UpdateContrast(Chipset.contrast); + return; +} + +VOID DestroyLcdBitmap(VOID) +{ + // set contrast palette to startup colors + WORD i = 0; dwKMLColor[i++] = W; + while(i < 32) dwKMLColor[i++] = B; + while(i < 64) dwKMLColor[i++] = I; + + if (hLcdDC != NULL) + { + // destroy LCD bitmap + DeleteObject(SelectObject(hLcdDC,hLcdBitmap)); + DeleteDC(hLcdDC); + hLcdDC = NULL; + hLcdBitmap = NULL; + } + return; +} + +BOOL CreateMainBitmap(LPCTSTR szFilename) +{ + HPALETTE hAssertPalette; + + _ASSERT(hWindowDC != NULL); + hMainDC = CreateCompatibleDC(hWindowDC); + _ASSERT(hMainDC != NULL); + if (hMainDC == NULL) return FALSE; // quit if failed + hMainBitmap = LoadBitmapFile(szFilename); + if (hMainBitmap == NULL) + { + DeleteDC(hMainDC); + hMainDC = NULL; + return FALSE; + } + hMainBitmap = SelectObject(hMainDC,hMainBitmap); + _ASSERT(hPalette != NULL); + hAssertPalette = SelectPalette(hMainDC,hPalette,FALSE); + _ASSERT(hAssertPalette != NULL); + RealizePalette(hMainDC); + return TRUE; +} + +VOID DestroyMainBitmap(VOID) +{ + if (hMainDC != NULL) + { + // destroy Main bitmap + DeleteObject(SelectObject(hMainDC,hMainBitmap)); + DeleteDC(hMainDC); + hMainDC = NULL; + hMainBitmap = NULL; + } + return; +} + +//**************** +//* +//* LCD functions +//* +//**************** + +VOID UpdateDisplayPointers(VOID) +{ + EnterCriticalSection(&csLcdLock); + { + #if defined DEBUG_DISPLAY + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: Update Display Pointer\n"),Chipset.pc); + OutputDebugString(buffer); + } + #endif + + // calculate display width + Chipset.width = (34 + Chipset.loffset + (Chipset.boffset / 4) * 2) & 0xFFFFFFFE; + Chipset.end1 = Chipset.start1 + MAINSCREENHEIGHT * Chipset.width; + if (Chipset.end1 < Chipset.start1) + { + // calculate first address of main display + Chipset.start12 = Chipset.end1 - Chipset.width; + // calculate last address of main display + Chipset.end1 = Chipset.start1 - Chipset.width; + } + else + { + Chipset.start12 = Chipset.start1; + } + Chipset.end2 = Chipset.start2 + MENUHEIGHT * 34; + } + LeaveCriticalSection(&csLcdLock); + return; +} + +VOID UpdateMainDisplay(VOID) +{ + UINT x, y; + DWORD d = Chipset.start1; + BYTE *p = pbyLcd+(Chipset.d0size*LCD_ROW); // CdB for HP: add 64/80 ligne display for apples + + #if defined DEBUG_DISPLAY + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: Update Main Display\n"),Chipset.pc); + OutputDebugString(buffer); + } + #endif + + if (!(Chipset.IORam[BITOFFSET]&DON)) + { + ZeroMemory(pbyLcd, LCD_ROW * SCREENHEIGHT); + } + else + { + for (y = 0; y < MAINSCREENHEIGHT; ++y) + { + Npeek(Buf,d,36); + for (x=0; x<36; ++x) // every 4 pixel + { + DIBPIXEL(p,Pattern[Buf[x]]); + // check for display buffer overflow + _ASSERT(p <= pbyLcd + LCD_ROW * SCREENHEIGHT); + } + d+=Chipset.width; + } + } + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + // CdB for HP: add 64/80 ligne display for apples + StretchBlt(hWindowDC, + nLcdX, nLcdY+Chipset.d0size*nLcdZoom, + 131*nLcdZoom, MAINSCREENHEIGHT*nLcdZoom, + hLcdDC, + Chipset.boffset, Chipset.d0size, + 131, MAINSCREENHEIGHT, + SRCCOPY); + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + return; +} + +VOID UpdateMenuDisplay(VOID) +{ + UINT x, y; + BYTE *p; + DWORD d = Chipset.start2; + + #if defined DEBUG_DISPLAY + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: Update Menu Display\n"),Chipset.pc); + OutputDebugString(buffer); + } + #endif + + if (!(Chipset.IORam[BITOFFSET]&DON)) return; + if (MENUHEIGHT==0) return; // menu disabled + + // calculate bitmap offset + p = pbyLcd + ((Chipset.d0size+MAINSCREENHEIGHT)*LCD_ROW); // CdB for HP: add 64/80 ligne display for apples + for (y = 0; y < MENUHEIGHT; ++y) + { + Npeek(Buf,d,34); // 34 nibbles are viewed + for (x=0; x<34; ++x) // every 4 pixel + { + DIBPIXEL(p,Pattern[Buf[x]]); + // check for display buffer overflow + _ASSERT(p <= pbyLcd + LCD_ROW * SCREENHEIGHT); + } + // adjust pointer to 36 DIBPIXEL drawing calls + p += (36-34) * sizeof(DWORD); + d+=34; + } + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + // CdB for HP: add 64/80 ligne display for apples + StretchBlt(hWindowDC, + nLcdX, nLcdY+(MAINSCREENHEIGHT+Chipset.d0size)*nLcdZoom, + 131*nLcdZoom, MENUHEIGHT*nLcdZoom, + hLcdDC, + 0, (MAINSCREENHEIGHT+Chipset.d0size), + 131, MENUHEIGHT, + SRCCOPY); + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + return; +} + +// CdB for HP: add header management +VOID RefreshDisp0() +{ + UINT x, y; + BYTE *p; + BYTE* d = Chipset.d0memory; + + #if defined DEBUG_DISPLAY + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: Update header Display\n"),Chipset.pc); + OutputDebugString(buffer); + } + #endif + + if (!(Chipset.IORam[BITOFFSET]&DON)) return; + + // calculate bitmap offset + p = pbyLcd; + for (y = 0; y= (INT)Chipset.d0size && y0 < (INT)(MAINSCREENHEIGHT+Chipset.d0size)); + if (!(y0 >= (INT)Chipset.d0size && y0 < (INT)(MAINSCREENHEIGHT+Chipset.d0size))) return; + + while (s--) // loop for nibbles to write + { + if (x<36) // only fill visible area + { + *p = Pattern[*a]; + } + a++; // next value to write + x++; // next x position + if (((INT) x==lWidth)&&s) // end of display line + { + x = 0; // first coloumn + y++; // next row + if (y == (INT) MAINSCREENHEIGHT+Chipset.d0size) break; + // recalculate bitmap memory position of new line + p = (DWORD*) (pbyLcd+y*LCD_ROW); // CdB for HP: add 64/80 ligne display for apples + } else p++; + } + if (y==y0) y++; + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + StretchBlt(hWindowDC, + nLcdX, nLcdY+y0*nLcdZoom, + 131*nLcdZoom, (y-y0)*nLcdZoom, + hLcdDC, + Chipset.boffset, y0, + 131, y-y0, + SRCCOPY); // CdB for HP: add 64/80 ligne display for apples + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + return; +} + +VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s) +{ + UINT x0, x; + UINT y0, y; + DWORD *p; + + if (bGrayscale) + { + return; + } + + #if defined DEBUG_DISPLAY + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: Write Menu Display %x,%u\n"),Chipset.pc,d,s); + OutputDebugString(buffer); + } + #endif + + if (!(Chipset.IORam[BITOFFSET]&DON)) return; // display off + if (MENUHEIGHT == 0) return; // menu disabled + + d -= Chipset.start2; // nibble offset to DISPADDR (start of display) + y0 = y = (d / 34) + MAINSCREENHEIGHT+Chipset.d0size; // bitmap row + x0 = x = d % 34; // bitmap coloumn + p = (DWORD*)(pbyLcd + y0*LCD_ROW + x0*sizeof(*p)); + + // outside menu display area +// _ASSERT(y0 >= (INT)(Chipset.d0size+MAINSCREENHEIGHT) && y0 < (INT)(SCREENHEIGHT)); + if (!(y0 >= (UINT)(Chipset.d0size+MAINSCREENHEIGHT) && y0 < (UINT)(SCREENHEIGHT))) return; + + while (s--) // loop for nibbles to write + { + if (x<36) // only fill visible area + { + *p = Pattern[*a]; + } + a++; // next value to write + x++; // next x position + if ((x==34)&&s) // end of display line + { + x = 0; // first coloumn + y++; // next row + if (y == SCREENHEIGHTREAL) break; + // recalculate bitmap memory position of new line + p=(DWORD*)(pbyLcd+y*LCD_ROW); // CdB for HP: add 64/80 ligne display for apples + } else p++; + } + if (y==y0) y++; + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + StretchBlt(hWindowDC, + nLcdX, nLcdY+y0*nLcdZoom, + 131*nLcdZoom, (y-y0)*nLcdZoom, + hLcdDC, + 0, y0, + 131, y-y0, + SRCCOPY); // CdB for HP: add 64/80 ligne display for apples + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + return; +} + +VOID UpdateAnnunciators(VOID) +{ + BYTE c; + + c = (BYTE)(Chipset.IORam[ANNCTRL] | (Chipset.IORam[ANNCTRL+1]<<4)); + // switch annunciators off if timer stopped + if ((c & AON) == 0 || (Chipset.IORam[TIMER2_CTRL] & RUN) == 0) + c = 0; + + DrawAnnunciator(1,c&LA1); + DrawAnnunciator(2,c&LA2); + DrawAnnunciator(3,c&LA3); + DrawAnnunciator(4,c&LA4); + DrawAnnunciator(5,c&LA5); + DrawAnnunciator(6,c&LA6); + return; +} + +VOID ResizeWindow(VOID) +{ + RECT rectWindow; + RECT rectClient; + + if (hWnd == NULL) return; // return if window closed + + rectWindow.left = 0; + rectWindow.top = 0; + rectWindow.right = nBackgroundW; + rectWindow.bottom = nBackgroundH; + AdjustWindowRect(&rectWindow, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, TRUE); + SetWindowPos (hWnd, (HWND)NULL, 0, 0, + rectWindow.right - rectWindow.left, + rectWindow.bottom - rectWindow.top, + SWP_NOMOVE | SWP_NOZORDER); + GetClientRect(hWnd, &rectClient); + AdjustWindowRect(&rectClient, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, TRUE); + if (rectClient.bottom < rectWindow.bottom) + { + rectWindow.bottom += (rectWindow.bottom - rectClient.bottom); + SetWindowPos (hWnd, (HWND)NULL, 0, 0, + rectWindow.right - rectWindow.left, + rectWindow.bottom - rectWindow.top, + SWP_NOMOVE | SWP_NOZORDER); + } + + _ASSERT(hWindowDC); // move destination window + SetWindowOrgEx(hWindowDC, nBackgroundX, nBackgroundY, NULL); + InvalidateRect(hWnd,NULL,TRUE); + return; +} + + +#define DISPLAY_FREQ 19 // display update 1/frequency (1/64) in ms + +static LARGE_INTEGER lLcdRef; // reference time for VBL counter + +static UINT uLcdTimerId = 0; + +static BYTE byVblRef = 0; // VBL stop reference + +// LCD line counter calculation +static BYTE F4096Hz(VOID) // get a 6 bit 4096Hz down counter value +{ + LARGE_INTEGER lLC; + + QueryPerformanceCounter(&lLC); // get counter value + + // calculate 4096 Hz frequency down counter value + return -(BYTE)(((lLC.QuadPart - lAppStart.QuadPart) << 12) / lFreq.QuadPart) & 0x3F; +} + +static VOID CALLBACK LcdProc(UINT uEventId, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +{ + EnterCriticalSection(&csLcdLock); + { + UpdateMainDisplay(); // update display + UpdateMenuDisplay(); + RefreshDisp0(); // CdB for HP: add header management + } + LeaveCriticalSection(&csLcdLock); + + QueryPerformanceCounter(&lLcdRef); // actual time + + return; + UNREFERENCED_PARAMETER(uEventId); + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(dwUser); + UNREFERENCED_PARAMETER(dw1); + UNREFERENCED_PARAMETER(dw2); +} + +// LCD line counter calculation +BYTE GetLineCounter(VOID) +{ + LARGE_INTEGER lLC; + BYTE byTime; + + if (!bGrayscale) + { + _ASSERT(byVblRef < 0x40); + return (0x40 + F4096Hz() - byVblRef) & 0x3F; + } + + if (uLcdTimerId == 0) // display off + return ((Chipset.IORam[LINECOUNT+1] & (LC5|LC4)) << 4) | Chipset.IORam[LINECOUNT]; + + QueryPerformanceCounter(&lLC); // get elapsed time since display update + + // elapsed ticks so far + byTime = (BYTE) (((lLC.QuadPart - lLcdRef.QuadPart) << 12) / lFreq.QuadPart); + + if (byTime > 0x3F) byTime = 0x3F; // all counts made + + return 0x3F - byTime; // update display between VBL counter 0x3F-0x3E +} + +VOID StartDisplay(BYTE byInitial) +{ + if (!bGrayscale) + { + // get positive VBL difference between now and stop time + byVblRef = (0x40 + F4096Hz() - byInitial) & 0x3F; + return; + } + + if (uLcdTimerId) // LCD update timer running + return; // -> quit + + if (Chipset.IORam[BITOFFSET]&DON) // display on? + { + QueryPerformanceCounter(&lLcdRef); // actual time of top line + + // adjust startup counter to get the right VBL value + _ASSERT(byInitial <= 0x3F); // line counter value 0 - 63 + lLcdRef.QuadPart -= ((LONGLONG) (0x3F - byInitial) * lFreq.QuadPart) >> 12; + + uLcdTimerId = timeSetEvent(DISPLAY_FREQ,0,(LPTIMECALLBACK)&LcdProc,0,TIME_PERIODIC); + _ASSERT(uLcdTimerId); // test if display update timer started + } + return; +} + +VOID StopDisplay(VOID) +{ + BYTE a[2]; + ReadIO(a,LINECOUNT,2,TRUE); // update VBL at display off time + + if (!bGrayscale) + return; + + if (uLcdTimerId == 0) // timer stopped + return; // -> quit + + timeKillEvent(uLcdTimerId); // stop display update + uLcdTimerId = 0; // set flag display update stopped + + EnterCriticalSection(&csLcdLock); // update to last condition + { + UpdateMainDisplay(); // update display + UpdateMenuDisplay(); + RefreshDisp0(); // CdB for HP: add header management + } + LeaveCriticalSection(&csLcdLock); + return; +} diff --git a/SOURCE/EMU48.C b/SOURCE/EMU48.C new file mode 100644 index 0000000..5c1409c --- /dev/null +++ b/SOURCE/EMU48.C @@ -0,0 +1,1615 @@ +/* + * Emu48.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "io.h" +#include "kml.h" +#include "debugger.h" + +#define VERSION "1.42+" + +#define MAXPORTS 256 // number of COM ports + +// #define MONOCHROME // CF_BITMAP clipboard format + +#ifdef _DEBUG +LPTSTR szNoTitle = _T("Emu48 ")_T(VERSION)_T(" Debug"); +#else +LPTSTR szNoTitle = _T("Emu48 ")_T(VERSION); +#endif +LPTSTR szAppName = _T("Emu48"); // application name for DDE server +LPTSTR szTopic = _T("Stack"); // topic for DDE server +LPTSTR szTitle = NULL; + +static BOOL bOwnCursor = FALSE; + +static const LPCTSTR szLicence = + _T("This program is free software; you can redistribute it and/or modify\r\n") + _T("it under the terms of the GNU General Public License as published by\r\n") + _T("the Free Software Foundation; either version 2 of the License, or\r\n") + _T("(at your option) any later version.\r\n") + _T("\r\n") + _T("This program is distributed in the hope that it will be useful,\r\n") + _T("but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n") + _T("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\r\n") + _T("See the GNU General Public License for more details.\r\n") + _T("\r\n") + _T("You should have received a copy of the GNU General Public License\r\n") + _T("along with this program; if not, write to the Free Software Foundation,\r\n") + _T("Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"); + + +CRITICAL_SECTION csGDILock; // critical section for hWindowDC +CRITICAL_SECTION csLcdLock; // critical section for display update +CRITICAL_SECTION csKeyLock; // critical section for key scan +CRITICAL_SECTION csIOLock; // critical section for I/O access +CRITICAL_SECTION csT1Lock; // critical section for timer1 access +CRITICAL_SECTION csT2Lock; // critical section for timer2 access +CRITICAL_SECTION csTxdLock; // critical section for transmit byte +CRITICAL_SECTION csRecvLock; // critical section for receive byte +CRITICAL_SECTION csSlowLock; // critical section for speed slow down +INT nArgc; // no. of command line arguments +LPCTSTR *ppArgv; // command line arguments +LARGE_INTEGER lFreq; // high performance counter frequency +LARGE_INTEGER lAppStart; // high performance counter value at Appl. start +DWORD idDdeInst; // DDE server id +UINT uCF_HpObj; // DDE clipboard format +HANDLE hHeap; +HANDLE hThread; +DWORD lThreadId; +HANDLE hEventShutdn; // event handle to stop cpu thread + +HINSTANCE hApp = NULL; +HWND hWnd = NULL; +HWND hDlgDebug = NULL; // handle for debugger dialog +HWND hDlgFind = NULL; // handle for debugger find dialog +HWND hDlgProfile = NULL; // handle for debugger profile dialog +HDC hWindowDC = NULL; +HPALETTE hPalette = NULL; +HPALETTE hOldPalette = NULL; // old palette of hWindowDC +HCURSOR hCursorArrow = NULL; +HCURSOR hCursorHand = NULL; +BOOL bClassicCursor = FALSE; // use hand instead of arrow cursor +BOOL bAutoSave = FALSE; +BOOL bAutoSaveOnExit = TRUE; +BOOL bSaveDefConfirm = TRUE; // yes +BOOL bAlwaysDisplayLog = TRUE; +BOOL bLoadObjectWarning = TRUE; + + +//################ +//# +//# Window Status +//# +//################ + +VOID SetWindowTitle(LPCTSTR szString) +{ + if (szTitle) HeapFree(hHeap,0,szTitle); + + _ASSERT(hWnd != NULL); + if (szString) + { + szTitle = DuplicateString(szString); + SetWindowText(hWnd, szTitle); + } + else + { + szTitle = NULL; + SetWindowText(hWnd, szNoTitle); + } + return; +} + +VOID UpdateWindowStatus(VOID) +{ + if (hWnd) // window open + { + // disable stack loading items on HP38G, HP39/40G, HP39G+ + BOOL bStackEnable = cCurrentRomType!='6' && cCurrentRomType!='A' && cCurrentRomType!='E' && cCurrentRomType!='P'; // CdB for HP: add apples + BOOL bRun = nState == SM_RUN || nState == SM_SLEEP; + + UINT uStackEnable = (bRun && bStackEnable) ? MF_ENABLED : MF_GRAYED; + UINT uRun = bRun ? MF_ENABLED : MF_GRAYED; + UINT uBackup = bBackup ? MF_ENABLED : MF_GRAYED; + + HMENU hMenu = GetMenu(hWnd); // get menu handle + + EnableMenuItem(hMenu,ID_FILE_NEW,MF_ENABLED); + EnableMenuItem(hMenu,ID_FILE_OPEN,MF_ENABLED); + EnableMenuItem(hMenu,ID_FILE_SAVE,(bRun && szCurrentFilename[0]) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenu,ID_FILE_SAVEAS,uRun); + EnableMenuItem(hMenu,ID_FILE_CLOSE,uRun); + EnableMenuItem(hMenu,ID_OBJECT_LOAD,uStackEnable); + EnableMenuItem(hMenu,ID_OBJECT_SAVE,uStackEnable); + EnableMenuItem(hMenu,ID_VIEW_COPY,uRun); + EnableMenuItem(hMenu,ID_STACK_COPY,uStackEnable); + EnableMenuItem(hMenu,ID_STACK_PASTE,uStackEnable); + EnableMenuItem(hMenu,ID_VIEW_RESET,uRun); + EnableMenuItem(hMenu,ID_BACKUP_SAVE,uRun); + EnableMenuItem(hMenu,ID_BACKUP_RESTORE,uBackup); + EnableMenuItem(hMenu,ID_BACKUP_DELETE,uBackup); + EnableMenuItem(hMenu,ID_VIEW_SCRIPT,uRun); + EnableMenuItem(hMenu,ID_TOOL_DISASM,uRun); + EnableMenuItem(hMenu,ID_TOOL_DEBUG,(bRun && nDbgState == DBG_OFF) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenu,ID_TOOL_MACRO_RECORD,(bRun && nMacroState == MACRO_OFF) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenu,ID_TOOL_MACRO_PLAY,(bRun && nMacroState == MACRO_OFF) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenu,ID_TOOL_MACRO_STOP,(bRun && nMacroState != MACRO_OFF) ? MF_ENABLED : MF_GRAYED); + } + return; +} + + + +//################ +//# +//# Clipboard Tool +//# +//################ + +VOID CopyItemsToClipboard(HWND hWnd) // save selected Listbox Items to Clipboard +{ + LONG i; + LPINT lpnCount; + + // get number of selections + if ((i = (LONG) SendMessage(hWnd,LB_GETSELCOUNT,0,0)) == 0) + return; // no items selected + + if ((lpnCount = HeapAlloc(hHeap,0,i * sizeof(INT))) != NULL) + { + LPTSTR lpszData; + HANDLE hClipObj; + LONG j,lMem = 0; + + // get indexes of selected items + i = (LONG) SendMessage(hWnd,LB_GETSELITEMS,i,(LPARAM) lpnCount); + for (j = 0;j < i;++j) // scan all selected items + { + // calculate total amount of characters + lMem += (LONG) SendMessage(hWnd,LB_GETTEXTLEN,lpnCount[j],0) + 2; + } + // allocate clipboard data + if ((hClipObj = GlobalAlloc(GMEM_MOVEABLE,(lMem + 1) * sizeof(*lpszData))) != NULL) + { + if ((lpszData = GlobalLock(hClipObj))) + { + for (j = 0;j < i;++j) // scan all selected items + { + lpszData += SendMessage(hWnd,LB_GETTEXT,lpnCount[j],(LPARAM) lpszData); + *lpszData++ = _T('\r'); + *lpszData++ = _T('\n'); + } + *lpszData = 0; // set EOS + GlobalUnlock(hClipObj); // unlock memory + } + + if (OpenClipboard(hWnd)) + { + if (EmptyClipboard()) + #if defined _UNICODE + SetClipboardData(CF_UNICODETEXT,hClipObj); + #else + SetClipboardData(CF_TEXT,hClipObj); + #endif + else + GlobalFree(hClipObj); + CloseClipboard(); + } + else // clipboard open failed + { + GlobalFree(hClipObj); + } + } + HeapFree(hHeap,0,lpnCount); // free item table + } + return; +} + + + +//################ +//# +//# Settings +//# +//################ + +// get R/W state of file +static BOOL IsFileWriteable(LPCTSTR szFilename) +{ + DWORD dwFileAtt; + + BOOL bWriteable = FALSE; + + SetCurrentDirectory(szEmuDirectory); + dwFileAtt = GetFileAttributes(szFilename); + if (dwFileAtt != 0xFFFFFFFF) + bWriteable = ((dwFileAtt & FILE_ATTRIBUTE_READONLY) == 0); + SetCurrentDirectory(szCurrentDirectory); + return bWriteable; +} + +// set listfield for serial combobox +static VOID SetCommList(HWND hDlg,int nIDDlgItem,LPCTSTR szSetting) +{ + HANDLE hComm; + BOOL bAdd; + WORD wCount, wIndex = 1; + TCHAR szBuffer[16]; + + WPARAM wSelect = 0; // set select to disabled + SendDlgItemMessage(hDlg,nIDDlgItem,CB_ADDSTRING,0,(LPARAM) _T(NO_SERIAL)); + + for (wCount = 1;wCount <= MAXPORTS;++wCount) + { + wsprintf(szBuffer,_T("\\\\.\\COM%u"),wCount); + if ((bAdd = (lstrcmp(&szBuffer[4],szSetting) == 0))) + wSelect = wIndex; + + // test if COM port is valid + hComm = CreateFile(szBuffer,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); + if(hComm != INVALID_HANDLE_VALUE) + { + VERIFY(CloseHandle(hComm)); + bAdd = TRUE; + } + + if (bAdd) // add item to combobox + { + SendDlgItemMessage(hDlg,nIDDlgItem,CB_ADDSTRING,0,(LPARAM) &szBuffer[4]); + ++wIndex; + } + } + SendDlgItemMessage(hDlg,nIDDlgItem,CB_SETCURSEL,wSelect,0L); +} + +static INT_PTR CALLBACK SettingsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + LPCTSTR szActPort2Filename = _T(""); + + BOOL bPort2CfgChange = FALSE; + BOOL bPort2AttChange = FALSE; + + switch (message) + { + case WM_INITDIALOG: + // init speed checkbox + CheckDlgButton(hDlg,IDC_REALSPEED,bRealSpeed); + CheckDlgButton(hDlg,IDC_AUTOSAVE,bAutoSave); + CheckDlgButton(hDlg,IDC_AUTOSAVEONEXIT,bAutoSaveOnExit); + CheckDlgButton(hDlg,IDC_OBJECTLOADWARNING,bLoadObjectWarning); + CheckDlgButton(hDlg,IDC_ALWAYSDISPLOG,bAlwaysDisplayLog); + CheckDlgButton(hDlg,IDC_GRAYSCALE,bGrayscale); + + // set disassebler mode + CheckDlgButton(hDlg,(disassembler_mode == HP_MNEMONICS) ? IDC_DISASM_HP : IDC_DISASM_CLASS,BST_CHECKED); + + // set sound slider + SendDlgItemMessage(hDlg,IDC_SOUND_SLIDER,TBM_SETRANGE,FALSE,MAKELONG(0,255)); + SendDlgItemMessage(hDlg,IDC_SOUND_SLIDER,TBM_SETTICFREQ,256/8,0); + SendDlgItemMessage(hDlg,IDC_SOUND_SLIDER,TBM_SETPOS,TRUE,dwWaveVol); + + // set sound radio button + CheckDlgButton(hDlg,bWaveBeep ? IDC_SOUND_WAVE : IDC_SOUND_SPEAKER,BST_CHECKED); + EnableWindow(GetDlgItem(hDlg,IDC_SOUND_SLIDER),bWaveBeep); + + // set combobox parameter + SetCommList(hDlg,IDC_WIRE,szSerialWire); + SetCommList(hDlg,IDC_IR,szSerialIr); + if (bCommInit) // disable when port open + { + EnableWindow(GetDlgItem(hDlg,IDC_WIRE),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_IR),FALSE); + } + + // HP48SX/GX + if (cCurrentRomType=='S' || cCurrentRomType=='G' || cCurrentRomType==0) + { + // init port1 enable checkbox + CheckDlgButton(hDlg,IDC_PORT1EN,(Chipset.cards_status & PORT1_PRESENT) != 0); + CheckDlgButton(hDlg,IDC_PORT1WR,(Chipset.cards_status & PORT1_WRITE) != 0); + + if (nArgc < 3) // port2 filename from Emu48.ini file + { + szActPort2Filename = szPort2Filename; + } + else // port2 filename given from command line + { + szActPort2Filename = ppArgv[2]; + EnableWindow(GetDlgItem(hDlg,IDC_PORT2),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT2LOAD),FALSE); + } + + // init port2 shared and writeable checkbox and set port2 filename + CheckDlgButton(hDlg,IDC_PORT2ISSHARED,bPort2IsShared); + CheckDlgButton(hDlg,IDC_PORT2WR,IsFileWriteable(szActPort2Filename)); + SetDlgItemText(hDlg,IDC_PORT2,szActPort2Filename); + if (nState == SM_INVALID) // Invalid State + { + EnableWindow(GetDlgItem(hDlg,IDC_PORT1EN),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT1WR),FALSE); + } + } + else // HP38G/HP39G/HP40G/HP49G/HP39G+/HP48Gii/HP49G+ // CdB for HP: add apples + { + EnableWindow(GetDlgItem(hDlg,IDC_PORT1EN),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT1WR),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT2ISSHARED),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT2WR),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT2),FALSE); + EnableWindow(GetDlgItem(hDlg,IDC_PORT2LOAD),FALSE); + if (cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q') // HP49G/HP48GII/HP49G+ // CdB for HP: add Apples + { + SendDlgItemMessage(hDlg,IDC_IR,CB_RESETCONTENT,0,0); + EnableWindow(GetDlgItem(hDlg,IDC_IR),FALSE); + } + } + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_SOUND_SPEAKER: + EnableWindow(GetDlgItem(hDlg,IDC_SOUND_SLIDER),FALSE); + return TRUE; + case IDC_SOUND_WAVE: + EnableWindow(GetDlgItem(hDlg,IDC_SOUND_SLIDER),TRUE); + return TRUE; + case IDC_PORT2LOAD: + if (GetLoadObjectFilename()) + { + TCHAR szFilename[MAX_PATH]; + LPTSTR lpFilePart; + + // check if file path and Emu48 directory path is identical + if (GetFullPathName(szBufferFilename,ARRAYSIZEOF(szFilename),szFilename,&lpFilePart)) + { + *(lpFilePart-1) = 0; // devide path and name + + // name is in the Emu48 directory -> use only name + if (lstrcmpi(szEmuDirectory,szFilename) == 0) + lstrcpy(szBufferFilename,lpFilePart); + } + SetDlgItemText(hDlg,IDC_PORT2,szBufferFilename); + + // adjust R/W checkbox + CheckDlgButton(hDlg,IDC_PORT2WR,IsFileWriteable(szBufferFilename)); + } + return TRUE; + case IDOK: + if (Chipset.Port1Size && (cCurrentRomType!='X' || cCurrentRomType!='2' || cCurrentRomType!='Q')) // CdB for HP: add apples + { + UINT nOldState = SwitchToState(SM_SLEEP); + // save old card status + BYTE bCardsStatus = Chipset.cards_status; + + // port1 disabled? + Chipset.cards_status &= ~(PORT1_PRESENT | PORT1_WRITE); + if (IsDlgButtonChecked(hDlg, IDC_PORT1EN)) + { + Chipset.cards_status |= PORT1_PRESENT; + if (IsDlgButtonChecked(hDlg, IDC_PORT1WR)) + Chipset.cards_status |= PORT1_WRITE; + } + + // changed card status in slot1? + if ( ((bCardsStatus ^ Chipset.cards_status) & (PORT1_PRESENT | PORT1_WRITE)) != 0 + && (Chipset.IORam[CARDCTL] & ECDT) != 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0 + ) + { + Chipset.HST |= MP; // set Module Pulled + IOBit(SRQ2,NINT,FALSE); // set NINT to low + Chipset.SoftInt = TRUE; // set interrupt + bInterrupt = TRUE; + } + SwitchToState(nOldState); + } + // HP48SX/GX port2 change settings detection + if(cCurrentRomType=='S' || cCurrentRomType=='G' || cCurrentRomType==0) + { + TCHAR szFilename[MAX_PATH]; + BOOL bOldPort2IsShared = bPort2IsShared; + + szActPort2Filename = (nArgc < 3) ? szPort2Filename : ppArgv[2]; + + // shared port + bPort2IsShared = IsDlgButtonChecked(hDlg,IDC_PORT2ISSHARED); + if (bPort2IsShared != bOldPort2IsShared) + { + bPort2CfgChange = TRUE; // slot2 configuration changed + } + + if (nArgc < 3) // port2 filename from Emu48.ini file + { + // get current filename and notify difference + GetDlgItemText(hDlg,IDC_PORT2,szFilename,ARRAYSIZEOF(szFilename)); + if (lstrcmp(szPort2Filename,szFilename) != 0) + { + lstrcpyn(szPort2Filename,szFilename,ARRAYSIZEOF(szPort2Filename)); + bPort2CfgChange = TRUE; // slot2 configuration changed + } + } + + // R/W port + if ( *szActPort2Filename != 0 + && (BOOL) IsDlgButtonChecked(hDlg,IDC_PORT2WR) != IsFileWriteable(szActPort2Filename)) + { + bPort2AttChange = TRUE; // slot2 file R/W attribute changed + bPort2CfgChange = TRUE; // slot2 configuration changed + } + } + // get speed checkbox value + bRealSpeed = IsDlgButtonChecked(hDlg,IDC_REALSPEED); + bAutoSave = IsDlgButtonChecked(hDlg, IDC_AUTOSAVE); + bAutoSaveOnExit = IsDlgButtonChecked(hDlg, IDC_AUTOSAVEONEXIT); + bLoadObjectWarning = IsDlgButtonChecked(hDlg, IDC_OBJECTLOADWARNING); + bAlwaysDisplayLog = IsDlgButtonChecked(hDlg, IDC_ALWAYSDISPLOG); + SetSpeed(bRealSpeed); // set speed + // LCD grayscale checkbox has been changed + if (bGrayscale != (BOOL) IsDlgButtonChecked(hDlg,IDC_GRAYSCALE)) + { + UINT nOldState = SwitchToState(SM_INVALID); + bGrayscale = !bGrayscale; // set new grayscale mode + UpdateContrast(Chipset.contrast); // update LCD color table for new mode + SwitchToState(nOldState); + } + if (bPort2CfgChange) // slot2 configuration changed + { + UINT nOldState = SwitchToState(SM_INVALID); + + UnmapPort2(); // unmap port2 + + if (bPort2AttChange) // slot2 R/W mode changed + { + DWORD dwFileAtt; + + SetCurrentDirectory(szEmuDirectory); + dwFileAtt = GetFileAttributes(szActPort2Filename); + if (dwFileAtt != 0xFFFFFFFF) + { + if (IsDlgButtonChecked(hDlg,IDC_PORT2WR)) + dwFileAtt &= ~FILE_ATTRIBUTE_READONLY; + else + dwFileAtt |= FILE_ATTRIBUTE_READONLY; + + SetFileAttributes(szActPort2Filename,dwFileAtt); + } + SetCurrentDirectory(szCurrentDirectory); + } + + if (cCurrentRomType) // ROM defined + { + MapPort2(szActPort2Filename); + + // port2 changed and card detection enabled + if ( (bPort2AttChange || Chipset.wPort2Crc != CrcPort2()) + && (Chipset.IORam[CARDCTL] & ECDT) != 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0 + ) + { + Chipset.HST |= MP; // set Module Pulled + IOBit(SRQ2,NINT,FALSE); // set NINT to low + Chipset.SoftInt = TRUE; // set interrupt + bInterrupt = TRUE; + } + // save fingerprint of port2 + Chipset.wPort2Crc = CrcPort2(); + } + SwitchToState(nOldState); + } + // set disassembler mode + disassembler_mode = IsDlgButtonChecked(hDlg,IDC_DISASM_HP) ? HP_MNEMONICS : CLASS_MNEMONICS; + // set sound data + dwWaveVol = SendDlgItemMessage(hDlg,IDC_SOUND_SLIDER,TBM_GETPOS,0,0); + bWaveBeep = IsDlgButtonChecked(hDlg,IDC_SOUND_WAVE); + // set combobox parameter + GetDlgItemText(hDlg,IDC_WIRE,szSerialWire,ARRAYSIZEOF(szSerialWire)); + // HP49G, 48GII, 49G+ Ir port is not connected + if (cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') // HP49G/HP48GII/HP49G+ // CdB for HP: add Apples + GetDlgItemText(hDlg,IDC_IR,szSerialIr,ARRAYSIZEOF(szSerialIr)); + // no break + case IDCANCEL: + EndDialog(hDlg, LOWORD(wParam)); + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + + +//################ +//# +//# Save Helper +//# +//################ + +// +// UINT SaveChanges(BOOL bAuto); +// Return code : +// IDYES File successfuly saved +// IDNO File not saved +// IDCANCEL Cancel command +// +static UINT SaveChanges(BOOL bAuto) +{ + UINT uReply; + + if (pbyRom == NULL) return IDNO; + + if (bAuto) + uReply = IDYES; + else + { + UINT uStyle = bSaveDefConfirm ? 0 : MB_DEFBUTTON2; + + uReply = YesNoCancelMessage(_T("Do you want to save changes ?"),uStyle); + } + + if (uReply != IDYES) return uReply; + + if (szCurrentFilename[0]==0) + { // Save As... + uReply = GetSaveAsFilename(); + if (uReply != IDOK) return uReply; + if (!SaveDocumentAs(szBufferFilename)) return IDCANCEL; + WriteLastDocument(szCurrentFilename); + return IDYES; + } + + SaveDocument(); + return IDYES; +} + + + +//################ +//# +//# Message Handlers +//# +//################ + +// +// WM_CREATE +// +static LRESULT OnCreate(HWND hWindow) +{ + InitializeCriticalSection(&csGDILock); + InitializeCriticalSection(&csLcdLock); + InitializeCriticalSection(&csKeyLock); + InitializeCriticalSection(&csIOLock); + InitializeCriticalSection(&csT1Lock); + InitializeCriticalSection(&csT2Lock); + InitializeCriticalSection(&csTxdLock); + InitializeCriticalSection(&csRecvLock); + InitializeCriticalSection(&csSlowLock); + + // load cursors + hCursorArrow = LoadCursor(NULL,IDC_ARROW); + hCursorHand = LoadCursor(NULL,IDC_HAND); + if (hCursorHand == NULL) + { + // for Win95, NT4.0 + bOwnCursor = ((hCursorHand = CreateHandCursor()) != NULL); + } + + hWnd = hWindow; + hWindowDC = GetDC(hWnd); + return 0; +} + +// +// WM_DESTROY +// +static LRESULT OnDestroy(HWND hWindow) +{ + DragAcceptFiles(hWnd,FALSE); // no WM_DROPFILES message any more + if (hThread) SwitchToState(SM_RETURN); // exit emulation thread + SetWindowTitle(NULL); // free memory of title + ReleaseDC(hWnd, hWindowDC); + hWindowDC = NULL; // hWindowDC isn't valid any more + hWnd = NULL; + + if (bOwnCursor) // destroy hand cursor + { + DestroyCursor(hCursorHand); + bOwnCursor = FALSE; + } + + DeleteCriticalSection(&csGDILock); + DeleteCriticalSection(&csLcdLock); + DeleteCriticalSection(&csKeyLock); + DeleteCriticalSection(&csIOLock); + DeleteCriticalSection(&csT1Lock); + DeleteCriticalSection(&csT2Lock); + DeleteCriticalSection(&csTxdLock); + DeleteCriticalSection(&csRecvLock); + DeleteCriticalSection(&csSlowLock); + + #if defined _USRDLL // DLL version + DLLDestroyWnd(); // cleanup system + #else // EXE version + PostQuitMessage(0); // exit message loop + #endif + return 0; + UNREFERENCED_PARAMETER(hWindow); +} + +// +// WM_PAINT +// +static LRESULT OnPaint(HWND hWindow) +{ + PAINTSTRUCT Paint; + HDC hPaintDC; + + hPaintDC = BeginPaint(hWindow, &Paint); + if (hMainDC != NULL) + { + RECT rcMainPaint = Paint.rcPaint; + rcMainPaint.left += nBackgroundX; // coordinates in source bitmap + rcMainPaint.top += nBackgroundY; + rcMainPaint.right += nBackgroundX; + rcMainPaint.bottom += nBackgroundY; + + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + UINT nLines = MAINSCREENHEIGHT; + + // redraw background bitmap + BitBlt(hPaintDC, Paint.rcPaint.left, Paint.rcPaint.top, + Paint.rcPaint.right-Paint.rcPaint.left, Paint.rcPaint.bottom-Paint.rcPaint.top, + hMainDC, rcMainPaint.left, rcMainPaint.top, SRCCOPY); + // CdB for HP: add apples display stuff + // redraw header display area + StretchBlt(hWindowDC, nLcdX, nLcdY, + 131*nLcdZoom, Chipset.d0size*nLcdZoom, + hLcdDC, Chipset.d0offset, 0, 131, Chipset.d0size, SRCCOPY); + // redraw main display area + StretchBlt(hWindowDC, nLcdX, nLcdY+Chipset.d0size*nLcdZoom, + 131*nLcdZoom, nLines*nLcdZoom, + hLcdDC, Chipset.boffset, Chipset.d0size, 131, MAINSCREENHEIGHT, SRCCOPY); + // redraw menu display area + StretchBlt(hWindowDC, nLcdX, nLcdY+(MAINSCREENHEIGHT+Chipset.d0size)*nLcdZoom, + 131*nLcdZoom, MENUHEIGHT*nLcdZoom, + hLcdDC, 0, (MAINSCREENHEIGHT+Chipset.d0size), 131, MENUHEIGHT, SRCCOPY); + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + UpdateAnnunciators(); + RefreshButtons(&rcMainPaint); + } + EndPaint(hWindow, &Paint); + return 0; +} + +// +// WM_DROPFILES +// +static LRESULT OnDropFiles(HANDLE hFilesInfo) +{ + TCHAR szFileName[MAX_PATH]; + WORD wNumFiles,wIndex; + BOOL bSuccess; + + // get number of files dropped + wNumFiles = DragQueryFile (hFilesInfo,(UINT)-1,NULL,0); + + SuspendDebugger(); // suspend debugger + bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control + + // calculator off, turn on + if (!(Chipset.IORam[BITOFFSET]&DON)) + { + // turn on HP + KeyboardEvent(TRUE,0,0x8000); + KeyboardEvent(FALSE,0,0x8000); + } + + _ASSERT(nState == SM_RUN); // emulator must be in RUN state + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + { + DragFinish (hFilesInfo); + InfoMessage(_T("The emulator is busy.")); + goto cancel; + } + + _ASSERT(nState == SM_SLEEP); + + // get each name and load it into the emulator + for (wIndex = 0;wIndex < wNumFiles;++wIndex) + { + DragQueryFile (hFilesInfo,wIndex,szFileName,ARRAYSIZEOF(szFileName)); + + // szFileName has file name, now try loading it + if ((bSuccess = LoadObject(szFileName)) == FALSE) + break; + } + + DragFinish (hFilesInfo); + SwitchToState(SM_RUN); // run state + while (nState!=nNextState) Sleep(0); + _ASSERT(nState == SM_RUN); + + if (bSuccess == FALSE) // data not copied + goto cancel; + + KeyboardEvent(TRUE,0,0x8000); + KeyboardEvent(FALSE,0,0x8000); + // wait for sleep mode + while(Chipset.Shutdn == FALSE) Sleep(0); + +cancel: + bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control + ResumeDebugger(); + return 0; +} + +// +// ID_FILE_NEW +// +static LRESULT OnFileNew(VOID) +{ + SaveBackup(); + if (pbyRom) + { + SwitchToState(SM_INVALID); + if (IDCANCEL == SaveChanges(bAutoSave)) + goto cancel; + } + if (NewDocument()) SetWindowTitle(_T("Untitled")); + UpdateWindowStatus(); +cancel: + if (pbyRom) SwitchToState(SM_RUN); + return 0; +} + +// +// ID_FILE_OPEN +// +static LRESULT OnFileOpen(VOID) +{ + if (pbyRom) + { + SwitchToState(SM_INVALID); + if (IDCANCEL == SaveChanges(bAutoSave)) + goto cancel; + } + if (GetOpenFilename()) + { + OpenDocument(szBufferFilename); + } +cancel: + if (pbyRom) SwitchToState(SM_RUN); + return 0; +} + +// +// ID_FILE_SAVE +// +static LRESULT OnFileSave(VOID) +{ + if (pbyRom == NULL) return 0; + SwitchToState(SM_INVALID); + SaveChanges(TRUE); + SwitchToState(SM_RUN); + return 0; +} + +// +// ID_FILE_SAVEAS +// +static LRESULT OnFileSaveAs(VOID) +{ + UINT uReply; + + if (pbyRom == NULL) return 0; + SwitchToState(SM_INVALID); + + uReply = GetSaveAsFilename(); + if (uReply != IDOK) + { + SwitchToState(SM_RUN); + return 0; + } + if (!SaveDocumentAs(szBufferFilename)) + { + SwitchToState(SM_RUN); + return 0; + } + WriteLastDocument(szCurrentFilename); + + SwitchToState(SM_RUN); + return 0; +} + +// +// ID_FILE_CLOSE +// +static LRESULT OnFileClose(VOID) +{ + if (pbyRom == NULL) return 0; + SwitchToState(SM_INVALID); + if (SaveChanges(bAutoSave)!=IDCANCEL) + { + DisableDebugger(); + ResetDocument(); + SetWindowTitle(NULL); + } + else + { + SwitchToState(SM_RUN); + } + return 0; +} + +// +// ID_FILE_EXIT +// +// WM_SYS_CLOSE +// +static LRESULT OnFileExit(VOID) +{ + SwitchToState(SM_INVALID); // hold emulation thread + if (SaveChanges(bAutoSaveOnExit) == IDCANCEL) + { + SwitchToState(SM_RUN); // on cancel restart emulation thread + return 0; + } + DestroyWindow(hWnd); + return 0; +} + +// +// ID_VIEW_COPY +// +static LRESULT OnViewCopy(VOID) +{ + if (OpenClipboard(hWnd)) + { + if (EmptyClipboard()) + { +#if !defined MONOCHROME + // DIB bitmap + #define WIDTHBYTES(bits) (((bits) + 31) / 32 * 4) + #define PALVERSION 0x300 + + BITMAP bm; + LPBITMAPINFOHEADER lpbi; + PLOGPALETTE ppal; + HBITMAP hBmp; + HDC hBmpDC; + HANDLE hClipObj; + WORD wBits; + DWORD dwLen, dwSizeImage; + + _ASSERT(nLcdZoom == 1 || nLcdZoom == 2 || nLcdZoom == 4); + hBmp = CreateCompatibleBitmap(hLcdDC,131*nLcdZoom,SCREENHEIGHT*nLcdZoom); // CdB for HP: add apples display stuff + hBmpDC = CreateCompatibleDC(hLcdDC); + hBmp = SelectObject(hBmpDC,hBmp); + StretchBlt(hBmpDC,0,0,131*nLcdZoom,SCREENHEIGHT*nLcdZoom,hLcdDC,0,0, 131, SCREENHEIGHT, SRCCOPY); // CdB for HP: add apples display stuff + hBmp = SelectObject(hBmpDC,hBmp); + + // fill BITMAP structure for size information + GetObject(hBmp, sizeof(bm), &bm); + + wBits = bm.bmPlanes * bm.bmBitsPixel; + // make sure bits per pixel is valid + if (wBits <= 1) + wBits = 1; + else if (wBits <= 4) + wBits = 4; + else if (wBits <= 8) + wBits = 8; + else // if greater than 8-bit, force to 24-bit + wBits = 24; + + dwSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * wBits) * bm.bmHeight; + + // calculate memory size to store CF_DIB data + dwLen = sizeof(BITMAPINFOHEADER) + dwSizeImage; + if (wBits != 24) // a 24 bitcount DIB has no color table + { + // add size for color table + dwLen += (DWORD) (1 << wBits) * sizeof(RGBQUAD); + } + + // memory allocation for clipboard data + if ((hClipObj = GlobalAlloc(GMEM_MOVEABLE, dwLen)) != NULL) + { + lpbi = GlobalLock(hClipObj); + // initialize BITMAPINFOHEADER + lpbi->biSize = sizeof(BITMAPINFOHEADER); + lpbi->biWidth = bm.bmWidth; + lpbi->biHeight = bm.bmHeight; + lpbi->biPlanes = 1; + lpbi->biBitCount = wBits; + lpbi->biCompression = BI_RGB; + lpbi->biSizeImage = dwSizeImage; + lpbi->biXPelsPerMeter = 0; + lpbi->biYPelsPerMeter = 0; + lpbi->biClrUsed = 0; + lpbi->biClrImportant = 0; + // get bitmap color table and bitmap data + GetDIBits(hBmpDC, hBmp, 0, lpbi->biHeight, (LPBYTE)lpbi + dwLen - dwSizeImage, + (LPBITMAPINFO)lpbi, DIB_RGB_COLORS); + GlobalUnlock(hClipObj); + SetClipboardData(CF_DIB, hClipObj); + + // get number of entries in the logical palette + GetObject(hPalette,sizeof(WORD),&wBits); + + // memory allocation for temporary palette data + if ((ppal = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sizeof(LOGPALETTE) + wBits * sizeof(PALETTEENTRY))) != NULL) + { + ppal->palVersion = PALVERSION; + ppal->palNumEntries = wBits; + GetPaletteEntries(hPalette, 0, wBits, ppal->palPalEntry); + SetClipboardData(CF_PALETTE, CreatePalette(ppal)); + HeapFree(hHeap,0,ppal); + } + } + DeleteDC(hBmpDC); + DeleteObject(hBmp); + #undef WIDTHBYTES + #undef PALVERSION +#else + HBITMAP hOldBmp, hBmp; + HDC hBmpDC; + + // don't work with background index <> 0 + _ASSERT(nLcdZoom == 1 || nLcdZoom == 2 || nLcdZoom == 4); + hBmp = CreateBitmap(131*nLcdZoom,SCREENHEIGHT*nLcdZoom,1,1,NULL); // CdB for HP: add apples display management + hBmpDC = CreateCompatibleDC(NULL); + hOldBmp = (HBITMAP)SelectObject(hBmpDC,hBmp); + StretchBlt(hBmpDC,0,0,131*nLcdZoom,SCREENHEIGHT*nLcdZoom,hLcdDC,0,0, 131, SCREENHEIGHT, SRCCOPY); // CdB for HP: add apples display stuff + SetClipboardData(CF_BITMAP,hBmp); + SelectObject(hBmpDC,hOldBmp); + DeleteDC(hBmpDC); +#endif + } + CloseClipboard(); + } + return 0; +} + +// +// ID_VIEW_RESET +// +static LRESULT OnViewReset(VOID) +{ + if (nState != SM_RUN) return 0; + if (YesNoMessage(_T("Are you sure you want to press the Reset Button ?"))==IDYES) + { + SwitchToState(SM_SLEEP); + CpuReset(); // register setting after Cpu Reset + SwitchToState(SM_RUN); + } + return 0; +} + +// +// ID_VIEW_SETTINGS +// +static LRESULT OnViewSettings(VOID) +{ + // not in nState = SM_INVALID or port2 file must be closed from document + _ASSERT(nState != SM_INVALID || pbyPort2 == NULL); + + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_SETTINGS), hWnd, (DLGPROC)SettingsProc) == -1) + AbortMessage(_T("Settings Dialog Creation Error !")); + + WriteSettings(); + return 0; +} + +// +// ID_VIEW_SCRIPT +// +static LRESULT OnViewScript(VOID) +{ + BYTE cType = cCurrentRomType; + if (nState != SM_RUN) + { + InfoMessage(_T("You cannot change the KML script when Emu48 is not running.\n") + _T("Use the File,New menu item to create a new calculator.")); + return 0; + } + SwitchToState(SM_INVALID); + + do + { + if (!DisplayChooseKml(cType)) break; + } + while(!InitKML(szCurrentKml,FALSE)); + + SetWindowPathTitle(szCurrentFilename); // update window title line + if (pbyRom) SwitchToState(SM_RUN); + return 0; +} + +// +// ID_BACKUP_SAVE +// +static LRESULT OnBackupSave(VOID) +{ + UINT nOldState; + if (pbyRom == NULL) return 0; + nOldState = SwitchToState(SM_INVALID); + SaveBackup(); + SwitchToState(nOldState); + return 0; +} + +// +// ID_BACKUP_RESTORE +// +static LRESULT OnBackupRestore(VOID) +{ + SwitchToState(SM_INVALID); + RestoreBackup(); + if (pbyRom) SwitchToState(SM_RUN); + return 0; +} + +// +// ID_BACKUP_DELETE +// +static LRESULT OnBackupDelete(VOID) +{ + ResetBackup(); + return 0; +} + +// +// ID_OBJECT_LOAD +// +static LRESULT OnObjectLoad(VOID) +{ + SuspendDebugger(); // suspend debugger + bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control + + // calculator off, turn on + if (!(Chipset.IORam[BITOFFSET]&DON)) + { + KeyboardEvent(TRUE,0,0x8000); + KeyboardEvent(FALSE,0,0x8000); + + // wait for sleep mode + while(Chipset.Shutdn == FALSE) Sleep(0); + } + + if (nState != SM_RUN) + { + InfoMessage(_T("The emulator must be running to load an object.")); + goto cancel; + } + + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + { + InfoMessage(_T("The emulator is busy.")); + goto cancel; + } + + _ASSERT(nState == SM_SLEEP); + + if (bLoadObjectWarning) + { + UINT uReply = YesNoCancelMessage( + _T("Warning: Trying to load an object while the emulator is busy\n") + _T("will certainly result in a memory lost. Before loading an object\n") + _T("you should be sure that the calculator is not doing anything.\n") + _T("Do you want to see this warning next time you try to load an object ?"),0); + switch (uReply) + { + case IDYES: + break; + case IDNO: + bLoadObjectWarning = FALSE; + break; + case IDCANCEL: + SwitchToState(SM_RUN); + goto cancel; + } + } + + if (!GetLoadObjectFilename()) + { + SwitchToState(SM_RUN); + goto cancel; + } + + if (!LoadObject(szBufferFilename)) + { + SwitchToState(SM_RUN); + goto cancel; + } + + SwitchToState(SM_RUN); // run state + while (nState!=nNextState) Sleep(0); + _ASSERT(nState == SM_RUN); + KeyboardEvent(TRUE,0,0x8000); + KeyboardEvent(FALSE,0,0x8000); + while(Chipset.Shutdn == FALSE) Sleep(0); + +cancel: + bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control + ResumeDebugger(); + return 0; +} + +// +// ID_OBJECT_SAVE +// +static LRESULT OnObjectSave(VOID) +{ + if (nState != SM_RUN) + { + InfoMessage(_T("The emulator must be running to save an object.")); + return 0; + } + + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + { + InfoMessage(_T("The emulator is busy.")); + return 0; + } + + _ASSERT(nState == SM_SLEEP); + + if (!GetSaveObjectFilename()) + { + SwitchToState(SM_RUN); + return 0; + } + + SaveObject(szBufferFilename); + + SwitchToState(SM_RUN); + + return 0; +} + +// +// ID_TOOL_DISASM +// +static INT_PTR CALLBACK Disasm(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + static DWORD dwAddress, dwAddressMax; + + LONG i; + TCHAR *cpStop,szAddress[256] = _T("0"); + + switch (message) + { + case WM_INITDIALOG: + // set fonts & cursor + SendDlgItemMessage(hDlg,IDC_DISASM_MODULE,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_MAP,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_ROM,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_RAM,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_PORT1,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_PORT2,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_MAP,BM_SETCHECK,1,0); + SendDlgItemMessage(hDlg,IDC_ADDRESS,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_ADR,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_NEXT,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDC_DISASM_COPY,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SendDlgItemMessage(hDlg,IDCANCEL,WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); + SetDlgItemText(hDlg,IDC_DISASM_ADR,szAddress); + disassembler_map = MEM_MAP; // disassemble with mapped modules + dwAddress = _tcstoul(szAddress,&cpStop,16); + dwAddressMax = 0x100000; // greatest address (mapped mode) + return TRUE; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + // decode radio buttons + case IDC_DISASM_MAP: + disassembler_map = MEM_MAP; + dwAddressMax = 0x100000; + return TRUE; + case IDC_DISASM_ROM: + disassembler_map = MEM_ROM; + dwAddressMax = dwRomSize; + return TRUE; + case IDC_DISASM_RAM: + disassembler_map = MEM_RAM; + dwAddressMax = Chipset.Port0Size * 2048; + return TRUE; + case IDC_DISASM_PORT1: + disassembler_map = MEM_PORT1; + dwAddressMax = ((Chipset.cards_status & PORT1_PRESENT) != 0) ? (Chipset.Port1Size * 2048) : 0; + return TRUE; + case IDC_DISASM_PORT2: + disassembler_map = MEM_PORT2; + dwAddressMax = ((cCurrentRomType=='E' || cCurrentRomType=='X' || cCurrentRomType=='P' || cCurrentRomType=='2' || cCurrentRomType=='Q') // CdB for HP: add apples + ? Chipset.Port2Size + : dwPort2Size) + * 2048; + return TRUE; + case IDOK: + SendDlgItemMessage(hDlg,IDC_DISASM_ADR,EM_SETSEL,0,-1); + GetDlgItemText(hDlg,IDC_DISASM_ADR,szAddress,ARRAYSIZEOF(szAddress)); + // test if valid hex address + for (i = 0; i < (LONG) lstrlen(szAddress); ++i) + { + if (_istxdigit(szAddress[i]) == FALSE) + return FALSE; + } + dwAddress = _tcstoul(szAddress,&cpStop,16); + // no break + case IDC_DISASM_NEXT: + if (dwAddress >= dwAddressMax) + return FALSE; + i = wsprintf(szAddress,(dwAddress <= 0xFFFFF) ? _T("%05lX ") : _T("%06lX "),dwAddress); + dwAddress = disassemble(dwAddress,&szAddress[i],VIEW_LONG); + i = (LONG) SendDlgItemMessage(hDlg,IDC_DISASM_WIN,LB_ADDSTRING,0,(LPARAM) szAddress); + SendDlgItemMessage(hDlg,IDC_DISASM_WIN,LB_SELITEMRANGE,FALSE,MAKELPARAM(0,i)); + SendDlgItemMessage(hDlg,IDC_DISASM_WIN,LB_SETSEL,TRUE,i); + SendDlgItemMessage(hDlg,IDC_DISASM_WIN,LB_SETTOPINDEX,i,0); + return TRUE; + case IDC_DISASM_COPY: + // copy selected items to clipboard + CopyItemsToClipboard(GetDlgItem(hDlg,IDC_DISASM_WIN)); + return TRUE; + case IDCANCEL: + EndDialog(hDlg,IDCANCEL); + return TRUE; + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +// +// ID_ABOUT +// +static INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + SetDlgItemText(hDlg,IDC_VERSION,szNoTitle); + SetDlgItemText(hDlg,IDC_LICENSE,szLicence); + return TRUE; + case WM_COMMAND: + wParam = LOWORD(wParam); + if ((wParam==IDOK)||(wParam==IDCANCEL)) + { + EndDialog(hDlg, wParam); + return TRUE; + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +static LRESULT OnToolDisasm(VOID) // disasm dialogbox call +{ + if (pbyRom) SwitchToState(SM_SLEEP); + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_DISASM), hWnd, (DLGPROC)Disasm) == -1) + AbortMessage(_T("Disassembler Dialog Box Creation Error !")); + if (pbyRom) SwitchToState(SM_RUN); + return 0; +} + +static LRESULT OnAbout(VOID) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_ABOUT), hWnd, (DLGPROC)About) == -1) + AbortMessage(_T("About Dialog Box Creation Error !")); + return 0; +} + +static LRESULT OnLButtonDown(UINT nFlags, WORD x, WORD y) +{ + if (nMacroState == MACRO_PLAY) return 0; // playing macro + if (nState == SM_RUN) MouseButtonDownAt(nFlags, x,y); + return 0; +} + +static LRESULT OnLButtonUp(UINT nFlags, WORD x, WORD y) +{ + if (nMacroState == MACRO_PLAY) return 0; // playing macro + if (nState == SM_RUN) MouseButtonUpAt(nFlags, x,y); + return 0; +} + +static LRESULT OnMouseMove(UINT nFlags, WORD x, WORD y) +{ + if (nMacroState == MACRO_PLAY) return 0; // playing macro + if (nState == SM_RUN) MouseMovesTo(nFlags, x,y); + return 0; +} + +static LRESULT OnKeyDown(int nVirtKey, LPARAM lKeyData) +{ + if (nMacroState == MACRO_PLAY) return 0; // playing macro + // call RunKey() only once (suppress autorepeat feature) + if (nState == SM_RUN && (lKeyData & 0x40000000) == 0) + RunKey((BYTE)nVirtKey, TRUE); + return 0; +} + +static LRESULT OnKeyUp(int nVirtKey, LPARAM lKeyData) +{ + if (nMacroState == MACRO_PLAY) return 0; // playing macro + if (nState == SM_RUN) RunKey((BYTE)nVirtKey, FALSE); + return 0; + UNREFERENCED_PARAMETER(lKeyData); +} + +LRESULT CALLBACK MainWndProc(HWND hWindow, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_CREATE: return OnCreate(hWindow); + case WM_DESTROY: return OnDestroy(hWindow); + case WM_PAINT: return OnPaint(hWindow); + case WM_DROPFILES: return OnDropFiles((HANDLE)wParam); + case WM_ACTIVATE: + if (LOWORD(wParam)==WA_INACTIVE) break; + case WM_QUERYNEWPALETTE: + if (hPalette) + { + SelectPalette(hWindowDC, hPalette, FALSE); + if (RealizePalette(hWindowDC)) + { + InvalidateRect(hWindow,NULL,TRUE); + return TRUE; + } + } + return FALSE; + case WM_PALETTECHANGED: + if ((HWND)wParam == hWindow) break; + if (hPalette) + { + SelectPalette(hWindowDC, hPalette, FALSE); + if (RealizePalette(hWindowDC)) + { + // UpdateColors(hWindowDC); + InvalidateRect (hWnd, (LPRECT) (NULL), 1); + } + } + return FALSE; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case ID_FILE_NEW: return OnFileNew(); + case ID_FILE_OPEN: return OnFileOpen(); + case ID_FILE_SAVE: return OnFileSave(); + case ID_FILE_SAVEAS: return OnFileSaveAs(); + case ID_FILE_CLOSE: return OnFileClose(); + case ID_FILE_EXIT: return OnFileExit(); + case ID_STACK_COPY: return OnStackCopy(); + case ID_STACK_PASTE: return OnStackPaste(); + case ID_VIEW_COPY: return OnViewCopy(); + case ID_VIEW_RESET: return OnViewReset(); + case ID_VIEW_SETTINGS: return OnViewSettings(); + case ID_VIEW_SCRIPT: return OnViewScript(); + case ID_BACKUP_SAVE: return OnBackupSave(); + case ID_BACKUP_RESTORE: return OnBackupRestore(); + case ID_BACKUP_DELETE: return OnBackupDelete(); + case ID_OBJECT_LOAD: return OnObjectLoad(); + case ID_OBJECT_SAVE: return OnObjectSave(); + case ID_TOOL_DISASM: return OnToolDisasm(); + case ID_TOOL_DEBUG: return OnToolDebug(); + case ID_TOOL_MACRO_RECORD: return OnToolMacroNew(); + case ID_TOOL_MACRO_PLAY: return OnToolMacroPlay(); + case ID_TOOL_MACRO_STOP: return OnToolMacroStop(); + case ID_TOOL_MACRO_SETTINGS: return OnToolMacroSettings(); + case ID_ABOUT: return OnAbout(); + } + break; + case WM_SYSCOMMAND: + switch (wParam & 0xFFF0) + { + case SC_CLOSE: return OnFileExit(); + } + break; + case WM_RBUTTONDOWN: + case WM_LBUTTONDOWN: return OnLButtonDown((UINT) wParam, LOWORD(lParam), HIWORD(lParam)); + case WM_LBUTTONUP: return OnLButtonUp((UINT) wParam, LOWORD(lParam), HIWORD(lParam)); + case WM_MOUSEMOVE: return OnMouseMove((UINT) wParam, LOWORD(lParam), HIWORD(lParam)); + case WM_KEYUP: return OnKeyUp((int)wParam, lParam); + case WM_KEYDOWN: return OnKeyDown((int)wParam, lParam); + } + return DefWindowProc(hWindow, uMsg, wParam, lParam); +} + +int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow) +{ + MSG msg; + WNDCLASS wc; + RECT rectWindow; + HACCEL hAccel; + HSZ hszService, hszTopic; // variables for DDE server + DWORD_PTR dwAffMask; + + hApp = hInst; + #if defined _UNICODE + { + ppArgv = CommandLineToArgvW(GetCommandLine(),&nArgc); + } + #else + { + nArgc = __argc; // no. of command line arguments + ppArgv = (LPCTSTR*) __argv; // command line arguments + } + #endif + + hHeap = GetProcessHeap(); + if (hHeap == NULL) + { + AbortMessage(_T("Heap creation failed.")); + return FALSE; + } + + wc.style = CS_BYTEALIGNCLIENT; + wc.lpfnWndProc = (WNDPROC)MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInst; + wc.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_EMU48)); + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU); + wc.lpszClassName = _T("CEmu48"); + + if (!RegisterClass(&wc)) + { + AbortMessage( + _T("CEmu48 class registration failed.\n") + _T("This application will now terminate.")); + return FALSE; + } + + // Create window + rectWindow.left = 0; + rectWindow.top = 0; + rectWindow.right = 256; + rectWindow.bottom = 0; + AdjustWindowRect(&rectWindow, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, TRUE); + + hWnd = CreateWindow(_T("CEmu48"), _T("Emu48"), + WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, + CW_USEDEFAULT, CW_USEDEFAULT, + rectWindow.right - rectWindow.left, + rectWindow.bottom - rectWindow.top, + NULL,NULL,hApp,NULL + ); + + if (hWnd == NULL) + { + AbortMessage(_T("Window creation failed.")); + return FALSE; + } + + VERIFY(hAccel = LoadAccelerators(hInst, MAKEINTRESOURCE(IDR_MENU))); + + // initialization + QueryPerformanceFrequency(&lFreq); // init high resolution counter + QueryPerformanceCounter(&lAppStart); + + GetCurrentDirectory(ARRAYSIZEOF(szCurrentDirectory), szCurrentDirectory); + szCurrentKml[0] = 0; // no KML file selected + + ReadSettings(); + + UpdateWindowStatus(); + + // create auto event handle + hEventShutdn = CreateEvent(NULL,FALSE,FALSE,NULL); + if (hEventShutdn == NULL) + { + AbortMessage(_T("Event creation failed.")); + DestroyWindow(hWnd); + return FALSE; + } + + nState = SM_RUN; // init state must be <> nNextState + nNextState = SM_INVALID; // go into invalid state + hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&WorkerThread, NULL, CREATE_SUSPENDED, &lThreadId); + if (hThread == NULL) + { + CloseHandle(hEventShutdn); // close event handle + AbortMessage(_T("Thread creation failed.")); + DestroyWindow(hWnd); + return FALSE; + } + // on multiprocessor machines for QueryPerformanceCounter() + dwAffMask = SetThreadAffinityMask(hThread,1); + _ASSERT(dwAffMask != 0); + ResumeThread(hThread); // start thread + while (nState!=nNextState) Sleep(0); // wait for thread initialized + + idDdeInst = 0; // initialize DDE server + if (DdeInitialize(&idDdeInst,(PFNCALLBACK) &DdeCallback, + APPCLASS_STANDARD | + CBF_FAIL_EXECUTES | CBF_FAIL_ADVISES | + CBF_SKIP_REGISTRATIONS | CBF_SKIP_UNREGISTRATIONS,0)) + { + TerminateThread(hThread, 0); // kill emulation thread + CloseHandle(hEventShutdn); // close event handle + AbortMessage(_T("Could not initialize server!")); + DestroyWindow(hWnd); + return FALSE; + } + + _ASSERT(hWnd != NULL); + _ASSERT(hWindowDC != NULL); + + if (nArgc >= 2) // use decoded parameter line + lstrcpyn(szBufferFilename,ppArgv[1],ARRAYSIZEOF(szBufferFilename)); + else // use last document setting + ReadLastDocument(szBufferFilename, ARRAYSIZEOF(szBufferFilename)); + + if (szBufferFilename[0]) // given default document + { + TCHAR szTemp[MAX_PATH+8] = _T("Loading "); + RECT rectClient; + + _ASSERT(hWnd != NULL); + VERIFY(GetClientRect(hWnd,&rectClient)); + GetCutPathName(szBufferFilename,&szTemp[8],MAX_PATH,rectClient.right/10-8); + SetWindowTitle(szTemp); + if (OpenDocument(szBufferFilename)) + goto start; + } + + // no default document, ask for new one + SetWindowTitle(NewDocument() ? _T("Untitled") : _T("New Document")); + +start: + // init clipboard format and name service + uCF_HpObj = RegisterClipboardFormat(_T(CF_HPOBJ)); + hszService = DdeCreateStringHandle(idDdeInst,szAppName,0); + hszTopic = DdeCreateStringHandle(idDdeInst,szTopic,0); + DdeNameService(idDdeInst,hszService,NULL,DNS_REGISTER); + + ShowWindow(hWnd, nCmdShow); + + if (pbyRom) SwitchToState(SM_RUN); + + while (GetMessage(&msg, NULL, 0, 0)) + { + if( !TranslateAccelerator(hWnd, hAccel, &msg) + && (hDlgDebug == NULL || !IsDialogMessage(hDlgDebug, &msg)) + && (hDlgFind == NULL || !IsDialogMessage(hDlgFind, &msg)) + && (hDlgProfile == NULL || !IsDialogMessage(hDlgProfile, &msg))) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + // clean up DDE server + DdeNameService(idDdeInst, hszService, NULL, DNS_UNREGISTER); + DdeFreeStringHandle(idDdeInst, hszService); + DdeFreeStringHandle(idDdeInst, hszTopic); + DdeUninitialize(idDdeInst); + + WriteSettings(); // save emulation settings + + CloseHandle(hThread); // close thread handle + CloseHandle(hEventShutdn); // close event handle + _ASSERT(nState == SM_RETURN); // emulation thread down? + ResetDocument(); + ResetBackup(); + _ASSERT(pbyRom == NULL); // rom file unmapped + _ASSERT(pbyPort2 == NULL); // port2 file unmapped + _ASSERT(pKml == NULL); // KML script not closed + _ASSERT(szTitle == NULL); // freed allocated memory + _ASSERT(hPalette == NULL); // freed resource memory + + return (int) msg.wParam; + UNREFERENCED_PARAMETER(lpCmdLine); + UNREFERENCED_PARAMETER(hPrevInst); +} diff --git a/SOURCE/EMU48.H b/SOURCE/EMU48.H new file mode 100644 index 0000000..48424e6 --- /dev/null +++ b/SOURCE/EMU48.H @@ -0,0 +1,347 @@ +/* + * Emu48.h + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "types.h" + +#define HARDWARE "Yorke" // emulator hardware +#define MODELS "26AEGPQSX" // valid calculator models + +#define ARRAYSIZEOF(a) (sizeof(a) / sizeof(a[0])) + +// cards status +#define PORT1_PRESENT ((cCurrentRomType=='S')?P1C:P2C) +#define PORT1_WRITE ((cCurrentRomType=='S')?P1W:P2W) +#define PORT2_PRESENT ((cCurrentRomType=='S')?P2C:P1C) +#define PORT2_WRITE ((cCurrentRomType=='S')?P2W:P1W) + +#define BINARYHEADER48 "HPHP48-W" +#define BINARYHEADER49 "HPHP49-W" + +#define CF_HPOBJ "CF_HPOBJ" // clipboard format for DDE + +// CPU cycles in 16384 Hz time frame +#define T2CYCLES ((cCurrentRomType=='S')?dwSXCycles:(cCurrentRomType=='G')?dwGXCycles:(cCurrentRomType=='P')?dwGPCycles:(cCurrentRomType=='Q')?dwGPCycles:dwG2Cycles) // CdB for HP: add apples + +#define SM_RUN 0 // states of cpu emulation thread +#define SM_INVALID 1 +#define SM_RETURN 2 +#define SM_SLEEP 3 + +#define S_ERR_NO 0 // stack errorcodes +#define S_ERR_BINARY 1 +#define S_ERR_ASCII 2 + +#define NO_SERIAL "disabled" // port not open + +#define HP_MNEMONICS FALSE // disassembler mnenomics mode +#define CLASS_MNEMONICS TRUE + +#define MEM_MAP 0 // memory module definition +#define MEM_ROM 1 +#define MEM_RAM 2 +#define MEM_PORT1 3 +#define MEM_PORT2 4 + +#define VIEW_SHORT FALSE // view of disassembler output +#define VIEW_LONG TRUE + +#define MACRO_OFF 0 // macro recorder off +#define MACRO_NEW 1 +#define MACRO_PLAY 2 + +#define DISP_POINTER 0x01 // defines for display area +#define DISP_MAIN 0x02 +#define DISP_MENUE 0x04 +#define DISP_ANNUN 0x08 + +// macro to check for valid calculator model +#define isModelValid(m) (m != 0 && strchr(MODELS,m) != NULL) + +// values for mapping area +enum MMUMAP { M_IO, M_ROM, M_RAM, M_P1, M_P2, M_BS }; + +// Emu48.c +extern HPALETTE hPalette; +extern HPALETTE hOldPalette; +extern HANDLE hEventShutdn; +extern LPTSTR szAppName; +extern LPTSTR szTopic; +extern LPTSTR szTitle; +extern CRITICAL_SECTION csGDILock; +extern CRITICAL_SECTION csLcdLock; +extern CRITICAL_SECTION csKeyLock; +extern CRITICAL_SECTION csIOLock; +extern CRITICAL_SECTION csT1Lock; +extern CRITICAL_SECTION csT2Lock; +extern CRITICAL_SECTION csTxdLock; +extern CRITICAL_SECTION csRecvLock; +extern CRITICAL_SECTION csSlowLock; +extern INT nArgc; +extern LPCTSTR *ppArgv; +extern LARGE_INTEGER lFreq; +extern LARGE_INTEGER lAppStart; +extern DWORD idDdeInst; +extern UINT uCF_HpObj; +extern HANDLE hHeap; +extern HINSTANCE hApp; +extern HWND hWnd; +extern HWND hDlgDebug; +extern HWND hDlgFind; +extern HWND hDlgProfile; +extern HDC hWindowDC; +extern HCURSOR hCursorArrow; +extern HCURSOR hCursorHand; +extern BOOL bClassicCursor; +extern BOOL bAutoSave; +extern BOOL bAutoSaveOnExit; +extern BOOL bSaveDefConfirm; +extern BOOL bAlwaysDisplayLog; +extern BOOL bLoadObjectWarning; +extern HANDLE hThread; +extern DWORD lThreadId; +extern VOID SetWindowTitle(LPCTSTR szString); +extern VOID CopyItemsToClipboard(HWND hWnd); +extern VOID UpdateWindowStatus(VOID); + +// Settings.c +extern VOID ReadSettings(VOID); +extern VOID WriteSettings(VOID); +extern VOID ReadLastDocument(LPTSTR szFileName, DWORD nSize); +extern VOID WriteLastDocument(LPCTSTR szFilename); + +// Display.c +extern UINT nBackgroundX; +extern UINT nBackgroundY; +extern UINT nBackgroundW; +extern UINT nBackgroundH; +extern UINT nLcdX; +extern UINT nLcdY; +extern UINT nLcdZoom; +extern LPBYTE pbyLcd; +extern HDC hLcdDC; +extern HDC hMainDC; +extern VOID UpdateContrast(BYTE byContrast); +extern VOID SetLcdColor(UINT nId, UINT nRed, UINT nGreen, UINT nBlue); +extern VOID CreateLcdBitmap(VOID); +extern VOID DestroyLcdBitmap(VOID); +extern BOOL CreateMainBitmap(LPCTSTR szFilename); +extern VOID DestroyMainBitmap(VOID); +extern VOID UpdateDisplayPointers(VOID); +extern VOID UpdateMainDisplay(VOID); +extern VOID UpdateMenuDisplay(VOID); +extern VOID RefreshDisp0(); // CdB for HP: add apples display management +extern VOID WriteToMainDisplay(LPBYTE a, DWORD d, UINT s); +extern VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s); +extern VOID UpdateAnnunciators(VOID); +extern VOID ResizeWindow(VOID); +extern BYTE GetLineCounter(VOID); +extern VOID StartDisplay(BYTE byInitial); +extern VOID StopDisplay(VOID); + +// Engine.c +extern BOOL bInterrupt; +extern UINT nState; +extern UINT nNextState; +extern BOOL bRealSpeed; +extern BOOL bKeySlow; +extern BOOL bCommInit; +extern BOOL bGrayscale; +extern CHIPSET Chipset; +extern TCHAR szSerialWire[16]; +extern TCHAR szSerialIr[16]; +extern DWORD dwSXCycles; +extern DWORD dwGXCycles; +extern DWORD dwGPCycles; // CdB for HP: add apples speed +extern DWORD dwG2Cycles; // CdB for HP: add apples speed +extern HANDLE hEventDebug; +extern BOOL bDbgAutoStateCtrl; +extern INT nDbgState; +extern BOOL bDbgNOP3; +extern BOOL bDbgCode; +extern BOOL bDbgRPL; +extern BOOL bDbgSkipInt; +extern DWORD dwDbgStopPC; +extern DWORD dwDbgRplPC; +extern DWORD dwDbgRstkp; +extern DWORD dwDbgRstk; +extern DWORD *pdwInstrArray; +extern WORD wInstrSize; +extern WORD wInstrWp; +extern WORD wInstrRp; +extern VOID SuspendDebugger(VOID); +extern VOID ResumeDebugger(VOID); +extern VOID CheckSerial(VOID); +extern VOID AdjKeySpeed(VOID); +extern VOID SetSpeed(BOOL bAdjust); +extern VOID UpdateKdnBit(VOID); +extern BOOL WaitForSleepState(VOID); +extern UINT SwitchToState(UINT nNewState); +extern UINT WorkerThread(LPVOID pParam); + +// Fetch.c +extern VOID EvalOpcode(LPBYTE I); + +// Files.c +extern TCHAR szEmuDirectory[MAX_PATH]; +extern TCHAR szCurrentDirectory[MAX_PATH]; +extern TCHAR szCurrentKml[MAX_PATH]; +extern TCHAR szBackupKml[MAX_PATH]; +extern TCHAR szCurrentFilename[MAX_PATH]; +extern TCHAR szBackupFilename[MAX_PATH]; +extern TCHAR szBufferFilename[MAX_PATH]; +extern TCHAR szPort2Filename[MAX_PATH]; +extern LPBYTE pbyRom; +extern DWORD dwRomSize; +extern BYTE cCurrentRomType; +extern UINT nCurrentClass; +extern BOOL bRomWriteable; +extern LPBYTE pbyPort2; +extern BOOL bPort2Writeable; +extern BOOL bPort2IsShared; +extern DWORD dwPort2Size; +extern DWORD dwPort2Mask; +extern BOOL bBackup; +extern VOID SetWindowLocation(HWND hWnd,INT nPosX,INT nPosY); +extern DWORD GetCutPathName(LPCTSTR szFileName,LPTSTR szBuffer,DWORD dwBufferLength,INT nCutLength); +extern VOID SetWindowPathTitle(LPCTSTR szFileName); +extern VOID UpdatePatches(BOOL bPatch); +extern BOOL PatchRom(LPCTSTR szFilename); +extern BOOL MapRom(LPCTSTR szFilename); +extern VOID UnmapRom(VOID); +extern WORD CrcPort2(VOID); +extern BOOL MapPort2(LPCTSTR szFilename); +extern VOID UnmapPort2(VOID); +extern VOID ResetDocument(VOID); +extern BOOL NewDocument(VOID); +extern BOOL OpenDocument(LPCTSTR szFilename); +extern BOOL SaveDocument(VOID); +extern BOOL SaveDocumentAs(LPCTSTR szFilename); +extern BOOL SaveBackup(VOID); +extern BOOL RestoreBackup(VOID); +extern BOOL ResetBackup(VOID); +extern BOOL GetOpenFilename(VOID); +extern BOOL GetSaveAsFilename(VOID); +extern BOOL GetLoadObjectFilename(VOID); +extern BOOL GetSaveObjectFilename(VOID); +extern WORD WriteStack(UINT nStkLevel,LPBYTE lpBuf,DWORD dwSize); +extern BOOL LoadObject(LPCTSTR szFilename); +extern BOOL SaveObject(LPCTSTR szFilename); +extern HBITMAP LoadBitmapFile(LPCTSTR szFilename); + +// Timer.c +extern VOID SetHP48Time(VOID); +extern VOID StartTimers(VOID); +extern VOID StopTimers(VOID); +extern DWORD ReadT2(VOID); +extern VOID SetT2(DWORD dwValue); +extern BYTE ReadT1(VOID); +extern VOID SetT1(BYTE byValue); + +// Mops.c +extern BOOL bFlashRomArray; +extern BYTE disp; +extern LPBYTE RMap[256]; +extern LPBYTE WMap[256]; +extern VOID Map(BYTE a, BYTE b); +extern VOID RomSwitch(DWORD adr); +extern VOID Config(VOID); +extern VOID Uncnfg(VOID); +extern VOID Reset(VOID); +extern VOID C_Eq_Id(VOID); +extern enum MMUMAP MapData(DWORD d); +extern VOID CpuReset(VOID); +extern VOID Npeek(BYTE *a, DWORD d, UINT s); +extern VOID Nread(BYTE *a, DWORD d, UINT s); +extern VOID Nwrite(BYTE *a, DWORD d, UINT s); +extern BYTE Read2(DWORD d); +extern DWORD Read5(DWORD d); +extern VOID Write5(DWORD d, DWORD n); +extern VOID Write2(DWORD d, BYTE n); +extern VOID IOBit(DWORD d, BYTE b, BOOL s); +extern VOID ReadIO(BYTE *a, DWORD b, DWORD s, BOOL bUpdate); +extern VOID WriteIO(BYTE *a, DWORD b, DWORD s); + +// Keyboard.c +extern VOID ScanKeyboard(BOOL bActive, BOOL bReset); +extern VOID KeyboardEvent(BOOL bPress, UINT out, UINT in); + +// Keymacro.c +extern INT nMacroState; +extern INT nMacroTimeout; +extern BOOL bMacroRealSpeed; +extern VOID KeyMacroRecord(BOOL bPress, UINT out, UINT in); +extern LRESULT OnToolMacroNew(VOID); +extern LRESULT OnToolMacroPlay(VOID); +extern LRESULT OnToolMacroStop(VOID); +extern LRESULT OnToolMacroSettings(VOID); + +// Stack.c +extern LRESULT OnStackCopy(VOID); +extern LRESULT OnStackPaste(VOID); + +// RPL.c +extern BOOL RPL_GetSystemFlag(INT nFlag); +extern DWORD RPL_SkipOb(DWORD d); +extern DWORD RPL_ObjectSize(BYTE *o); +extern DWORD RPL_CreateTemp(DWORD l); +extern UINT RPL_Depth(VOID); +extern DWORD RPL_Pick(UINT l); +extern VOID RPL_Replace(DWORD n); +extern VOID RPL_Push(UINT l,DWORD n); + +// External.c +extern BOOL bWaveBeep; +extern DWORD dwWaveVol; +extern VOID External(CHIPSET* w); + +// DDEserv.c +extern HDDEDATA CALLBACK DdeCallback(UINT, UINT, HCONV, HSZ, HSZ, HDDEDATA, DWORD, DWORD); + +// Disasm.c +extern BOOL disassembler_mode; +extern WORD disassembler_map; +extern DWORD disassemble (DWORD addr, LPTSTR out, BOOL view); + +// Serial.c +extern BOOL CommOpen(LPTSTR strWirePort,LPTSTR strIrPort); +extern VOID CommClose(VOID); +extern VOID CommSetBaud(VOID); +extern BOOL UpdateUSRQ(VOID); +extern VOID CommTxBRK(VOID); +extern VOID CommTransmit(VOID); +extern VOID CommReceive(VOID); + +// Cursor.c +extern HCURSOR CreateHandCursor(VOID); + +#if defined _USRDLL // DLL version +// Emu48dll.c +extern VOID (CALLBACK *pEmuDocumentNotify)(LPCTSTR lpszFilename); +extern BOOL DLLCreateWnd(LPCTSTR lpszFilename, LPCTSTR lpszPort2Name); +extern BOOL DLLDestroyWnd(VOID); +#endif + +// Message Boxes +static __inline int InfoMessage(LPCTSTR szMessage) {return MessageBox(hWnd, szMessage, szTitle, MB_APPLMODAL|MB_OK|MB_ICONINFORMATION|MB_SETFOREGROUND);} +static __inline int AbortMessage(LPCTSTR szMessage) {return MessageBox(hWnd, szMessage, szTitle, MB_APPLMODAL|MB_OK|MB_ICONSTOP|MB_SETFOREGROUND);} +static __inline int YesNoMessage(LPCTSTR szMessage) {return MessageBox(hWnd, szMessage, szTitle, MB_APPLMODAL|MB_YESNO|MB_ICONEXCLAMATION|MB_SETFOREGROUND);} +static __inline int YesNoCancelMessage(LPCTSTR szMessage,UINT uStyle) {return MessageBox(hWnd, szMessage, szTitle, MB_APPLMODAL|MB_YESNOCANCEL|MB_ICONEXCLAMATION|MB_SETFOREGROUND|uStyle);} + +// Missing Win32 API calls +static __inline LPTSTR DuplicateString(LPCTSTR szString) +{ + UINT uLength = lstrlen(szString) + 1; + LPTSTR szDup = HeapAlloc(hHeap,0,uLength*sizeof(szDup[0])); + lstrcpy(szDup,szString); + return szDup; +} + +#define SCREENHEIGHT (cCurrentRomType=='Q' ? 80 : 64) // CdB for HP: add apples display management +#define SCREENHEIGHTREAL ((cCurrentRomType=='Q') ? (80-Chipset.d0size) : 64) +#define MAINSCREENHEIGHT (((Chipset.lcounter) == 0) ? SCREENHEIGHTREAL : SCREENHEIGHTREAL-64+((Chipset.lcounter)+1)) +#define MENUHEIGHT (Chipset.lcounter==0?0:64-(Chipset.lcounter+1)) diff --git a/SOURCE/EMU48.ICO b/SOURCE/EMU48.ICO new file mode 100644 index 0000000000000000000000000000000000000000..82fc1a73569ab0c7218b5a583c7a2f8860c833bd GIT binary patch literal 1078 zcmcgrI}(C05PhICbcP#9<#-H_lnxp+9duraqu6Olhn4Yd0)u8yMrZWF{_>VxNERSM zj4VqbxWvGQHcfrJjeykx*m5nS>?jeI)PP~`Gz>{H#&|qjC?^FQ{DS2{FbYzn|gD8Gf!lr2U)zj{YHEIlTV + + +Emu48 + + + + + + diff --git a/SOURCE/EMU48DLL.C b/SOURCE/EMU48DLL.C new file mode 100644 index 0000000..c1dd194 --- /dev/null +++ b/SOURCE/EMU48DLL.C @@ -0,0 +1,585 @@ +/* + * Emu48Dll.c + * + * This file is part of Emu48 + * + * Copyright (C) 2000 Christoph Gießelink + * + */ + +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "io.h" +#include "kml.h" +#include "debugger.h" + +#include "Emu48Dll.h" + +static LPCTSTR pArgv[3]; // command line memory +static HACCEL hAccel; // accelerator table + +static HSZ hszService, hszTopic; // variables for DDE server + +extern LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM); + +// callback function notify document filename +VOID (CALLBACK *pEmuDocumentNotify)(LPCTSTR lpszFilename) = NULL; + +// callback function notify Emu48 closed +static VOID (CALLBACK *pEmuClose)(VOID) = NULL; + +//################ +//# +//# Public internal functions +//# +//################ + +// +// DLLCreateWnd +// +BOOL DLLCreateWnd(LPCTSTR lpszFilename, LPCTSTR lpszPort2Name) +{ + WNDCLASS wc; + RECT rectWindow; + + BOOL bFileExist = FALSE; // state file don't exist + + hApp = GetModuleHandle(_T("EMU48.DLL")); + if (hApp == NULL) return TRUE; + + hHeap = GetProcessHeap(); // get process heap + if (hHeap == NULL) return TRUE; + + nArgc = 1; // no argument + if (lpszFilename[0]) + { + // try to open given filename + HANDLE hFile = CreateFile(lpszFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); + if (bFileExist = (hFile != INVALID_HANDLE_VALUE)) + CloseHandle(hFile); + + ppArgv = pArgv; // command line arguments + nArgc = 2; // one argument: state file, no port2 file + pArgv[1] = lpszFilename; // name of state file + + if (lpszPort2Name) // port2 filename + { + nArgc = 3; // two arguments: state file, port2 file + pArgv[2] = lpszPort2Name; // name of port2 file + } + } + + wc.style = CS_BYTEALIGNCLIENT; + wc.lpfnWndProc = (WNDPROC)MainWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hApp; + wc.hIcon = LoadIcon(hApp, MAKEINTRESOURCE(IDI_EMU48)); + wc.hCursor = NULL; + wc.hbrBackground = NULL; + wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU); + wc.lpszClassName = _T("CEmu48"); + RegisterClass(&wc); + + // Create window + rectWindow.left = 0; + rectWindow.top = 0; + rectWindow.right = 256; + rectWindow.bottom = 0; + AdjustWindowRect(&rectWindow, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, TRUE); + + hWnd = CreateWindow(_T("CEmu48"), _T("Emu48"), + WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, + CW_USEDEFAULT, CW_USEDEFAULT, + rectWindow.right - rectWindow.left, + rectWindow.bottom - rectWindow.top, + NULL,NULL,hApp,NULL + ); + + if (hWnd == NULL) + { + UnregisterClass(_T("CEmu48"),hApp); + return TRUE; + } + + VERIFY(hAccel = LoadAccelerators(hApp,MAKEINTRESOURCE(IDR_MENU))); + + // remove debugger menu entry from resource + DeleteMenu(GetMenu(hWnd),ID_TOOL_DEBUG,MF_BYCOMMAND); + + // initialization + EmuClearAllBreakpoints(); + QueryPerformanceFrequency(&lFreq); // init high resolution counter + + GetCurrentDirectory(sizeof(szCurrentDirectory), szCurrentDirectory); + szCurrentKml[0] = 0; // no KML file selected + + ReadSettings(); + + UpdateWindowStatus(); + + // create shutdown auto event handle + hEventShutdn = CreateEvent(NULL,FALSE,FALSE,NULL); + if (hEventShutdn == NULL) + { + DestroyWindow(hWnd); + return TRUE; + } + + // create debugger auto event handle + hEventDebug = CreateEvent(NULL,FALSE,FALSE,NULL); + if (hEventDebug == NULL) + { + CloseHandle(hEventShutdn); // close shutdown event handle + DestroyWindow(hWnd); + return TRUE; + } + + nState = SM_RUN; // init state must be <> nNextState + nNextState = SM_INVALID; // go into invalid state + hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&WorkerThread, NULL, CREATE_SUSPENDED, &lThreadId); + if (hThread == NULL) + { + CloseHandle(hEventDebug); // close debugger event handle + CloseHandle(hEventShutdn); // close event handle + DestroyWindow(hWnd); + return TRUE; + } + // on multiprocessor machines for QueryPerformanceCounter() + SetThreadAffinityMask(hThread,1); + ResumeThread(hThread); // start thread + while (nState!=nNextState) Sleep(0); // wait for thread initialized + + idDdeInst = 0; // initialize DDE server + if (DdeInitialize(&idDdeInst,(PFNCALLBACK) &DdeCallback, + APPCLASS_STANDARD | + CBF_FAIL_EXECUTES | CBF_FAIL_ADVISES | + CBF_SKIP_REGISTRATIONS | CBF_SKIP_UNREGISTRATIONS,0)) + { + TerminateThread(hThread, 0); // kill emulation thread + CloseHandle(hEventDebug); // close debugger event handle + CloseHandle(hEventShutdn); // close event handle + DestroyWindow(hWnd); + return TRUE; + } + + _ASSERT(hWnd != NULL); + _ASSERT(hWindowDC != NULL); + + szBufferFilename[0] = 0; + if (bFileExist) // open existing file + { + lstrcpyn(szBufferFilename,ppArgv[1],ARRAYSIZEOF(szBufferFilename)); + } + if (nArgc == 1) // no argument + { + ReadLastDocument(szBufferFilename,ARRAYSIZEOF(szBufferFilename)); + } + + if (szBufferFilename[0]) // given default document + { + TCHAR szTemp[MAX_PATH+8] = _T("Loading "); + RECT rectClient; + + _ASSERT(hWnd != NULL); + VERIFY(GetClientRect(hWnd,&rectClient)); + GetCutPathName(szBufferFilename,&szTemp[8],MAX_PATH,rectClient.right/10-8); + SetWindowTitle(szTemp); + if (OpenDocument(szBufferFilename)) + goto start; + } + + SetWindowTitle(_T("New Document")); + if (NewDocument()) + { + if (nArgc >= 2) + SaveDocumentAs(ppArgv[1]); + else + SetWindowTitle(_T("Untitled")); + goto start; + } + + DestroyWindow(hWnd); // clean up system + return TRUE; + +start: + // init clipboard format and name service + uCF_HpObj = RegisterClipboardFormat(_T(CF_HPOBJ)); + hszService = DdeCreateStringHandle(idDdeInst,szAppName,0); + hszTopic = DdeCreateStringHandle(idDdeInst,szTopic,0); + DdeNameService(idDdeInst,hszService,NULL,DNS_REGISTER); + + ShowWindow(hWnd, SW_SHOW); + + if (pbyRom) SwitchToState(SM_RUN); + + return FALSE; +} + +// +// DLLDestroyWnd +// +BOOL DLLDestroyWnd(VOID) +{ + // clean up DDE server + DdeNameService(idDdeInst, hszService, NULL, DNS_UNREGISTER); + DdeFreeStringHandle(idDdeInst, hszService); + DdeFreeStringHandle(idDdeInst, hszTopic); + DdeUninitialize(idDdeInst); + + WriteSettings(); // save variable settings + + CloseHandle(hThread); // close emulation thread handle + CloseHandle(hEventShutdn); // close shutdown event handle + CloseHandle(hEventDebug); // close debugger event handle + _ASSERT(nState == SM_RETURN); // emulation thread down? + ResetDocument(); + ResetBackup(); + _ASSERT(pbyRom == NULL); // rom file unmapped + _ASSERT(pbyPort2 == NULL); // port2 file unmapped + _ASSERT(pKml == NULL); // KML script not closed + _ASSERT(szTitle == NULL); // freed allocated memory + _ASSERT(hPalette == NULL); // freed resource memory + if (pEmuClose) pEmuClose(); // call notify function + return FALSE; +} + + +//################ +//# +//# Public external functions +//# +//################ + +/**************************************************************************** +* EmuCreate +***************************************************************************** +* +* @func start Emu48 and load Ram file into emulator, if Ram file don't +* exist create a new one and save it under the given name +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuCreate( + LPCTSTR lpszFilename) // @parm String with RAM filename +{ + return DLLCreateWnd(lpszFilename, NULL); +} + +/**************************************************************************** +* EmuCreateEx +***************************************************************************** +* +* @func start Emu48 and load Ram and Port2 file into emulator, if Ram file +* don't exist create a new one and save it under the given name +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuCreateEx( + LPCTSTR lpszFilename, // @parm String with RAM filename + LPCTSTR lpszPort2Name) // @parm String with Port2 filename + // or NULL for using name inside INI file +{ + return DLLCreateWnd(lpszFilename, lpszPort2Name); +} + +/**************************************************************************** +* EmuDestroy +***************************************************************************** +* +* @func close Emu48, free all memory +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuDestroy(VOID) +{ + if (hWnd == NULL) return TRUE; // Emu48 closed + + // close Emu48 via exit + SendMessage(hWnd,WM_SYSCOMMAND,SC_CLOSE,0); + return FALSE; +} + +/**************************************************************************** +* EmuAcceleratorTable +***************************************************************************** +* +* @func load accelerator table of emulator +* +* @xref none +* +* @rdesc HACCEL: handle of the loaded accelerator table +* +****************************************************************************/ + +DECLSPEC HACCEL CALLBACK EmuAcceleratorTable( + HWND *phEmuWnd) // @parm return of emulator window handle +{ + *phEmuWnd = hWnd; + return hAccel; +} + +/**************************************************************************** +* EmuCallBackClose +***************************************************************************** +* +* @func init CallBack handler to notify caller when Emu48 window close +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuCallBackClose( + VOID (CALLBACK *EmuClose)(VOID)) // @parm CallBack function notify caller Emu48 closed +{ + pEmuClose = EmuClose; // set new handler + return; +} + +/**************************************************************************** +* EmuCallBackDocumentNotify +***************************************************************************** +* +* @func init CallBack handler to notify caller for actual document file +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuCallBackDocumentNotify( + VOID (CALLBACK *EmuDocumentNotify)(LPCTSTR lpszFilename)) // @parm CallBack function notify document filename +{ + pEmuDocumentNotify = EmuDocumentNotify; // set new handler + return; +} + +/**************************************************************************** +* EmuLoadRamFile +***************************************************************************** +* +* @func load Ram file into emulator +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error (old file reloaded) +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuLoadRamFile( + LPCTSTR lpszFilename) // @parm String with RAM filename +{ + BOOL bErr; + + if (pbyRom) SwitchToState(SM_INVALID); // stop emulation thread + bErr = !OpenDocument(lpszFilename); // load state file + if (pbyRom) SwitchToState(SM_RUN); // restart emulation thread + return bErr; +} + +/**************************************************************************** +* EmuSaveRamFile +***************************************************************************** +* +* @func save the current emulator Ram to file +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error (old file reloaded) +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuSaveRamFile(VOID) +{ + BOOL bErr; + + if (pbyRom == NULL) return TRUE; // fail + SwitchToState(SM_INVALID); // stop emulation thread + bErr = !SaveDocument(); // save current state file + SwitchToState(SM_RUN); // restart emulation thread + return bErr; +} + +/**************************************************************************** +* EmuLoadObject +***************************************************************************** +* +* @func load object file to stack +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuLoadObject( + LPCTSTR lpszObjectFilename) // @parm String with object filename +{ + HANDLE hFile; + DWORD dwFileSizeLow; + DWORD dwFileSizeHigh; + LPBYTE lpBuf; + WORD wError = S_ERR_BINARY; // set into error state + + SuspendDebugger(); // suspend debugger + bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control + + if (!(Chipset.IORam[BITOFFSET]&DON)) // calculator off, turn on + { + KeyboardEvent(TRUE,0,0x8000); + Sleep(200); + KeyboardEvent(FALSE,0,0x8000); + Sleep(200); + // wait for sleep mode + while(Chipset.Shutdn == FALSE) Sleep(0); + } + + if (nState != SM_RUN) goto cancel; // emulator must run to load on object + if (WaitForSleepState()) goto cancel; // wait for cpu SHUTDN then sleep state + + _ASSERT(nState == SM_SLEEP); + + hFile = CreateFile(lpszObjectFilename,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_FLAG_SEQUENTIAL_SCAN,NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + SwitchToState(SM_RUN); // run state + goto cancel; + } + dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh); + if (dwFileSizeHigh != 0) + { // file is too large. + SwitchToState(SM_RUN); // run state + CloseHandle(hFile); + goto cancel; + } + lpBuf = HeapAlloc(hHeap,0,dwFileSizeLow*2); + if (lpBuf == NULL) + { + SwitchToState(SM_RUN); // run state + CloseHandle(hFile); + goto cancel; + } + ReadFile(hFile, lpBuf+dwFileSizeLow, dwFileSizeLow, &dwFileSizeHigh, NULL); + CloseHandle(hFile); + + wError = WriteStack(1,lpBuf,dwFileSizeLow); + + HeapFree(hHeap,0,lpBuf); + + SwitchToState(SM_RUN); // run state + while (nState!=nNextState) Sleep(0); + _ASSERT(nState == SM_RUN); + KeyboardEvent(TRUE,0,0x8000); + Sleep(200); + KeyboardEvent(FALSE,0,0x8000); + while(Chipset.Shutdn == FALSE) Sleep(0); + +cancel: + bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control + ResumeDebugger(); + return wError != S_ERR_NO; +} + +/**************************************************************************** +* EmuSaveObject +***************************************************************************** +* +* @func save object on stack to file +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +DECLSPEC BOOL CALLBACK EmuSaveObject( + LPCTSTR lpszObjectFilename) // @parm String with object filename +{ + BOOL bErr; + + if (nState != SM_RUN) return TRUE; // emulator must run to load on object + if (WaitForSleepState()) return TRUE; // wait for cpu SHUTDN then sleep state + _ASSERT(nState == SM_SLEEP); + bErr = !SaveObject(lpszObjectFilename); + SwitchToState(SM_RUN); + return bErr; +} + +/**************************************************************************** +* EmuCalculatorType +***************************************************************************** +* +* @func get ID of current calculator type +* +* @xref none +* +* @rdesc BYTE: '6' = HP38G with 64KB RAM +* 'A' = HP38G +* 'E' = HP39/40G +* 'S' = HP48SX +* 'G' = HP48GX +* 'X' = HP49G +* 'P' = HP39G+ +* '2' = HP48GII +* 'Q' = HP49G+ +* +****************************************************************************/ + +DECLSPEC BYTE CALLBACK EmuCalculatorType(VOID) +{ + return Chipset.type; +} + +/**************************************************************************** +* EmuSimulateKey +***************************************************************************** +* +* @func simulate a key action +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuSimulateKey( + BOOL bKeyState, // @parm TRUE = pressed, FALSE = released + UINT out, // @parm key out line + UINT in) // @parm key in line +{ + KeyboardEvent(bKeyState,out,in); +} + +/**************************************************************************** +* EmuPressOn +***************************************************************************** +* +* @func press On key (emulation must run) +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +DECLSPEC VOID CALLBACK EmuPressOn( + BOOL bKeyState) // @parm TRUE = pressed, FALSE = released +{ + KeyboardEvent(bKeyState,0,0x8000); +} \ No newline at end of file diff --git a/SOURCE/EMU48DLL.DSP b/SOURCE/EMU48DLL.DSP new file mode 100644 index 0000000..c0a9bf7 --- /dev/null +++ b/SOURCE/EMU48DLL.DSP @@ -0,0 +1,256 @@ +# Microsoft Developer Studio Project File - Name="Emu48" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=Emu48 - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "EMU48DLL.MAK". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "EMU48DLL.MAK" CFG="Emu48 - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Emu48 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "Emu48 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Emu48 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir ".\Release" +# PROP BASE Intermediate_Dir ".\Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\Release" +# PROP Intermediate_Dir ".\Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /c +# ADD CPP /nologo /Gr /MT /W3 /GX /O2 /Ob2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /D "_MBCS" /D "_USRDLL" /D "REGISTRY" /Yu"pch.h" /FD /D REGISTRYKEY=\"Software\\Hewlett-Packard\\Debug4x\\Emu48\" /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib advapi32.lib /nologo /dll /machine:I386 /out:".\Release/Emu48.dll" + +!ELSEIF "$(CFG)" == "Emu48 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir ".\Debug" +# PROP BASE Intermediate_Dir ".\Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\Debug" +# PROP Intermediate_Dir ".\Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /D "_MBCS" /D "_USRDLL" /D "REGISTRY" /FR /Yu"pch.h" /FD /D REGISTRYKEY=\"Software\\Hewlett-Packard\\Debug4x\\Emu48\" /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib advapi32.lib /nologo /dll /debug /machine:I386 /out:".\Debug/Emu48.dll" + +!ENDIF + +# Begin Target + +# Name "Emu48 - Win32 Release" +# Name "Emu48 - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\apple.c +# End Source File +# Begin Source File + +SOURCE=.\cursor.c +# End Source File +# Begin Source File + +SOURCE=.\ddeserv.c +# End Source File +# Begin Source File + +SOURCE=.\debugdll.c +# End Source File +# Begin Source File + +SOURCE=.\disasm.c +# End Source File +# Begin Source File + +SOURCE=.\display.c +# End Source File +# Begin Source File + +SOURCE=.\Emu48.c +# End Source File +# Begin Source File + +SOURCE=.\Emu48.rc +# End Source File +# Begin Source File + +SOURCE=.\Emu48dll.c +# End Source File +# Begin Source File + +SOURCE=.\engine.c +# End Source File +# Begin Source File + +SOURCE=.\external.c +# End Source File +# Begin Source File + +SOURCE=.\fetch.c +# End Source File +# Begin Source File + +SOURCE=.\files.c +# End Source File +# Begin Source File + +SOURCE=.\i28f160.c +# End Source File +# Begin Source File + +SOURCE=.\keyboard.c +# End Source File +# Begin Source File + +SOURCE=.\keymacro.c +# End Source File +# Begin Source File + +SOURCE=.\kml.c +# End Source File +# Begin Source File + +SOURCE=.\mops.c +# End Source File +# Begin Source File + +SOURCE=.\opcodes.c +# End Source File +# Begin Source File + +SOURCE=.\pch.c +# ADD CPP /Yc"pch.h" +# End Source File +# Begin Source File + +SOURCE=.\rpl.c +# End Source File +# Begin Source File + +SOURCE=.\serial.c +# End Source File +# Begin Source File + +SOURCE=.\settings.c +# End Source File +# Begin Source File + +SOURCE=.\stack.c +# End Source File +# Begin Source File + +SOURCE=.\timer.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\apple.h +# End Source File +# Begin Source File + +SOURCE=.\color.h +# End Source File +# Begin Source File + +SOURCE=.\debugger.h +# End Source File +# Begin Source File + +SOURCE=.\Emu48.h +# End Source File +# Begin Source File + +SOURCE=.\Emu48dll.h +# End Source File +# Begin Source File + +SOURCE=.\i28f160.h +# End Source File +# Begin Source File + +SOURCE=.\io.h +# End Source File +# Begin Source File + +SOURCE=.\kml.h +# End Source File +# Begin Source File + +SOURCE=.\opcodes.h +# End Source File +# Begin Source File + +SOURCE=.\ops.h +# End Source File +# Begin Source File + +SOURCE=.\pch.h +# End Source File +# Begin Source File + +SOURCE=.\types.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\Emu48.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/SOURCE/EMU48DLL.DSW b/SOURCE/EMU48DLL.DSW new file mode 100644 index 0000000..e6c40c6 --- /dev/null +++ b/SOURCE/EMU48DLL.DSW @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Emu48"=".\EMU48DLL.DSP" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/SOURCE/EMU48DLL.H b/SOURCE/EMU48DLL.H new file mode 100644 index 0000000..6baa01a --- /dev/null +++ b/SOURCE/EMU48DLL.H @@ -0,0 +1,642 @@ +/* + * Emu48Dll.h + * + * This file is part of Emu48 + * + * Copyright (C) 2000 Christoph Gießelink + * + */ + +#define DECLSPEC __declspec(dllexport) + +////////////////////////////////// +// +// breakpoint type definitions +// +////////////////////////////////// + +#define BP_EXEC 0x01 // code breakpoint +#define BP_READ 0x02 // read memory breakpoint +#define BP_WRITE 0x04 // write memory breakpoint +#define BP_RPL 0x08 // RPL breakpoint +#define BP_ACCESS (BP_READ|BP_WRITE) // read/write memory breakpoint + +#define BP_ROM 0x8000 // absolute ROM adress breakpoint + +////////////////////////////////// +// +// REGISTER ACCESS API +// +////////////////////////////////// + +#define EMU_REGISTER_PC 0 +#define EMU_REGISTER_D0 1 +#define EMU_REGISTER_D1 2 +#define EMU_REGISTER_DUMMY 3 +#define EMU_REGISTER_AL 4 +#define EMU_REGISTER_AH 5 +#define EMU_REGISTER_BL 6 +#define EMU_REGISTER_BH 7 +#define EMU_REGISTER_CL 8 +#define EMU_REGISTER_CH 9 +#define EMU_REGISTER_DL 10 +#define EMU_REGISTER_DH 11 +#define EMU_REGISTER_R0L 12 +#define EMU_REGISTER_R0H 13 +#define EMU_REGISTER_R1L 14 +#define EMU_REGISTER_R1H 15 +#define EMU_REGISTER_R2L 16 +#define EMU_REGISTER_R2H 17 +#define EMU_REGISTER_R3L 18 +#define EMU_REGISTER_R3H 19 +#define EMU_REGISTER_R4L 20 +#define EMU_REGISTER_R4H 21 +#define EMU_REGISTER_R5L 22 +#define EMU_REGISTER_R5H 23 +#define EMU_REGISTER_R6L 24 +#define EMU_REGISTER_R6H 25 +#define EMU_REGISTER_R7L 26 +#define EMU_REGISTER_R7H 27 +#define EMU_REGISTER_FLAGS 28 +#define EMU_REGISTER_OUT 29 +#define EMU_REGISTER_IN 30 +#define EMU_REGISTER_VIEW1 31 +#define EMU_REGISTER_VIEW2 32 +#define EMU_REGISTER_RSTKP 63 +#define EMU_REGISTER_RSTK0 64 +#define EMU_REGISTER_RSTK1 65 +#define EMU_REGISTER_RSTK2 66 +#define EMU_REGISTER_RSTK3 67 +#define EMU_REGISTER_RSTK4 68 +#define EMU_REGISTER_RSTK5 69 +#define EMU_REGISTER_RSTK6 70 +#define EMU_REGISTER_RSTK7 71 +#define EMU_REGISTER_CLKL 72 +#define EMU_REGISTER_CLKH 73 +#define EMU_REGISTER_CRC 74 + +/** + * "FLAGS" register format : + * + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ST |S|x|x|x|K|I|C|M| HST | P | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * M : Mode (0:Hex, 1:Dec) + * C : Carry + * I : Interrupt pending + * K : KDN Interrupts Enabled + * S : Shutdn Flag (read only) + * x : reserved + */ + +/**************************************************************************** +* EmuCreate +***************************************************************************** +* +* @func start Emu48 and load Ram file into emulator, if Ram file don't +* exist create a new one and save it under the given name +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuCreate( + LPCTSTR lpszFilename); // @parm String with RAM filename + +/**************************************************************************** +* EmuCreateEx +***************************************************************************** +* +* @func start Emu48 and load Ram and Port2 file into emulator, if Ram file +* don't exist create a new one and save it under the given name +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuCreateEx( + LPCTSTR lpszFilename, // @parm String with RAM filename + LPCTSTR lpszPort2Name); // @parm String with Port2 filename + // or NULL for using name inside INI file + +/**************************************************************************** +* EmuDestroy +***************************************************************************** +* +* @func close Emu48, free all memory +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuDestroy(VOID); + +/**************************************************************************** +* EmuAcceleratorTable +***************************************************************************** +* +* @func load accelerator table of emulator +* +* @xref none +* +* @rdesc HACCEL: handle of the loaded accelerator table +* +****************************************************************************/ + +EXTERN_C DECLSPEC HACCEL CALLBACK EmuAcceleratorTable( + HWND *phEmuWnd); // @parm return of emulator window handle + +/**************************************************************************** +* EmuCallBackClose +***************************************************************************** +* +* @func init CallBack handler to notify caller when Emu48 window close +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuCallBackClose( + VOID (CALLBACK *EmuClose)(VOID)); // @parm CallBack function notify caller Emu48 closed + +/**************************************************************************** +* EmuCallBackDocumentNotify +***************************************************************************** +* +* @func init CallBack handler to notify caller for actual document file +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuCallBackDocumentNotify( + VOID (CALLBACK *EmuDocumentNotify)(LPCTSTR lpszFilename)); // @parm CallBack function notify document filename + +/**************************************************************************** +* EmuLoadRamFile +***************************************************************************** +* +* @func load Ram file into emulator +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error (old file reloaded) +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuLoadRamFile( + LPCTSTR lpszFilename); // @parm String with RAM filename + +/**************************************************************************** +* EmuSaveRamFile +***************************************************************************** +* +* @func save the current emulator Ram to file +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error (old file reloaded) +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuSaveRamFile(VOID); + +/**************************************************************************** +* EmuLoadObject +***************************************************************************** +* +* @func load object file to stack +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuLoadObject( + LPCTSTR lpszObjectFilename); // @parm String with object filename + +/**************************************************************************** +* EmuSaveObject +***************************************************************************** +* +* @func save object on stack to file +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuSaveObject( + LPCTSTR lpszObjectFilename); // @parm String with object filename + +/**************************************************************************** +* EmuCalculatorType +***************************************************************************** +* +* @func get ID of current calculator type +* +* @xref none +* +* @rdesc BYTE: '6' = HP38G with 64KB RAM +* 'A' = HP38G +* 'E' = HP39/40G +* 'S' = HP48SX +* 'G' = HP48GX +* 'X' = HP49G +* 'P' = HP39G+ +* '2' = HP48GII +* 'Q' = HP49G+ +* +****************************************************************************/ + +EXTERN_C DECLSPEC BYTE CALLBACK EmuCalculatorType(VOID); + +/**************************************************************************** +* EmuSimulateKey +***************************************************************************** +* +* @func simulate a key action +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuSimulateKey( + BOOL bKeyState, // @parm TRUE = pressed, FALSE = released + UINT out, // @parm key out line + UINT in); // @parm key in line + +/**************************************************************************** +* EmuPressOn +***************************************************************************** +* +* @func press On key (emulation must run) +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuPressOn( + BOOL bKeyState); // @parm TRUE = pressed, FALSE = released + +/**************************************************************************** +* EmuInitLastInstr +***************************************************************************** +* +* @func init a circular buffer area for saving the last instruction +* addresses +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuInitLastInstr( + WORD wNoInstr, // @parm number of saved instructions, + // 0 = frees the memory buffer + DWORD *pdwArray); // @parm pointer to linear array + +/**************************************************************************** +* EmuGetLastInstr +***************************************************************************** +* +* @func return number of valid entries in the last instruction array, +* each entry contents a PC address, array[0] contents the oldest, +* array[*pwNoEntries-1] the last PC address +* +* @xref none +* +* @rdesc BOOL: FALSE = OK, TRUE = Error +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuGetLastInstr( + WORD *pwNoEntries); // @parm return number of valid entries in array + +/**************************************************************************** +* EmuRun +***************************************************************************** +* +* @func run emulation +* +* @xref none +* +* @rdesc BOOL: FALSE = OK +* TRUE = Error, Emu48 is running +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuRun(VOID); + +/**************************************************************************** +* EmuRunPC +***************************************************************************** +* +* @func run emulation until stop address +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuRunPC( + DWORD dwAddressPC); // @parm breakpoint address + +/**************************************************************************** +* EmuStep +***************************************************************************** +* +* @func execute one ASM instruction and return to caller +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuStep(VOID); + +/**************************************************************************** +* EmuStepOver +***************************************************************************** +* +* @func execute one ASM instruction but skip GOSUB, GOSUBL, GOSBVL +* subroutines +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuStepOver(VOID); + +/**************************************************************************** +* EmuStepOut +***************************************************************************** +* +* @func run emulation until a RTI, RTN, RTNC, RTNCC, RTNNC, RTNSC, RTNSXN, +* RTNYES instruction is found above the current stack level +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuStepOut(VOID); + +/**************************************************************************** +* EmuStop +***************************************************************************** +* +* @func break emulation +* +* @xref none +* +* @rdesc BOOL: FALSE = OK +* TRUE = Error, no debug notify handler +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuStop(VOID); + +/**************************************************************************** +* EmuCallBackDebugNotify +***************************************************************************** +* +* @func init CallBack handler to notify caller on debugger breakpoint +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuCallBackDebugNotify( + VOID (CALLBACK *EmuDbgNotify)(INT nBreaktype)); // @parm CallBack function notify Debug breakpoint + +/**************************************************************************** +* EmuCallBackStackNotify +***************************************************************************** +* +* @func init CallBack handler to notify caller on hardware stack change; +* if the CallBack function return TRUE, emulation stops behind the +* opcode changed hardware stack content, otherwise, if the CallBack +* function return FALSE, no breakpoint is set +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuCallBackStackNotify( + BOOL (CALLBACK *EmuStackNotify)(VOID)); // @parm CallBack function notify stack changed + +/**************************************************************************** +* EmuGetRegister +***************************************************************************** +* +* @func read a 32 bit register +* +* @xref none +* +* @rdesc DWORD: 32 bit value of register +* +****************************************************************************/ + +EXTERN_C DECLSPEC DWORD CALLBACK EmuGetRegister( + UINT uRegister); // @parm index of register + +/**************************************************************************** +* EmuSetRegister +***************************************************************************** +* +* @func write a 32 bit register +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuSetRegister( + UINT uRegister, // @parm index of register + DWORD dwValue); // @parm new 32 bit value + +/**************************************************************************** +* EmuGetMem +***************************************************************************** +* +* @func read one nibble from the specified mapped address +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuGetMem( + DWORD dwMapAddr, // @parm mapped address of Saturn CPU + BYTE *pbyValue); // @parm readed nibble + +/**************************************************************************** +* EmuSetMem +***************************************************************************** +* +* @func write one nibble to the specified mapped address +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuSetMem( + DWORD dwMapAddr, // @parm mapped address of Saturn CPU + BYTE byValue); // @parm nibble to write + +/**************************************************************************** +* EmuGetRom +***************************************************************************** +* +* @func return size and base address of mapped ROM +* +* @xref none +* +* @rdesc LPBYTE: base address of ROM (pointer to original data) +* +****************************************************************************/ + +EXTERN_C DECLSPEC LPBYTE CALLBACK EmuGetRom( + DWORD *pdwRomSize); // @parm return size of ROM in nibbles + +/**************************************************************************** +* EmuSetBreakpoint +***************************************************************************** +* +* @func set ASM code/data breakpoint +* +* @xref none +* +* @rdesc BOOL: TRUE = Error, Breakpoint table full +* FALSE = OK, Breakpoint set +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuSetBreakpoint( + DWORD dwAddress, // @parm breakpoint address to set + UINT nBreakpointType); // @parm breakpoint type to set + +/**************************************************************************** +* EmuClearBreakpoint +***************************************************************************** +* +* @func clear ASM code/data breakpoint +* +* @xref none +* +* @rdesc BOOL: TRUE = Error, Breakpoint not found +* FALSE = OK, Breakpoint cleared +* +****************************************************************************/ + +EXTERN_C DECLSPEC BOOL CALLBACK EmuClearBreakpoint( + DWORD dwAddress, // @parm breakpoint address to clear + UINT nBreakpointType); // @parm breakpoint type to clear + +/**************************************************************************** +* EmuClearAllBreakpoints +***************************************************************************** +* +* @func clear all breakpoints +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuClearAllBreakpoints(VOID); + +/**************************************************************************** +* EmuEnableNop3Breakpoint +***************************************************************************** +* +* @func enable/disable NOP3 breakpoint +* stop emulation at Opcode 420 for GOC + (next line) +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuEnableNop3Breakpoint( + BOOL bEnable); // @parm stop on NOP3 opcode + +/**************************************************************************** +* EmuEnableDocodeBreakpoint +***************************************************************************** +* +* @func enable/disable DOCODE breakpoint +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuEnableDoCodeBreakpoint( + BOOL bEnable); // @parm stop on DOCODE entry + +/**************************************************************************** +* EmuEnableRplBreakpoint +***************************************************************************** +* +* @func enable/disable RPL breakpoint +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuEnableRplBreakpoint( + BOOL bEnable); // @parm stop on RPL exit + +/**************************************************************************** +* EmuEnableSkipInterruptCode +***************************************************************************** +* +* @func enable/disable skip single step execution inside the interrupt +* handler, this option has no effect on code and data breakpoints +* +* @xref none +* +* @rdesc VOID +* +****************************************************************************/ + +EXTERN_C DECLSPEC VOID CALLBACK EmuEnableSkipInterruptCode( + BOOL bEnable); // @parm TRUE = skip code instructions + // inside interrupt service routine diff --git a/SOURCE/ENGINE.C b/SOURCE/ENGINE.C new file mode 100644 index 0000000..6e189bc --- /dev/null +++ b/SOURCE/ENGINE.C @@ -0,0 +1,632 @@ +/* + * engine.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "Emu48.h" +#include "Opcodes.h" +#include "io.h" +#include "debugger.h" + +#define SAMPLE 16384 // speed adjust sample frequency + +BOOL bInterrupt = FALSE; +UINT nState = SM_INVALID; +UINT nNextState = SM_RUN; +BOOL bRealSpeed = FALSE; +BOOL bKeySlow = FALSE; // slow down for key emulation +BOOL bCommInit = FALSE; // COM port not open + +CHIPSET Chipset; + +TCHAR szSerialWire[16]; // devicename for wire port +TCHAR szSerialIr[16]; // devicename for IR port + +DWORD dwSXCycles = 82; // SX cpu cycles in interval +DWORD dwGXCycles = 123; // GX cpu cycles in interval +DWORD dwGPCycles = 123*3; // G+ cpu cycles in interval // CdB for HP: add apples display management +DWORD dwG2Cycles = 123*2; // Gii cpu cycles in interval // CdB for HP: add apples display management + +// variables for debugger engine +HANDLE hEventDebug; // event handle to stop cpu thread + +BOOL bDbgAutoStateCtrl = TRUE; // debugger suspend control by SwitchToState() +INT nDbgState = DBG_OFF; // state of debugger + +BOOL bDbgNOP3 = FALSE; // halt on NOP3 (#420 opcode) +BOOL bDbgRPL = FALSE; // halt on RPL entry +BOOL bDbgCode = FALSE; // halt on DOCODE entry + +BOOL bDbgSkipInt = FALSE; // execute interrupt handler + +DWORD dwDbgStopPC = -1; // stop address for goto cursor +DWORD dwDbgRplPC = -1; // stop address for RPL breakpoint + +DWORD dwDbgRstkp; // stack recursion level of step over end +DWORD dwDbgRstk; // possible return address + +DWORD *pdwInstrArray = NULL; // last instruction array +WORD wInstrSize; // size of last instruction array +WORD wInstrWp; // write pointer of instruction array +WORD wInstrRp; // read pointer of instruction array + +static INT nDbgRplBreak = BN_ASM; // flag for RPL breakpoint detection +static INT nDbgOldState = DBG_OFF; // old state of debugger for suspend/resume + +static BOOL bCpuSlow = FALSE; // enable/disable real speed + +static DWORD dwEDbgT2 = 0; // debugger timer2 emulation +static DWORD dwEDbgCycles = 0; // debugger cycle counter + +static DWORD dwOldCyc; // cpu cycles at last event +static DWORD dwSpeedRef; // timer value at last event +static DWORD dwTickRef; // sample timer ticks + +#include "Ops.h" + +// save last instruction in circular instruction buffer +static __inline VOID SaveInstrAddr(DWORD dwAddr) +{ + if (pdwInstrArray) // circular buffer allocated + { + pdwInstrArray[wInstrWp] = dwAddr; + wInstrWp = (wInstrWp + 1) % wInstrSize; + if (wInstrWp == wInstrRp) + wInstrRp = (wInstrRp + 1) % wInstrSize; + } + return; +} + +static __inline VOID Debugger(VOID) // debugger part +{ + LARGE_INTEGER lDummyInt; // sample timer ticks + BOOL bStopEmulation; + LPBYTE I = FASTPTR(Chipset.pc); // get opcode stream + + UpdateDbgCycleCounter(); // update 64 bit cpu cycle counter + SaveInstrAddr(Chipset.pc); // save pc in last instruction buffer + + nDbgRplBreak = BN_ASM; // notify ASM breakpoint + + // check for code breakpoints + bStopEmulation = CheckBreakpoint(Chipset.pc, 1, BP_EXEC); + + // check for memory breakpoints, opcode #14x or #15x + if (I[0] == 0x1 && (I[1] == 0x4 || I[1] == 0x5)) + { + DWORD dwData = (I[2] & 0x1) ? Chipset.d1 : Chipset.d0; + UINT nType = (I[2] & 0x2) ? BP_READ : BP_WRITE; + + DWORD dwRange; + if (I[1] == 0x4) // B,A + { + dwRange = (I[2] & 0x8) ? 2 : 5; + } + else // number of nibbles, (P,WP,XS,X,S,M,W) + { + dwRange = (I[2] & 0x8) ? (I[3]+1) : (F_l[I[3]]); + } + #if defined DEBUG_DEBUGGER + { + TCHAR buffer[256]; + wsprintf(buffer,_T("Memory breakpoint %.5lx, %u\n",dwData,dwRange)); + OutputDebugString(buffer); + } + #endif + bStopEmulation |= CheckBreakpoint(dwData, dwRange, nType); + } + + // check for step cursor + bStopEmulation |= (dwDbgStopPC == Chipset.pc); + + // NOP3, opcode #420 (GOC) + if (bDbgNOP3 && I[0] == 0x4 && I[1] == 0x2 && I[2] == 0x0) + bStopEmulation = TRUE; + + // stop on first instruction of DOCODE object + if (bDbgCode && (Chipset.pc == 0x02DDE || Chipset.pc == 0x02E3C)) + { + // return address + DWORD dwAddr = Chipset.rstk[(Chipset.rstkp-1)&7]; + + _ASSERT(I[0] == 0 && I[1] == 1); // stopped at RTN opcode + + if (MapData(dwAddr) != M_ROM) // address not in ROM + dwDbgStopPC = dwAddr; // then stop + } + + // check for RPL breakpoint + if (dwDbgRplPC == Chipset.pc) + { + dwDbgRplPC = -1; + nDbgRplBreak = bStopEmulation ? BN_ASM_BT : BN_RPL; + bStopEmulation = TRUE; + } + + // RPL breakpoints, PC=(A), opcode #808C or PC=(C), opcode #808E + if (I[0] == 0x8 && I[1] == 0x0 && I[2] == 0x8 && (I[3] == 0xC || I[3] == 0xE )) + { + // get next RPL entry + DWORD dwAddr = Npack((I[3] == 0xC) ? Chipset.A : Chipset.C,5); + + if (bDbgRPL || CheckBreakpoint(dwAddr, 1, BP_RPL)) + { + BYTE byRplPtr[5]; + + Npeek(byRplPtr,dwAddr,5); // get PC address of next opcode + dwDbgRplPC = Npack(byRplPtr,5); // set RPL breakpoint + } + } + + // step over interrupt execution + if (bDbgSkipInt && !bStopEmulation && !Chipset.inte) + return; + + // check for step into + bStopEmulation |= (nDbgState == DBG_STEPINTO); + + // check for step over + bStopEmulation |= (nDbgState == DBG_STEPOVER) && dwDbgRstkp == Chipset.rstkp; + + // check for step out, something was popped from hardware stack + if (nDbgState == DBG_STEPOUT && dwDbgRstkp == Chipset.rstkp) + { + _ASSERT(bStopEmulation == FALSE); + if ((bStopEmulation = (Chipset.pc == dwDbgRstk)) == FALSE) + { + // it was C=RSTK, check for next object popped from hardware stack + dwDbgRstkp = (Chipset.rstkp-1)&7; + dwDbgRstk = Chipset.rstk[dwDbgRstkp]; + } + } + + if (bStopEmulation) // stop condition + { + StopTimers(); // hold timer values when emulator is stopped + if (Chipset.IORam[TIMER2_CTRL]&RUN) // check if timer running + { + if (dwEDbgT2 == Chipset.t2) + { + // cpu cycles for one timer2 tick elapsed + if ((DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwEDbgCycles + >= (SAMPLE / 8192) * (DWORD) T2CYCLES) + { + --Chipset.t2; + // adjust cycles reference + dwEDbgCycles += (SAMPLE / 8192) * T2CYCLES; + } + } + else // new timer2 value + { + // new cycle reference + dwEDbgCycles = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + } + + // check rising edge of Bit 8 of timer2 + if ((dwEDbgT2 & 0x100) == 0 && (Chipset.t2 & 0x100) != 0) + Chipset.t1 = (Chipset.t1 - 1) & 0xF; + } + dwEDbgT2 = Chipset.t2; // timer2 check reference value + + // redraw debugger window and stop + NotifyDebugger(nDbgRplBreak); + WaitForSingleObject(hEventDebug,INFINITE); + + StartTimers(); // continue timers + + if (nDbgState >= DBG_OFF) // if debugger is active + { + Chipset.Shutdn = FALSE; + Chipset.bShutdnWake = FALSE; + } + + // init slow down part + dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + QueryPerformanceCounter(&lDummyInt); + dwSpeedRef = lDummyInt.LowPart; + } + return; +} + +VOID SuspendDebugger(VOID) +{ + // auto control enabled, emulation halted by debugger + if (bDbgAutoStateCtrl && nDbgState > DBG_OFF) + { + nDbgOldState = nDbgState; // save old state + nDbgState = DBG_SUSPEND; // suspend state + SetEvent(hEventDebug); // exit debugger + } + return; +} + +VOID ResumeDebugger(VOID) +{ + // auto control enabled, debugger is suspended + if (bDbgAutoStateCtrl && nDbgState == DBG_SUSPEND) + { + // active RPL breakpoint + if (nDbgRplBreak) dwDbgRplPC = Chipset.pc; + nDbgState = nDbgOldState; // set to old debugger state + if (Chipset.Shutdn) // inside shutdown + SetEvent(hEventShutdn); // leave it to call debugger + } + return; +} + +static __inline VOID CheckDisp(BOOL bSync) +{ + if (disp == 0) return; // no display update need + + // update display when drawing top line or display is off + if (bSync && GetLineCounter() != 0x3F && (Chipset.IORam[0x00]&8)) + return; + + _ASSERT((disp & DISP_POINTER) == 0); // display pointer already updated + if (disp & DISP_MAIN) UpdateMainDisplay(); + if (disp & DISP_MENUE) UpdateMenuDisplay(); + _ASSERT((disp & DISP_ANNUN) == 0); // annunciators already updated + disp = 0; // display updated + return; +} + +static __inline VOID AdjustSpeed(VOID) // adjust emulation speed +{ + if (bCpuSlow || bKeySlow) // emulation slow down + { + DWORD dwCycles,dwTicks; + + EnterCriticalSection(&csSlowLock); + { + // cycles elapsed for next check + if ((dwCycles = (DWORD) (Chipset.cycles & 0xFFFFFFFF)-dwOldCyc) >= (DWORD) T2CYCLES) + { + LARGE_INTEGER lAct; + do + { + VERIFY(QueryPerformanceCounter(&lAct)); + + // get time difference + dwTicks = lAct.LowPart - dwSpeedRef; + } + // ticks elapsed or negative number (workaround for QueryPerformanceCounter() in Win2k) + while(dwTicks <= dwTickRef || (dwTicks & 0x80000000) != 0); + + dwOldCyc += T2CYCLES; // adjust cycles reference + dwSpeedRef += dwTickRef; // adjust reference time + } + } + LeaveCriticalSection(&csSlowLock); + } + return; +} + +VOID CheckSerial(VOID) +{ + // COM port closed and serial on + if (bCommInit == FALSE && (Chipset.IORam[IOC] & SON) != 0) + { + bCommInit = CommOpen(szSerialWire,szSerialIr); // open COM ports + } + + // COM port opened and serial off + if (bCommInit == TRUE && (Chipset.IORam[IOC] & SON) == 0) + { + CommClose(); // close COM port + bCommInit = FALSE; + } + return; +} + +VOID AdjKeySpeed(VOID) // slow down key repeat +{ + WORD i; + BOOL bKey; + + if (bCpuSlow) return; // no need to slow down + + bKey = FALSE; // search for a pressed key + for (i = 0;i < ARRAYSIZEOF(Chipset.Keyboard_Row) && !bKey;++i) + bKey = (Chipset.Keyboard_Row[i] != 0); + + EnterCriticalSection(&csSlowLock); + { + if (!bKeySlow && bKey) // key pressed, init variables + { + LARGE_INTEGER lTime; // sample timer ticks + // save reference cycles + dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + QueryPerformanceCounter(&lTime); // get timer ticks + dwSpeedRef = lTime.LowPart; // save reference time + } + bKeySlow = bKey; // save new state + } + LeaveCriticalSection(&csSlowLock); + return; +} + +VOID SetSpeed(BOOL bAdjust) // set emulation speed +{ + EnterCriticalSection(&csSlowLock); + { + if (bAdjust) // switch to real speed + { + LARGE_INTEGER lTime; // sample timer ticks + // save reference cycles + dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + QueryPerformanceCounter(&lTime); // get timer ticks + dwSpeedRef = lTime.LowPart; // save reference time + } + bCpuSlow = bAdjust; // save emulation speed + } + LeaveCriticalSection(&csSlowLock); + return; +} + +VOID UpdateKdnBit(VOID) // update KDN bit +{ + if ( Chipset.intk + && (Chipset.IORam[TIMER2_CTRL]&RUN) != 0 + && (DWORD) (Chipset.cycles & 0xFFFFFFFF) - Chipset.dwKdnCycles > (DWORD) T2CYCLES * 16) + IOBit(SRQ2,KDN,Chipset.in != 0); + return; +} + +BOOL WaitForSleepState(VOID) // wait for cpu SHUTDN then sleep state +{ + DWORD dwRefTime; + + SuspendDebugger(); // suspend debugger + + dwRefTime = timeGetTime(); + // wait for the SHUTDN command with 1.5 sec timeout + while (timeGetTime() - dwRefTime < 1500L && !Chipset.Shutdn) + Sleep(0); + + if (Chipset.Shutdn) // not timeout, cpu is down + SwitchToState(SM_SLEEP); // go to sleep state + else + ResumeDebugger(); // timeout, resume to debugger + + return SM_SLEEP != nNextState; // state not changed, emulator was busy +} + +UINT SwitchToState(UINT nNewState) +{ + UINT nOldState = nState; + + if (nState == nNewState) return nOldState; + switch (nState) + { + case SM_RUN: // Run + switch (nNewState) + { + case SM_INVALID: // -> Invalid + nNextState = SM_INVALID; + if (Chipset.Shutdn) + SetEvent(hEventShutdn); + else + bInterrupt = TRUE; + SuspendDebugger(); // suspend debugger + while (nState!=nNextState) Sleep(0); + UpdateWindowStatus(); + break; + case SM_RETURN: // -> Return + DisableDebugger(); // disable debugger + nNextState = SM_INVALID; + if (Chipset.Shutdn) + SetEvent(hEventShutdn); + else + bInterrupt = TRUE; + while (nState!=nNextState) Sleep(0); + nNextState = SM_RETURN; + SetEvent(hEventShutdn); + WaitForSingleObject(hThread,INFINITE); + UpdateWindowStatus(); + break; + case SM_SLEEP: // -> Sleep + nNextState = SM_SLEEP; + bInterrupt = TRUE; // exit main loop + SuspendDebugger(); // suspend debugger + SetEvent(hEventShutdn); // exit shutdown + while (nState!=nNextState) Sleep(0); + bInterrupt = FALSE; + ResetEvent(hEventDebug); + ResetEvent(hEventShutdn); + break; + } + break; + case SM_INVALID: // Invalid + switch (nNewState) + { + case SM_RUN: // -> Run + nNextState = SM_RUN; + // don't enter opcode loop on interrupt request + bInterrupt = Chipset.Shutdn || Chipset.SoftInt; + ResumeDebugger(); + SetEvent(hEventShutdn); + while (nState!=nNextState) Sleep(0); + UpdateWindowStatus(); + break; + case SM_RETURN: // -> Return + DisableDebugger(); // disable debugger + nNextState = SM_RETURN; + SetEvent(hEventShutdn); + WaitForSingleObject(hThread,INFINITE); + break; + case SM_SLEEP: // -> Sleep + nNextState = SM_SLEEP; + SetEvent(hEventShutdn); + while (nState!=nNextState) Sleep(0); + UpdateWindowStatus(); + break; + } + break; + case SM_SLEEP: // Sleep + switch (nNewState) + { + case SM_RUN: // -> Run + nNextState = SM_RUN; + // don't enter opcode loop on interrupt request + bInterrupt = (nDbgState == DBG_OFF) && (Chipset.Shutdn || Chipset.SoftInt); + ResumeDebugger(); + SetEvent(hEventShutdn); // leave sleep state + break; + case SM_INVALID: // -> Invalid + nNextState = SM_INVALID; + SetEvent(hEventShutdn); + while (nState!=nNextState) Sleep(0); + UpdateWindowStatus(); + break; + case SM_RETURN: // -> Return + DisableDebugger(); // disable debugger + nNextState = SM_INVALID; + SetEvent(hEventShutdn); + while (nState!=nNextState) Sleep(0); + nNextState = SM_RETURN; + SetEvent(hEventShutdn); + WaitForSingleObject(hThread,INFINITE); + UpdateWindowStatus(); + break; + } + break; + } + return nOldState; +} + +UINT WorkerThread(LPVOID pParam) +{ + LARGE_INTEGER lDummyInt; // sample timer ticks + QueryPerformanceFrequency(&lDummyInt); // init timer ticks + lDummyInt.QuadPart /= SAMPLE; // calculate sample ticks + dwTickRef = lDummyInt.LowPart; // sample timer ticks + _ASSERT(dwTickRef); // tick resolution error + +loop: + while (nNextState == SM_INVALID) // go into invalid state + { + OnToolMacroStop(); // close open keyboard macro handler + CommClose(); // close COM port + bCommInit = FALSE; // COM port not open + nState = SM_INVALID; // in invalid state + WaitForSingleObject(hEventShutdn,INFINITE); + if (nNextState == SM_RETURN) // go into return state + { + nState = SM_RETURN; // in return state + return 0; // kill thread + } + CheckSerial(); // test if UART on + } + while (nNextState == SM_RUN) + { + if (nState != SM_RUN) + { + nState = SM_RUN; + // clear port2 status bits + Chipset.cards_status &= ~(PORT2_PRESENT | PORT2_WRITE); + if (pbyPort2 || Chipset.Port2) // card plugged in port2 + { + Chipset.cards_status |= PORT2_PRESENT; + + if (bPort2Writeable) // is card writeable + Chipset.cards_status |= PORT2_WRITE; + } + // card detection off and timer running + if ((Chipset.IORam[CARDCTL] & ECDT) == 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0) + { + BOOL bNINT2 = Chipset.IORam[SRQ1] == 0 && (Chipset.IORam[SRQ2] & LSRQ) == 0; + BOOL bNINT = (Chipset.IORam[CARDCTL] & SMP) == 0; + + // state of CDT2 + bNINT2 = bNINT2 && (Chipset.cards_status & (P2W|P2C)) != P2C; + // state of CDT1 + bNINT = bNINT && (Chipset.cards_status & (P1W|P1C)) != P1C; + + IOBit(SRQ2,NINT2,bNINT2); + IOBit(SRQ2,NINT,bNINT); + } + RomSwitch(Chipset.Bank_FF); // select HP49G ROM bank and update memory mapping + UpdateContrast(Chipset.contrast); + UpdateDisplayPointers(); + UpdateMainDisplay(); + UpdateMenuDisplay(); + RefreshDisp0(); // CdB for HP: add apples display management + UpdateAnnunciators(); + // init speed reference + dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + QueryPerformanceCounter(&lDummyInt); + dwSpeedRef = lDummyInt.LowPart; + SetHP48Time(); // update HP48 time & date + StartTimers(); + // start display counter/update engine + StartDisplay((BYTE)(((Chipset.IORam[LINECOUNT+1]<<4)|Chipset.IORam[LINECOUNT])&0x3F)); + } + PCHANGED; + while (!bInterrupt) + { + if (nDbgState > DBG_OFF) // debugger active + { + Debugger(); + + // if suspended skip next opcode execution + if (nDbgState == DBG_SUSPEND) + { + if (Chipset.Shutdn) break; + continue; + } + } + + EvalOpcode(FASTPTR(Chipset.pc)); // execute opcode + + if (!bGrayscale) + CheckDisp(!Chipset.Shutdn); // check for display update + AdjustSpeed(); // adjust emulation speed + } + bInterrupt = FALSE; // be sure to reenter opcode loop + + // enter SHUTDN handler only in RUN mode + if (Chipset.Shutdn && !(nDbgState == DBG_STEPINTO || nDbgState == DBG_STEPOVER)) + { + if (!Chipset.SoftInt) // ignore SHUTDN on interrupt request + WaitForSingleObject(hEventShutdn,INFINITE); + else + Chipset.bShutdnWake = TRUE; // waked by interrupt + + if (Chipset.bShutdnWake) // waked up by timer, keyboard or serial + { + Chipset.bShutdnWake = FALSE; + Chipset.Shutdn = FALSE; + // init speed reference + dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + QueryPerformanceCounter(&lDummyInt); + dwSpeedRef = lDummyInt.LowPart; + } + } + if (Chipset.SoftInt) + { + Chipset.SoftInt = FALSE; + if (Chipset.inte) + { + Chipset.inte = FALSE; + rstkpush(Chipset.pc); + Chipset.pc = 0xf; + } + } + } + _ASSERT(nNextState != SM_RUN); + + StopDisplay(); // stop display counter/update + StopTimers(); + + while (nNextState == SM_SLEEP) // go into sleep state + { + nState = SM_SLEEP; // in sleep state + WaitForSingleObject(hEventShutdn,INFINITE); + } + goto loop; + UNREFERENCED_PARAMETER(pParam); +} diff --git a/SOURCE/EXTERNAL.C b/SOURCE/EXTERNAL.C new file mode 100644 index 0000000..74e2dd5 --- /dev/null +++ b/SOURCE/EXTERNAL.C @@ -0,0 +1,188 @@ +/* + * external.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "Emu48.h" +#include "ops.h" + +#define MUSIC_FREQ 11025 // this can be adjusted for quality + +//| 38G | 39G | 40G | 48SX | 48GX | 49G | Name +//#F0E4F #80F0F #80F0F #706D2 #80850 #80F0F =SFLAG53_56 + +// memory address for flags -53 to -56 + // CdB for HP: add apples beep management +#define SFLAG53_56 ( (cCurrentRomType=='6') \ + ? 0xE0E4F \ + : ( (cCurrentRomType=='A') \ + ? 0xF0E4F \ + : ( (cCurrentRomType!='E' && cCurrentRomType!='X' && cCurrentRomType!='P' && cCurrentRomType!='2' && cCurrentRomType!='Q') \ + ? ( (cCurrentRomType=='S') \ + ? 0x706D2 \ + : 0x80850 \ + ) \ + : 0x80F0F \ + ) \ + ) \ + ) + +BOOL bWaveBeep = FALSE; // PC speaker +DWORD dwWaveVol = 64; // wave sound volume + +static __inline VOID BeepWave(DWORD dwFrequency,DWORD dwDuration) +{ + HWAVEOUT hSoundDevice; + WAVEFORMATEX wf; + WAVEHDR wh; + HANDLE hEventSound; + DWORD i; + + if (dwFrequency == 0) // this is just a delay + { + Sleep(dwDuration); + return; + } + + hEventSound = CreateEvent(NULL,FALSE,FALSE,NULL); + + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.nChannels = 1; + wf.nSamplesPerSec = MUSIC_FREQ; + wf.nAvgBytesPerSec = MUSIC_FREQ; + wf.nBlockAlign = 1; + wf.wBitsPerSample = 8; + wf.cbSize = 0; + + if (waveOutOpen(&hSoundDevice,WAVE_MAPPER,&wf,(DWORD_PTR)hEventSound,0,CALLBACK_EVENT) != 0) + { + CloseHandle(hEventSound); // no sound available + return; + } + + // (samp/sec) * msecs * (secs/msec) = samps + wh.dwBufferLength = (DWORD) ((QWORD) MUSIC_FREQ * dwDuration / 1000); + VERIFY(wh.lpData = HeapAlloc(hHeap,0,wh.dwBufferLength)); + wh.dwBytesRecorded = 0; + wh.dwUser = 0; + wh.dwFlags = 0; + wh.dwLoops = 0; + + for (i = 0; i < wh.dwBufferLength; ++i) // generate square wave + { + wh.lpData[i] = (BYTE) ((((QWORD) 2 * dwFrequency * i / MUSIC_FREQ) & 1) * dwWaveVol); + } + + VERIFY(waveOutPrepareHeader(hSoundDevice,&wh,sizeof(wh)) == MMSYSERR_NOERROR); + + ResetEvent(hEventSound); // prepare event for finishing + VERIFY(waveOutWrite(hSoundDevice,&wh,sizeof(wh)) == MMSYSERR_NOERROR); + WaitForSingleObject(hEventSound,INFINITE); // wait for finishing + + VERIFY(waveOutUnprepareHeader(hSoundDevice,&wh,sizeof(wh)) == MMSYSERR_NOERROR); + VERIFY(waveOutClose(hSoundDevice) == MMSYSERR_NOERROR); + + HeapFree(hHeap,0,wh.lpData); + CloseHandle(hEventSound); + return; +} + +static __inline VOID BeepWin9x(DWORD dwFrequency,DWORD dwDuration) +{ +#if !defined _WIN64 + #define PIT8254_CNT2 0x42 + #define PIT8254_MCR 0x43 + #define PPI8255_PBO 0x61 + + BYTE bySpk = _inp(PPI8255_PBO); // get current status + + if (dwFrequency != 0) + { + WORD wCount; + + // limit low frequency + if (lFreq.QuadPart / 65535 >= dwFrequency) + dwFrequency = (DWORD) (lFreq.QuadPart / 65535) + 1; + + // determine the timer frequency + wCount = (WORD) (lFreq.QuadPart / dwFrequency); + + _outp(PIT8254_MCR,0xB6); // set up the timer + + _outp(PIT8254_CNT2,wCount&0xff); + _outp(PIT8254_CNT2,wCount>>8); + + _outp(PPI8255_PBO,bySpk | 0x03); // turn on the speaker + } + + Sleep(dwDuration); + + _outp(PPI8255_PBO,bySpk & 0xFC); // turn off the speaker + return; + + #undef PIT8254_CNT2 + #undef PIT8254_MCR + #undef PPI8255_PBO +#endif +} + +static __inline VOID Return(CHIPSET* w) +{ + w->rstkp=(w->rstkp-1)&7; + w->pc = w->rstk[w->rstkp]; + w->rstk[w->rstkp] = 0; + return; +} + +VOID External(CHIPSET* w) // Beep patch +{ + BYTE fbeep; + DWORD freq,dur; + + freq = Npack(w->D,5); // frequency in Hz + dur = Npack(w->C,5); // duration in ms + Nread(&fbeep,SFLAG53_56,1); // fetch system flags -53 to -56 + + w->carry = TRUE; // setting of no beep + if (!(fbeep & 0x8) && freq) // bit -56 clear and frequency > 0 Hz + { + if (freq > 4400) freq = 4400; // high limit of HP (SX) + + if (bWaveBeep) + { + BeepWave(freq,dur); // wave output over sound card + } + else + { + OSVERSIONINFO version; + version.dwOSVersionInfoSize = sizeof(version); + GetVersionEx(&version); + + if (version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) + { + BeepWin9x(freq,dur); // do it the hard way on '9x / Me + } + else // VER_PLATFORM_WIN32_NT + { + if (freq < 37) freq = 37; // low limit of freqency (NT) + + _ASSERT(freq >= 0x25 && freq <= 0x7FFF); + Beep(freq,dur); // NT: ok, Windows 95: default sound or standard system beep + } + } + + // estimate cpu cycles for beeping time (2MHz / 4MHz) + w->cycles += dur * ((cCurrentRomType=='S') ? 2000 : 4000); + + // original routine return with... + w->P = 0; // P=0 + w->intk = TRUE; // INTON + w->carry = FALSE; // RTNCC + } + Return(w); + return; +} diff --git a/SOURCE/Emu48.dsp b/SOURCE/Emu48.dsp new file mode 100644 index 0000000..408ce8f --- /dev/null +++ b/SOURCE/Emu48.dsp @@ -0,0 +1,372 @@ +# Microsoft Developer Studio Project File - Name="Emu48" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=Emu48 - Win32 DebugRegDebug4x +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Emu48.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Emu48.mak" CFG="Emu48 - Win32 DebugRegDebug4x" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Emu48 - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "Emu48 - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE "Emu48 - Win32 Release Unicode" (based on "Win32 (x86) Application") +!MESSAGE "Emu48 - Win32 Debug Unicode" (based on "Win32 (x86) Application") +!MESSAGE "Emu48 - Win32 DebugRegDebug4x" (based on "Win32 (x86) Application") +!MESSAGE "Emu48 - Win32 ReleaseRegDebug4x" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Emu48 - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir ".\Release" +# PROP BASE Intermediate_Dir ".\Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\Release" +# PROP Intermediate_Dir ".\Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /Gr /MT /W3 /GX /O2 /Ob2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /Yu"pch.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "Emu48 - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir ".\Debug" +# PROP BASE Intermediate_Dir ".\Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\Debug" +# PROP Intermediate_Dir ".\Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /FR /Yu"pch.h" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /debug /machine:I386 + +!ELSEIF "$(CFG)" == "Emu48 - Win32 Release Unicode" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Emu48___" +# PROP BASE Intermediate_Dir "Emu48___" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\ReleaseUnicode" +# PROP Intermediate_Dir ".\ReleaseUnicode" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /Gr /MT /W3 /GX /O2 /Ob2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /Yu"pch.h" /FD /c +# ADD CPP /nologo /Gr /MT /W3 /GX /O2 /Ob2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /D "_UNICODE" /D "UNICODE" /Yu"pch.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "Emu48 - Win32 Debug Unicode" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Emu48__0" +# PROP BASE Intermediate_Dir "Emu48__0" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\DebugUnicode" +# PROP Intermediate_Dir ".\DebugUnicode" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /FR /Yu"pch.h" /FD /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /D "_UNICODE" /D "UNICODE" /FR /Yu"pch.h" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /debug /machine:I386 + +!ELSEIF "$(CFG)" == "Emu48 - Win32 DebugRegDebug4x" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Emu48___Win32_DebugRegDebug4x" +# PROP BASE Intermediate_Dir "Emu48___Win32_DebugRegDebug4x" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\DebugRegDebug4x" +# PROP Intermediate_Dir ".\DebugRegDebug4x" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /FR /Yu"pch.h" /FD /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /D "REGISTRY" /FR /Yu"pch.h" /FD /D REGISTRYKEY=\"Software\\Hewlett-Packard\\Debug4x\\Emu48\" /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /debug /machine:I386 + +!ELSEIF "$(CFG)" == "Emu48 - Win32 ReleaseRegDebug4x" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Emu48___Win32_ReleaseRegDebug4x" +# PROP BASE Intermediate_Dir "Emu48___Win32_ReleaseRegDebug4x" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\ReleaseRegDebug4x" +# PROP Intermediate_Dir ".\ReleaseRegDebug4x" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /Gr /MT /W3 /GX /O2 /Ob2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /Yu"pch.h" /FD /c +# ADD CPP /nologo /Gr /MT /W3 /GX /O2 /Ob2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /D "REGISTRY" /Yu"pch.h" /FD /D REGISTRYKEY=\"Software\\Hewlett-Packard\\Debug4x\\Emu48\" /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /machine:I386 + +!ENDIF + +# Begin Target + +# Name "Emu48 - Win32 Release" +# Name "Emu48 - Win32 Debug" +# Name "Emu48 - Win32 Release Unicode" +# Name "Emu48 - Win32 Debug Unicode" +# Name "Emu48 - Win32 DebugRegDebug4x" +# Name "Emu48 - Win32 ReleaseRegDebug4x" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" +# Begin Source File + +SOURCE=.\apple.c +# End Source File +# Begin Source File + +SOURCE=.\cursor.c +# End Source File +# Begin Source File + +SOURCE=.\ddeserv.c +# End Source File +# Begin Source File + +SOURCE=.\debugger.c +# End Source File +# Begin Source File + +SOURCE=.\disasm.c +# End Source File +# Begin Source File + +SOURCE=.\display.c +# End Source File +# Begin Source File + +SOURCE=.\Emu48.c +# End Source File +# Begin Source File + +SOURCE=.\Emu48.rc +# End Source File +# Begin Source File + +SOURCE=.\engine.c +# End Source File +# Begin Source File + +SOURCE=.\external.c +# End Source File +# Begin Source File + +SOURCE=.\fetch.c +# End Source File +# Begin Source File + +SOURCE=.\files.c +# End Source File +# Begin Source File + +SOURCE=.\i28f160.c +# End Source File +# Begin Source File + +SOURCE=.\keyboard.c +# End Source File +# Begin Source File + +SOURCE=.\keymacro.c +# End Source File +# Begin Source File + +SOURCE=.\kml.c +# End Source File +# Begin Source File + +SOURCE=.\mops.c +# End Source File +# Begin Source File + +SOURCE=.\opcodes.c +# End Source File +# Begin Source File + +SOURCE=.\pch.c +# ADD CPP /Yc"pch.h" +# End Source File +# Begin Source File + +SOURCE=.\rpl.c +# End Source File +# Begin Source File + +SOURCE=.\serial.c +# End Source File +# Begin Source File + +SOURCE=.\settings.c +# End Source File +# Begin Source File + +SOURCE=.\stack.c +# End Source File +# Begin Source File + +SOURCE=.\timer.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd" +# Begin Source File + +SOURCE=.\apple.h +# End Source File +# Begin Source File + +SOURCE=.\color.h +# End Source File +# Begin Source File + +SOURCE=.\debugger.h +# End Source File +# Begin Source File + +SOURCE=.\Emu48.h +# End Source File +# Begin Source File + +SOURCE=.\i28f160.h +# End Source File +# Begin Source File + +SOURCE=.\io.h +# End Source File +# Begin Source File + +SOURCE=.\kml.h +# End Source File +# Begin Source File + +SOURCE=.\opcodes.h +# End Source File +# Begin Source File + +SOURCE=.\ops.h +# End Source File +# Begin Source File + +SOURCE=.\pch.h +# End Source File +# Begin Source File + +SOURCE=.\types.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\CHECKBOX.BMP +# End Source File +# Begin Source File + +SOURCE=.\DBGTOOL.BMP +# End Source File +# Begin Source File + +SOURCE=.\Emu48.ico +# End Source File +# End Group +# End Target +# End Project diff --git a/SOURCE/Emu48.dsw b/SOURCE/Emu48.dsw new file mode 100644 index 0000000..dbd3607 --- /dev/null +++ b/SOURCE/Emu48.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Emu48"=".\Emu48.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/SOURCE/FETCH.C b/SOURCE/FETCH.C new file mode 100644 index 0000000..b9a98d8 --- /dev/null +++ b/SOURCE/FETCH.C @@ -0,0 +1,778 @@ +/* + * fetch.c + * + * This file is part of Emu48 + * + * Copyright (C) 1999 Christoph Gießelink + * + */ +#include "pch.h" +#include "Opcodes.h" + +#define F 0xFF // F = function + +typedef struct +{ + const VOID *pLnk; + const DWORD dwTyp; +} JMPTAB; + +// jump tables +static const JMPTAB oF_[] = +{ + oF0, F, + oF1, F, + oF2, F, + oF3, F, + oF4, F, + oF5, F, + oF6, F, + oF7, F, + oF8, F, + oF9, F, + oFA, F, + oFB, F, + oFC, F, + oFD, F, + oFE, F, + oFF, F +}; + +static const JMPTAB oE_[] = +{ + oE0, F, + oE1, F, + oE2, F, + oE3, F, + oE4, F, + oE5, F, + oE6, F, + oE7, F, + oE8, F, + oE9, F, + oEA, F, + oEB, F, + oEC, F, + oED, F, + oEE, F, + oEF, F +}; + +static const JMPTAB oD_[] = +{ + oD0, F, + oD1, F, + oD2, F, + oD3, F, + oD4, F, + oD5, F, + oD6, F, + oD7, F, + oD8, F, + oD9, F, + oDA, F, + oDB, F, + oDC, F, + oDD, F, + oDE, F, + oDF, F +}; + +static const JMPTAB oC_[] = +{ + oC0, F, + oC1, F, + oC2, F, + oC3, F, + oC4, F, + oC5, F, + oC6, F, + oC7, F, + oC8, F, + oC9, F, + oCA, F, + oCB, F, + oCC, F, + oCD, F, + oCE, F, + oCF, F +}; + +static const JMPTAB oBb_[] = +{ + oBb0, F, + oBb1, F, + oBb2, F, + oBb3, F, + oBb4, F, + oBb5, F, + oBb6, F, + oBb7, F, + oBb8, F, + oBb9, F, + oBbA, F, + oBbB, F, + oBbC, F, + oBbD, F, + oBbE, F, + oBbF, F +}; + +static const JMPTAB oBa_[] = +{ + oBa0, F, + oBa1, F, + oBa2, F, + oBa3, F, + oBa4, F, + oBa5, F, + oBa6, F, + oBa7, F, + oBa8, F, + oBa9, F, + oBaA, F, + oBaB, F, + oBaC, F, + oBaD, F, + oBaE, F, + oBaF, F +}; + +static const JMPTAB oB_[] = +{ + oBa_, 2, + oBa_, 2, + oBa_, 2, + oBa_, 2, + oBa_, 2, + oBa_, 2, + oBa_, 2, + oBa_, 2, + oBb_, 2, + oBb_, 2, + oBb_, 2, + oBb_, 2, + oBb_, 2, + oBb_, 2, + oBb_, 2, + oBb_, 2 +}; + +static const JMPTAB oAb_[] = +{ + oAb0, F, + oAb1, F, + oAb2, F, + oAb3, F, + oAb4, F, + oAb5, F, + oAb6, F, + oAb7, F, + oAb8, F, + oAb9, F, + oAbA, F, + oAbB, F, + oAbC, F, + oAbD, F, + oAbE, F, + oAbF, F +}; + +static const JMPTAB oAa_[] = +{ + oAa0, F, + oAa1, F, + oAa2, F, + oAa3, F, + oAa4, F, + oAa5, F, + oAa6, F, + oAa7, F, + oAa8, F, + oAa9, F, + oAaA, F, + oAaB, F, + oAaC, F, + oAaD, F, + oAaE, F, + oAaF, F +}; + +static const JMPTAB oA_[] = +{ + oAa_, 2, + oAa_, 2, + oAa_, 2, + oAa_, 2, + oAa_, 2, + oAa_, 2, + oAa_, 2, + oAa_, 2, + oAb_, 2, + oAb_, 2, + oAb_, 2, + oAb_, 2, + oAb_, 2, + oAb_, 2, + oAb_, 2, + oAb_, 2 +}; + +static const JMPTAB o9b_[] = +{ + o9b0, F, + o9b1, F, + o9b2, F, + o9b3, F, + o9b4, F, + o9b5, F, + o9b6, F, + o9b7, F, + o9b8, F, + o9b9, F, + o9bA, F, + o9bB, F, + o9bC, F, + o9bD, F, + o9bE, F, + o9bF, F +}; + +static const JMPTAB o9a_[] = +{ + o9a0, F, + o9a1, F, + o9a2, F, + o9a3, F, + o9a4, F, + o9a5, F, + o9a6, F, + o9a7, F, + o9a8, F, + o9a9, F, + o9aA, F, + o9aB, F, + o9aC, F, + o9aD, F, + o9aE, F, + o9aF, F +}; + +static const JMPTAB o9_[] = +{ + o9a_, 2, + o9a_, 2, + o9a_, 2, + o9a_, 2, + o9a_, 2, + o9a_, 2, + o9a_, 2, + o9a_, 2, + o9b_, 2, + o9b_, 2, + o9b_, 2, + o9b_, 2, + o9b_, 2, + o9b_, 2, + o9b_, 2, + o9b_, 2 +}; + +static const JMPTAB o8B_[] = +{ + o8B0, F, + o8B1, F, + o8B2, F, + o8B3, F, + o8B4, F, + o8B5, F, + o8B6, F, + o8B7, F, + o8B8, F, + o8B9, F, + o8BA, F, + o8BB, F, + o8BC, F, + o8BD, F, + o8BE, F, + o8BF, F +}; + +static const JMPTAB o8A_[] = +{ + o8A0, F, + o8A1, F, + o8A2, F, + o8A3, F, + o8A4, F, + o8A5, F, + o8A6, F, + o8A7, F, + o8A8, F, + o8A9, F, + o8AA, F, + o8AB, F, + o8AC, F, + o8AD, F, + o8AE, F, + o8AF, F +}; + +static const JMPTAB o81B_[] = +{ + o_invalid4, F, + o81B1, F, // normally o_invalid4, beep patch + o81B2, F, + o81B3, F, + o81B4, F, + o81B5, F, + o81B6, F, + o81B7, F, + o_invalid4, F, + o_invalid4, F, + o_invalid4, F, + o_invalid4, F, + o_invalid4, F, + o_invalid4, F, + o_invalid4, F, + o_invalid4, F +}; + +static const JMPTAB o81Af2_[] = +{ + o81Af20, F, + o81Af21, F, + o81Af22, F, + o81Af23, F, + o81Af24, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o81Af28, F, + o81Af29, F, + o81Af2A, F, + o81Af2B, F, + o81Af2C, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F +}; + +static const JMPTAB o81Af1_[] = +{ + o81Af10, F, + o81Af11, F, + o81Af12, F, + o81Af13, F, + o81Af14, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o81Af18, F, + o81Af19, F, + o81Af1A, F, + o81Af1B, F, + o81Af1C, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F +}; + +static const JMPTAB o81Af0_[] = +{ + o81Af00, F, + o81Af01, F, + o81Af02, F, + o81Af03, F, + o81Af04, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o81Af08, F, + o81Af09, F, + o81Af0A, F, + o81Af0B, F, + o81Af0C, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F +}; + +static const JMPTAB o81A_[] = +{ + o81Af0_, 5, + o81Af1_, 5, + o81Af2_, 5, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F +}; + +static const JMPTAB o819_[] = +{ + o819f0, F, + o819f1, F, + o819f2, F, + o819f3, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F +}; + +static const JMPTAB o818_[] = +{ + o818f0x, F, + o818f1x, F, + o818f2x, F, + o818f3x, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o818f8x, F, + o818f9x, F, + o818fAx, F, + o818fBx, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F, + o_invalid6, F +}; + +static const JMPTAB o81_[] = +{ + o810, F, + o811, F, + o812, F, + o813, F, + o814, F, + o815, F, + o816, F, + o817, F, + o818_, 4, + o819_, 4, + o81A_, 4, + o81B_, 3, + o81C, F, + o81D, F, + o81E, F, + o81F, F +}; + +static const JMPTAB o8081_[] = +{ + o80810, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F, + o_invalid5, F +}; + +static const JMPTAB o808_[] = +{ + o8080, F, + o8081_, 4, + o8082X, F, + o8083, F, + o8084n, F, + o8085n, F, + o8086n, F, + o8087n, F, + o8088n, F, + o8089n, F, + o808An, F, + o808Bn, F, + o808C, F, + o808D, F, + o808E, F, + o808F, F +}; + +static const JMPTAB o80_[] = +{ + o800, F, + o801, F, + o802, F, + o803, F, + o804, F, + o805, F, + o806, F, + o807, F, + o808_, 3, + o809, F, + o80A, F, + o80B, F, + o80Cn, F, + o80Dn, F, + o80E, F, + o80Fn, F +}; + +static const JMPTAB o8_[] = +{ + o80_, 2, + o81_, 2, + o82n, F, + o83n, F, + o84n, F, + o85n, F, + o86n, F, + o87n, F, + o88n, F, + o89n, F, + o8A_, 2, + o8B_, 2, + o8Cd4, F, + o8Dd5, F, + o8Ed4, F, + o8Fd5, F +}; + +static const JMPTAB o15_[] = +{ + o150a, F, + o151a, F, + o152a, F, + o153a, F, + o154a, F, + o155a, F, + o156a, F, + o157a, F, + o158x, F, + o159x, F, + o15Ax, F, + o15Bx, F, + o15Cx, F, + o15Dx, F, + o15Ex, F, + o15Fx, F +}; + +static const JMPTAB o14_[] = +{ + o140, F, + o141, F, + o142, F, + o143, F, + o144, F, + o145, F, + o146, F, + o147, F, + o148, F, + o149, F, + o14A, F, + o14B, F, + o14C, F, + o14D, F, + o14E, F, + o14F, F +}; + +static const JMPTAB o13_[] = +{ + o130, F, + o131, F, + o132, F, + o133, F, + o134, F, + o135, F, + o136, F, + o137, F, + o138, F, + o139, F, + o13A, F, + o13B, F, + o13C, F, + o13D, F, + o13E, F, + o13F, F +}; + +static const JMPTAB o12_[] = +{ + o120, F, + o121, F, + o122, F, + o123, F, + o124, F, + o_invalid3, F, + o_invalid3, F, + o_invalid3, F, + o128, F, + o129, F, + o12A, F, + o12B, F, + o12C, F, + o_invalid3, F, + o_invalid3, F, + o_invalid3, F +}; + +static const JMPTAB o11_[] = +{ + o110, F, + o111, F, + o112, F, + o113, F, + o114, F, + o_invalid3, F, + o_invalid3, F, + o_invalid3, F, + o118, F, + o119, F, + o11A, F, + o11B, F, + o11C, F, + o_invalid3, F, + o_invalid3, F, + o_invalid3, F +}; + +static const JMPTAB o10_[] = +{ + o100, F, + o101, F, + o102, F, + o103, F, + o104, F, + o_invalid3, F, + o_invalid3, F, + o_invalid3, F, + o108, F, + o109, F, + o10A, F, + o10B, F, + o10C, F, + o_invalid3, F, + o_invalid3, F, + o_invalid3, F +}; + +static const JMPTAB o1_[] = +{ + o10_, 2, + o11_, 2, + o12_, 2, + o13_, 2, + o14_, 2, + o15_, 2, + o16x, F, + o17x, F, + o18x, F, + o19d2, F, + o1Ad4, F, + o1Bd5, F, + o1Cx, F, + o1Dd2, F, + o1Ed4, F, + o1Fd5, F +}; + +static const JMPTAB o0E_[] = +{ + o0Ef0, F, + o0Ef1, F, + o0Ef2, F, + o0Ef3, F, + o0Ef4, F, + o0Ef5, F, + o0Ef6, F, + o0Ef7, F, + o0Ef8, F, + o0Ef9, F, + o0EfA, F, + o0EfB, F, + o0EfC, F, + o0EfD, F, + o0EfE, F, + o0EfF, F +}; + +static const JMPTAB o0_[] = +{ + o00, F, + o01, F, + o02, F, + o03, F, + o04, F, + o05, F, + o06, F, + o07, F, + o08, F, + o09, F, + o0A, F, + o0B, F, + o0C, F, + o0D, F, + o0E_, 3, + o0F, F +}; + +static const JMPTAB o_[] = +{ + o0_, 1, + o1_, 1, + o2n, F, + o3X, F, + o4d2, F, + o5d2, F, + o6d3, F, + o7d3, F, + o8_, 1, + o9_, 1, + oA_, 1, + oB_, 1, + oC_, 1, + oD_, 1, + oE_, 1, + oF_, 1 +}; + +// opcode dispatcher +VOID EvalOpcode(LPBYTE I) +{ + DWORD dwTemp,dwIndex = 0; + JMPTAB const *pJmpTab = o_; + + do + { + dwTemp = I[dwIndex]; // table entry + _ASSERT(dwTemp <= 0xf); // found packed data + dwIndex = pJmpTab[dwTemp].dwTyp; // next pointer type + pJmpTab = pJmpTab[dwTemp].pLnk; // next pointer to table/function + } + while (dwIndex != F); // reference to table? -> again + + ((VOID (*)(LPBYTE)) pJmpTab)(I); // call function + return; +} diff --git a/SOURCE/FILES.C b/SOURCE/FILES.C new file mode 100644 index 0000000..3a6e2cb --- /dev/null +++ b/SOURCE/FILES.C @@ -0,0 +1,1553 @@ +/* + * files.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "Emu48.h" +#include "ops.h" +#include "io.h" // I/O register definitions +#include "kml.h" +#include "i28f160.h" // flash support +#include "debugger.h" + +TCHAR szEmuDirectory[MAX_PATH]; +TCHAR szCurrentDirectory[MAX_PATH]; +TCHAR szCurrentKml[MAX_PATH]; +TCHAR szBackupKml[MAX_PATH]; +TCHAR szCurrentFilename[MAX_PATH]; +TCHAR szBackupFilename[MAX_PATH]; +TCHAR szBufferFilename[MAX_PATH]; +TCHAR szPort2Filename[MAX_PATH]; + +LPBYTE pbyRom = NULL; +static HANDLE hRomFile = NULL; +static HANDLE hRomMap = NULL; +DWORD dwRomSize = 0; +BYTE cCurrentRomType = 0; // Model -> hardware +UINT nCurrentClass = 0; // Class -> derivate +BOOL bRomWriteable = TRUE; // flag if ROM writeable + +static HANDLE hPort2File = NULL; +static HANDLE hPort2Map = NULL; +LPBYTE pbyPort2 = NULL; +BOOL bPort2Writeable = FALSE; +BOOL bPort2IsShared = FALSE; +DWORD dwPort2Size = 0; // size of mapped port2 +DWORD dwPort2Mask = 0; + +// document signatures +static BYTE pbySignatureA[16] = "Emu38 Document\xFE"; +static BYTE pbySignatureB[16] = "Emu39 Document\xFE"; +static BYTE pbySignatureE[16] = "Emu48 Document\xFE"; +static BYTE pbySignatureW[16] = "Win48 Document\xFE"; +static BYTE pbySignatureV[16] = "Emu49 Document\xFE"; +static HANDLE hCurrentFile = NULL; + +static CHIPSET BackupChipset; + +BOOL bBackup = FALSE; + +//################ +//# +//# Window Position Tools +//# +//################ + +VOID SetWindowLocation(HWND hWnd,INT nPosX,INT nPosY) +{ + WINDOWPLACEMENT wndpl; + RECT *pRc = &wndpl.rcNormalPosition; + + wndpl.length = sizeof(wndpl); + GetWindowPlacement(hWnd,&wndpl); + pRc->right = pRc->right - pRc->left + nPosX; + pRc->bottom = pRc->bottom - pRc->top + nPosY; + pRc->left = nPosX; + pRc->top = nPosY; + SetWindowPlacement(hWnd,&wndpl); + return; +} + + + +//################ +//# +//# Filename Title Helper Tool +//# +//################ + +DWORD GetCutPathName(LPCTSTR szFileName, LPTSTR szBuffer, DWORD dwBufferLength, INT nCutLength) +{ + TCHAR cPath[_MAX_PATH]; // full filename + TCHAR cDrive[_MAX_DRIVE]; + TCHAR cDir[_MAX_DIR]; + TCHAR cFname[_MAX_FNAME]; + TCHAR cExt[_MAX_EXT]; + + _ASSERT(nCutLength >= 0); // 0 = only drive and name + + // split original filename into parts + _tsplitpath(szFileName,cDrive,cDir,cFname,cExt); + + if (*cDir != 0) // contain directory part + { + LPTSTR lpFilePart; // address of file name in path + INT nNameLen,nPathLen,nMaxPathLen; + + GetFullPathName(szFileName,ARRAYSIZEOF(cPath),cPath,&lpFilePart); + _tsplitpath(cPath,cDrive,cDir,cFname,cExt); + + // calculate size of drive/name and path + nNameLen = lstrlen(cDrive) + lstrlen(cFname) + lstrlen(cExt); + nPathLen = lstrlen(cDir); + + // maximum length for path + nMaxPathLen = nCutLength - nNameLen; + + if (nPathLen > nMaxPathLen) // have to cut path + { + TCHAR cDirTemp[_MAX_DIR] = _T("\\..."); + LPTSTR szPtr; + + nMaxPathLen -= 4; // need 4 chars for additional "\..." + if (nMaxPathLen < 0) nMaxPathLen = 0; + + // get earliest possible '\' character + szPtr = &cDir[nPathLen - nMaxPathLen]; + szPtr = _tcschr(szPtr,_T('\\')); + // not found + if (szPtr == NULL) szPtr = _T(""); + + lstrcat(cDirTemp,szPtr); // copy path with preample to dir buffer + lstrcpyn(cDir,cDirTemp,ARRAYSIZEOF(cDir)); + } + } + + _tmakepath(cPath,cDrive,cDir,cFname,cExt); + lstrcpyn(szBuffer,cPath,dwBufferLength); + return lstrlen(szBuffer); +} + +VOID SetWindowPathTitle(LPCTSTR szFileName) +{ + TCHAR cPath[MAX_PATH]; + RECT rectClient; + + if (*szFileName != 0) // set new title + { + _ASSERT(hWnd != NULL); + VERIFY(GetClientRect(hWnd,&rectClient)); + GetCutPathName(szFileName,cPath,ARRAYSIZEOF(cPath),rectClient.right/10); + SetWindowTitle(cPath); + } + return; +} + + + +//################ +//# +//# Patch +//# +//################ + +static __inline BYTE Asc2Nib(BYTE c) +{ + if (c<'0') return 0; + if (c<='9') return c-'0'; + if (c<'A') return 0; + if (c<='F') return c-'A'+10; + if (c<'a') return 0; + if (c<='f') return c-'a'+10; + return 0; +} + +// functions to restore ROM patches +typedef struct tnode +{ + BOOL bPatch; // TRUE = ROM address patched + DWORD dwAddress; // patch address + BYTE byROM; // original ROM value + BYTE byPatch; // patched ROM value + struct tnode *next; // next node +} TREENODE; + +static TREENODE *nodePatch = NULL; + +static BOOL PatchNibble(DWORD dwAddress, BYTE byPatch) +{ + TREENODE *p; + + _ASSERT(pbyRom); // ROM defined + if((p = HeapAlloc(hHeap,0,sizeof(TREENODE))) == NULL) + return TRUE; + + p->bPatch = TRUE; // address patched + p->dwAddress = dwAddress; // save current values + p->byROM = pbyRom[dwAddress]; + p->byPatch = byPatch; + p->next = nodePatch; // save node + nodePatch = p; + + pbyRom[dwAddress] = byPatch; // patch ROM + return FALSE; +} + +static VOID RestorePatches(VOID) +{ + TREENODE *p; + + _ASSERT(pbyRom); // ROM defined + while (nodePatch != NULL) + { + // restore original data + pbyRom[nodePatch->dwAddress] = nodePatch->byROM; + + p = nodePatch->next; // save pointer to next node + HeapFree(hHeap,0,nodePatch); // free node + nodePatch = p; // new node + } + return; +} + +VOID UpdatePatches(BOOL bPatch) +{ + TREENODE *p = nodePatch; + + _ASSERT(pbyRom); // ROM defined + while (p != NULL) + { + if (bPatch) // patch ROM + { + if (!p->bPatch) // patch only if not patched + { + // use original data for patch restore + p->byROM = pbyRom[p->dwAddress]; + + // restore patch data + pbyRom[p->dwAddress] = p->byPatch; + p->bPatch = TRUE; // address patched + } + else + { + _ASSERT(FALSE); // call ROM patch on a patched ROM + } + } + else // restore ROM + { + // restore original data + pbyRom[p->dwAddress] = p->byROM; + p->bPatch = FALSE; // address not patched + } + + p = p->next; // next node + } + return; +} + +BOOL PatchRom(LPCTSTR szFilename) +{ + HANDLE hFile = NULL; + DWORD dwFileSizeLow = 0; + DWORD dwFileSizeHigh = 0; + DWORD lBytesRead = 0; + PSZ lpStop,lpBuf = NULL; + DWORD dwAddress = 0; + UINT nPos = 0; + + if (pbyRom == NULL) return FALSE; + SetCurrentDirectory(szEmuDirectory); + hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + SetCurrentDirectory(szCurrentDirectory); + if (hFile == INVALID_HANDLE_VALUE) return FALSE; + dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh); + if (dwFileSizeLow <= 5) + { // file is too small. + CloseHandle(hFile); + return FALSE; + } + if (dwFileSizeHigh != 0) + { // file is too large. + CloseHandle(hFile); + return FALSE; + } + lpBuf = HeapAlloc(hHeap,0,dwFileSizeLow+1); + if (lpBuf == NULL) + { + CloseHandle(hFile); + return FALSE; + } + ReadFile(hFile, lpBuf, dwFileSizeLow, &lBytesRead, NULL); + CloseHandle(hFile); + lpBuf[dwFileSizeLow] = 0; + nPos = 0; + while (lpBuf[nPos]) + { + do // remove blank space + { + if ( (lpBuf[nPos]!=' ') + &&(lpBuf[nPos]!='\n') + &&(lpBuf[nPos]!='\r') + &&(lpBuf[nPos]!='\t')) break; + nPos++; + } while (lpBuf[nPos]); + if (lpBuf[nPos]==';') // comment ? + { + do + { + nPos++; + if (lpBuf[nPos]=='\n') + { + nPos++; + break; + } + } while (lpBuf[nPos]); + continue; + } + dwAddress = strtoul(&lpBuf[nPos], &lpStop, 16); + nPos += (UINT) (lpStop - &lpBuf[nPos]) + 1; + if (*lpStop != ':' || *lpStop == 0) + continue; + while (lpBuf[nPos]) + { + if (isxdigit(lpBuf[nPos]) == FALSE) break; + // patch ROM and save original nibble + PatchNibble(dwAddress, Asc2Nib(lpBuf[nPos])); + dwAddress = (dwAddress+1)&(dwRomSize-1); + nPos++; + } + } + HeapFree(hHeap,0,lpBuf); + return TRUE; +} + + + +//################ +//# +//# ROM +//# +//################ + +static WORD CrcRom(VOID) // calculate fingerprint of ROM +{ + DWORD dwCount; + DWORD dwFileSize; + DWORD dwCrc = 0; + + dwFileSize = GetFileSize(hRomFile, &dwCount); // get real filesize + _ASSERT(dwCount == 0); // isn't created by MapRom() + + _ASSERT(pbyRom); // view on ROM + // use checksum, because it's faster + for (dwCount = 0;dwCount < dwFileSize; dwCount+=sizeof(dwCrc)) + dwCrc += *((DWORD *) &pbyRom[dwCount]); + + return (WORD) dwCrc; +} + +BOOL MapRom(LPCTSTR szFilename) +{ + DWORD dwFileSizeHigh; + + // open ROM for writing + BOOL bWrite = (cCurrentRomType == 'X' || cCurrentRomType == 'Q') ? bRomWriteable : FALSE; // CdB for HP: add apples + + if (pbyRom != NULL) + { + return FALSE; + } + SetCurrentDirectory(szEmuDirectory); + if (bWrite) // ROM writeable + { + hRomFile = CreateFile(szFilename, + GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hRomFile == INVALID_HANDLE_VALUE) + { + bWrite = FALSE; // ROM not writeable + hRomFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + } + } + else // writing ROM disabled + { + hRomFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + } + SetCurrentDirectory(szCurrentDirectory); + if (hRomFile == INVALID_HANDLE_VALUE) + { + hRomFile = NULL; + return FALSE; + } + dwRomSize = GetFileSize(hRomFile, &dwFileSizeHigh); + if (dwFileSizeHigh != 0) + { // file is too large. + CloseHandle(hRomFile); + hRomFile = NULL; + dwRomSize = 0; + return FALSE; + } + hRomMap = CreateFileMapping(hRomFile, NULL, bWrite ? PAGE_READWRITE : PAGE_WRITECOPY, 0, dwRomSize, NULL); + if (hRomMap == NULL) + { + CloseHandle(hRomFile); + hRomFile = NULL; + dwRomSize = 0; + return FALSE; + } + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + AbortMessage(_T("Sharing file mapping handle.")); + } + pbyRom = MapViewOfFile(hRomMap, bWrite ? FILE_MAP_WRITE : FILE_MAP_COPY, 0, 0, dwRomSize); + if (pbyRom == NULL) + { + CloseHandle(hRomMap); + CloseHandle(hRomFile); + hRomMap = NULL; + hRomFile = NULL; + dwRomSize = 0; + return FALSE; + } + // check for packed ROM image + if ((*(DWORD *) pbyRom & 0xF0F0F0F0) != 0) + { + UnmapRom(); // free memory + AbortMessage(_T("Packed ROM image detected!")); + return FALSE; + } + return TRUE; +} + +VOID UnmapRom(VOID) +{ + if (pbyRom == NULL) return; + RestorePatches(); // restore ROM Patches + UnmapViewOfFile(pbyRom); + CloseHandle(hRomMap); + CloseHandle(hRomFile); + pbyRom = NULL; + hRomMap = NULL; + hRomFile = NULL; + dwRomSize = 0; + return; +} + + + +//################ +//# +//# Port2 +//# +//################ + +WORD CrcPort2(VOID) // calculate fingerprint of port2 +{ + DWORD dwCount; + DWORD dwFileSize; + WORD wCrc = 0; + + // port2 CRC isn't available + if (pbyPort2 == NULL) return wCrc; + + dwFileSize = GetFileSize(hPort2File, &dwCount); // get real filesize + _ASSERT(dwCount == 0); // isn't created by MapPort2() + + for (dwCount = 0;dwCount < dwFileSize; ++dwCount) + wCrc = (wCrc >> 4) ^ (((wCrc ^ ((WORD) pbyPort2[dwCount])) & 0xf) * 0x1081); + return wCrc; +} + +BOOL MapPort2(LPCTSTR szFilename) +{ + DWORD dwFileSizeLo,dwFileSizeHi,dwCount; + + if (pbyPort2 != NULL) return FALSE; + bPort2Writeable = TRUE; + dwPort2Size = 0; // reset size of port2 + + SetCurrentDirectory(szEmuDirectory); + hPort2File = CreateFile(szFilename, + GENERIC_READ|GENERIC_WRITE, + bPort2IsShared ? FILE_SHARE_READ : 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hPort2File == INVALID_HANDLE_VALUE) + { + bPort2Writeable = FALSE; + hPort2File = CreateFile(szFilename, + GENERIC_READ, + bPort2IsShared ? (FILE_SHARE_READ|FILE_SHARE_WRITE) : 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hPort2File == INVALID_HANDLE_VALUE) + { + SetCurrentDirectory(szCurrentDirectory); + hPort2File = NULL; + return FALSE; + } + } + SetCurrentDirectory(szCurrentDirectory); + dwFileSizeLo = GetFileSize(hPort2File, &dwFileSizeHi); + if (dwFileSizeHi != 0) + { // file is too large. + CloseHandle(hPort2File); + hPort2File = NULL; + dwPort2Mask = 0; + bPort2Writeable = FALSE; + return FALSE; + } + + // count number of set bits + for (dwCount = 0, dwFileSizeHi = dwFileSizeLo; dwFileSizeHi != 0;dwFileSizeHi >>= 1) + { + if ((dwFileSizeHi & 0x1) != 0) ++dwCount; + } + + // size not 32, 128, 256, 512, 1024, 2048 or 4096 KB + if (dwCount != 1 || (dwFileSizeLo & 0xFF02FFFF) != 0) + { + CloseHandle(hPort2File); + hPort2File = NULL; + dwPort2Mask = 0; + bPort2Writeable = FALSE; + return FALSE; + } + + dwPort2Mask = (dwFileSizeLo - 1) >> 18; // mask for valid address lines of the BS-FF + hPort2Map = CreateFileMapping(hPort2File, NULL, bPort2Writeable ? PAGE_READWRITE : PAGE_READONLY, + 0, dwFileSizeLo, NULL); + if (hPort2Map == NULL) + { + CloseHandle(hPort2File); + hPort2File = NULL; + dwPort2Mask = 0; + bPort2Writeable = FALSE; + return FALSE; + } + pbyPort2 = MapViewOfFile(hPort2Map, bPort2Writeable ? FILE_MAP_WRITE : FILE_MAP_READ, 0, 0, dwFileSizeLo); + if (pbyPort2 == NULL) + { + CloseHandle(hPort2Map); + CloseHandle(hPort2File); + hPort2Map = NULL; + hPort2File = NULL; + dwPort2Mask = 0; + bPort2Writeable = FALSE; + return FALSE; + } + dwPort2Size = dwFileSizeLo / 2048; // mapping size of port2 + return TRUE; +} + +VOID UnmapPort2(VOID) +{ + if (pbyPort2 == NULL) return; + UnmapViewOfFile(pbyPort2); + CloseHandle(hPort2Map); + CloseHandle(hPort2File); + pbyPort2 = NULL; + hPort2Map = NULL; + hPort2File = NULL; + dwPort2Size = 0; // reset size of port2 + dwPort2Mask = 0; + bPort2Writeable = FALSE; + return; +} + + + +//################ +//# +//# Documents +//# +//################ + +VOID ResetDocument(VOID) +{ + if (szCurrentKml[0]) + { + KillKML(); + } + if (hCurrentFile) + { + CloseHandle(hCurrentFile); + hCurrentFile = NULL; + } + szCurrentKml[0] = 0; + szCurrentFilename[0]=0; + if (Chipset.Port0) HeapFree(hHeap,0,Chipset.Port0); + if (Chipset.Port1) HeapFree(hHeap,0,Chipset.Port1); + if (Chipset.Port2) HeapFree(hHeap,0,Chipset.Port2); else UnmapPort2(); + ZeroMemory(&Chipset,sizeof(Chipset)); + ZeroMemory(&RMap,sizeof(RMap)); // delete MMU mappings + ZeroMemory(&WMap,sizeof(WMap)); + UpdateWindowStatus(); + return; +} + +BOOL NewDocument(VOID) +{ + SaveBackup(); + ResetDocument(); + + if (!DisplayChooseKml(0)) goto restore; + if (!InitKML(szCurrentKml,FALSE)) goto restore; + Chipset.type = cCurrentRomType; + + if (Chipset.type == '6' || Chipset.type == 'A') // HP38G + { + Chipset.Port0Size = (Chipset.type == 'A') ? 32 : 64; + Chipset.Port1Size = 0; + Chipset.Port2Size = 0; + + Chipset.cards_status = 0x0; + } + if (Chipset.type == 'E' || Chipset.type == 'P') // HP39/40G/HP39G+ // CdB for HP: add apples + { + Chipset.Port0Size = 128; + Chipset.Port1Size = 0; + Chipset.Port2Size = 128; + + Chipset.cards_status = 0xF; + + bPort2Writeable = TRUE; // port2 is writeable + } + if (Chipset.type == 'S') // HP48SX + { + Chipset.Port0Size = 32; + Chipset.Port1Size = 128; + Chipset.Port2Size = 0; + + Chipset.cards_status = 0x5; + + // use 2nd command line argument if defined + MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]); + } + if (Chipset.type == 'G') // HP48GX + { + Chipset.Port0Size = 128; + Chipset.Port1Size = 128; + Chipset.Port2Size = 0; + + Chipset.cards_status = 0xA; + + // use 2nd command line argument if defined + MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]); + } + if (Chipset.type == 'X' || Chipset.type == '2' || Chipset.type == 'Q') // HP49G/HP48Gii/HP49G+ // CdB for HP: add apples + { + Chipset.Port0Size = 256; + Chipset.Port1Size = 128; + Chipset.Port2Size = 128; + + Chipset.cards_status = 0xF; + bPort2Writeable = TRUE; // port2 is writeable + + FlashInit(); // init flash structure + } + if (Chipset.type == 'Q') // HP49G+ // CdB for HP: add apples + { + Chipset.d0size = 16; + } + + Chipset.IORam[LPE] = RST; // set ReSeT bit at power on reset + + // allocate port memory + if (Chipset.Port0Size) + { + Chipset.Port0 = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,Chipset.Port0Size*2048); + _ASSERT(Chipset.Port0 != NULL); + } + if (Chipset.Port1Size) + { + Chipset.Port1 = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,Chipset.Port1Size*2048); + _ASSERT(Chipset.Port1 != NULL); + } + if (Chipset.Port2Size) + { + Chipset.Port2 = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,Chipset.Port2Size*2048); + _ASSERT(Chipset.Port2 != NULL); + } + LoadBreakpointList(NULL); // clear debugger breakpoint list + RomSwitch(0); // boot ROM view of HP49G and map memory + return TRUE; +restore: + RestoreBackup(); + ResetBackup(); + + // HP48SX/GX + if(Chipset.type == 'S' || Chipset.type == 'G') + { + // use 2nd command line argument if defined + MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]); + } + if (pbyRom) + { + SetWindowLocation(hWnd,Chipset.nPosX,Chipset.nPosY); + Map(0x00,0xFF); + } + return FALSE; +} + +BOOL OpenDocument(LPCTSTR szFilename) +{ + HANDLE hFile = INVALID_HANDLE_VALUE; + DWORD lBytesRead,lSizeofChipset; + BYTE pbyFileSignature[16]; + LPBYTE pbySig; + UINT ctBytesCompared; + UINT nLength; + + SaveBackup(); + ResetDocument(); + + // Open file + if (lstrcmpi(szBackupFilename, szFilename)==0) + { + if (YesNoMessage(_T("Do you want to reload this document ?")) == IDNO) + goto restore; + } + hFile = CreateFile(szFilename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + AbortMessage(_T("This file is missing or already loaded in another instance of Emu48.")); + goto restore; + } + + // Read and Compare signature + ReadFile(hFile, pbyFileSignature, 16, &lBytesRead, NULL); + switch (pbyFileSignature[0]) + { + case 'E': + pbySig = (pbyFileSignature[3] == '3') + ? ((pbyFileSignature[4] == '8') ? pbySignatureA : pbySignatureB) + : ((pbyFileSignature[4] == '8') ? pbySignatureE : pbySignatureV); + for (ctBytesCompared=0; ctBytesCompared<14; ctBytesCompared++) + { + if (pbyFileSignature[ctBytesCompared]!=pbySig[ctBytesCompared]) + { + AbortMessage(_T("This file is not a valid Emu48 document.")); + goto restore; + } + } + break; + case 'W': + for (ctBytesCompared=0; ctBytesCompared<14; ctBytesCompared++) + { + if (pbyFileSignature[ctBytesCompared]!=pbySignatureW[ctBytesCompared]) + { + AbortMessage(_T("This file is not a valid Win48 document.")); + goto restore; + } + } + break; + default: + AbortMessage(_T("This file is not a valid document.")); + goto restore; + } + + switch (pbyFileSignature[14]) + { + case 0xFE: // Win48 2.1 / Emu4x 0.99.x format + ReadFile(hFile,&nLength,sizeof(nLength),&lBytesRead,NULL); + #if defined _UNICODE + { + LPSTR szTmp = HeapAlloc(hHeap,0,nLength); + if (szTmp == NULL) + { + AbortMessage(_T("Memory Allocation Failure.")); + goto restore; + } + ReadFile(hFile, szTmp, nLength, &lBytesRead, NULL); + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szTmp, lBytesRead, + szCurrentKml, ARRAYSIZEOF(szCurrentKml)); + HeapFree(hHeap,0,szTmp); + } + #else + { + ReadFile(hFile, szCurrentKml, nLength, &lBytesRead, NULL); + } + #endif + if (nLength != lBytesRead) goto read_err; + szCurrentKml[nLength] = 0; + break; + case 0xFF: // Win48 2.05 format + break; + default: + AbortMessage(_T("This file is for an unknown version of Emu48.")); + goto restore; + } + + // read chipset size inside file + ReadFile(hFile, &lSizeofChipset, sizeof(lSizeofChipset), &lBytesRead, NULL); + if (lBytesRead != sizeof(lSizeofChipset)) goto read_err; + if (lSizeofChipset <= sizeof(Chipset)) // actual or older chipset version + { + // read chipset content + ZeroMemory(&Chipset,sizeof(Chipset)); // init chipset + ReadFile(hFile, &Chipset, lSizeofChipset, &lBytesRead, NULL); + } + else // newer chipset version + { + // read my used chipset content + ReadFile(hFile, &Chipset, sizeof(Chipset), &lBytesRead, NULL); + + // skip rest of chipset + SetFilePointer(hFile, lSizeofChipset-sizeof(Chipset), NULL, FILE_CURRENT); + lSizeofChipset = sizeof(Chipset); + } + Chipset.Port0 = NULL; // delete invalid port pointers + Chipset.Port1 = NULL; + Chipset.Port2 = NULL; + if (lBytesRead != lSizeofChipset) goto read_err; + + if (!isModelValid(Chipset.type)) // check for valid model in emulator state file + { + AbortMessage(_T("Emulator state file with invalid calculator model.")); + goto restore; + } + + SetWindowLocation(hWnd,Chipset.nPosX,Chipset.nPosY); + + while (TRUE) + { + if (szCurrentKml[0]) // KML file name + { + BOOL bOK; + + bOK = InitKML(szCurrentKml,FALSE); + bOK = bOK && (cCurrentRomType == Chipset.type); + if (bOK) break; + + KillKML(); + } + if (!DisplayChooseKml(Chipset.type)) + goto restore; + } + // reload old button state + ReloadButtons(Chipset.Keyboard_Row,sizeof(Chipset.Keyboard_Row)); + + FlashInit(); // init flash structure + + if (Chipset.Port0Size) + { + Chipset.Port0 = HeapAlloc(hHeap,0,Chipset.Port0Size*2048); + if (Chipset.Port0 == NULL) + { + AbortMessage(_T("Memory Allocation Failure.")); + goto restore; + } + + ReadFile(hFile, Chipset.Port0, Chipset.Port0Size*2048, &lBytesRead, NULL); + if (lBytesRead != Chipset.Port0Size*2048) goto read_err; + } + + if (Chipset.Port1Size) + { + Chipset.Port1 = HeapAlloc(hHeap,0,Chipset.Port1Size*2048); + if (Chipset.Port1 == NULL) + { + AbortMessage(_T("Memory Allocation Failure.")); + goto restore; + } + + ReadFile(hFile, Chipset.Port1, Chipset.Port1Size*2048, &lBytesRead, NULL); + if (lBytesRead != Chipset.Port1Size*2048) goto read_err; + } + + // HP48SX/GX + if(cCurrentRomType=='S' || cCurrentRomType=='G') + { + MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]); + // port2 changed and card detection enabled + if ( Chipset.wPort2Crc != CrcPort2() + && (Chipset.IORam[CARDCTL] & ECDT) != 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0 + ) + { + Chipset.HST |= MP; // set Module Pulled + IOBit(SRQ2,NINT,FALSE); // set NINT to low + Chipset.SoftInt = TRUE; // set interrupt + bInterrupt = TRUE; + } + } + else // HP38G, HP39/40G, HP49G + { + if (Chipset.Port2Size) + { + Chipset.Port2 = HeapAlloc(hHeap,0,Chipset.Port2Size*2048); + if (Chipset.Port2 == NULL) + { + AbortMessage(_T("Memory Allocation Failure.")); + goto restore; + } + + ReadFile(hFile, Chipset.Port2, Chipset.Port2Size*2048, &lBytesRead, NULL); + if (lBytesRead != Chipset.Port2Size*2048) goto read_err; + + bPort2Writeable = TRUE; + Chipset.cards_status = 0xF; + } + } + + LoadBreakpointList(hFile); // load debugger breakpoint list + RomSwitch(Chipset.Bank_FF); // reload ROM view of HP49G and map memory + + if (Chipset.wRomCrc != CrcRom()) // ROM changed + { + CpuReset(); + Chipset.Shutdn = FALSE; // automatic restart + } + + lstrcpy(szCurrentFilename, szFilename); + _ASSERT(hCurrentFile == NULL); + hCurrentFile = hFile; + #if defined _USRDLL // DLL version + // notify main proc about current document file + if (pEmuDocumentNotify) pEmuDocumentNotify(szCurrentFilename); + #endif + SetWindowPathTitle(szCurrentFilename); // update window title line + UpdateWindowStatus(); + return TRUE; + +read_err: + AbortMessage(_T("This file must be truncated, and cannot be loaded.")); +restore: + if (INVALID_HANDLE_VALUE != hFile) // close if valid handle + CloseHandle(hFile); + RestoreBackup(); + ResetBackup(); + + // HP48SX/GX + if(cCurrentRomType=='S' || cCurrentRomType=='G') + { + // use 2nd command line argument if defined + MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]); + } + return FALSE; +} + +BOOL SaveDocument(VOID) +{ + DWORD lBytesWritten; + DWORD lSizeofChipset; + LPBYTE pbySig; + UINT nLength; + WINDOWPLACEMENT wndpl; + + if (hCurrentFile == NULL) return FALSE; + + _ASSERT(hWnd); // window open + wndpl.length = sizeof(wndpl); // update saved window position + GetWindowPlacement(hWnd, &wndpl); + Chipset.nPosX = (SWORD) wndpl.rcNormalPosition.left; + Chipset.nPosY = (SWORD) wndpl.rcNormalPosition.top; + + SetFilePointer(hCurrentFile,0,NULL,FILE_BEGIN); + + // get document signature + pbySig = (Chipset.type=='6' || Chipset.type=='A') + ? pbySignatureA : (Chipset.type=='E' ? pbySignatureB : ((Chipset.type!='X') ? pbySignatureE : pbySignatureV)); + + if (!WriteFile(hCurrentFile, pbySig, 16, &lBytesWritten, NULL)) + { + AbortMessage(_T("Could not write into file !")); + return FALSE; + } + + Chipset.wRomCrc = CrcRom(); // save fingerprint of ROM + Chipset.wPort2Crc = CrcPort2(); // save fingerprint of port2 + + nLength = lstrlen(szCurrentKml); + WriteFile(hCurrentFile, &nLength, sizeof(nLength), &lBytesWritten, NULL); + #if defined _UNICODE + { + LPSTR szTmp = HeapAlloc(hHeap,0,nLength); + if (szTmp != NULL) + { + WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, + szCurrentKml, nLength, + szTmp, nLength, NULL, NULL); + WriteFile(hCurrentFile, szTmp, nLength, &lBytesWritten, NULL); + HeapFree(hHeap,0,szTmp); + } + } + #else + { + WriteFile(hCurrentFile, szCurrentKml, nLength, &lBytesWritten, NULL); + } + #endif + lSizeofChipset = sizeof(CHIPSET); + WriteFile(hCurrentFile, &lSizeofChipset, sizeof(lSizeofChipset), &lBytesWritten, NULL); + WriteFile(hCurrentFile, &Chipset, lSizeofChipset, &lBytesWritten, NULL); + if (Chipset.Port0Size) WriteFile(hCurrentFile, Chipset.Port0, Chipset.Port0Size*2048, &lBytesWritten, NULL); + if (Chipset.Port1Size) WriteFile(hCurrentFile, Chipset.Port1, Chipset.Port1Size*2048, &lBytesWritten, NULL); + if (Chipset.Port2Size) WriteFile(hCurrentFile, Chipset.Port2, Chipset.Port2Size*2048, &lBytesWritten, NULL); + SaveBreakpointList(hCurrentFile); // save debugger breakpoint list + SetEndOfFile(hCurrentFile); // cut the rest + return TRUE; +} + +BOOL SaveDocumentAs(LPCTSTR szFilename) +{ + HANDLE hFile; + + if (hCurrentFile) // already file in use + { + CloseHandle(hCurrentFile); // close it, even it's same, so data always will be saved + hCurrentFile = NULL; + } + hFile = CreateFile(szFilename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL); + if (hFile == INVALID_HANDLE_VALUE) // error, couldn't create a new file + { + AbortMessage(_T("This file must be currently used by another instance of Emu48.")); + return FALSE; + } + lstrcpy(szCurrentFilename, szFilename); // save new file name + hCurrentFile = hFile; // and the corresponding handle + #if defined _USRDLL // DLL version + // notify main proc about current document file + if (pEmuDocumentNotify) pEmuDocumentNotify(szCurrentFilename); + #endif + SetWindowPathTitle(szCurrentFilename); // update window title line + UpdateWindowStatus(); // and draw it + return SaveDocument(); // save current content +} + + + +//################ +//# +//# Backup +//# +//################ + +BOOL SaveBackup(VOID) +{ + WINDOWPLACEMENT wndpl; + + if (pbyRom == NULL) return FALSE; + + // save window position + _ASSERT(hWnd); // window open + wndpl.length = sizeof(wndpl); // update saved window position + GetWindowPlacement(hWnd, &wndpl); + Chipset.nPosX = (SWORD) wndpl.rcNormalPosition.left; + Chipset.nPosY = (SWORD) wndpl.rcNormalPosition.top; + + lstrcpy(szBackupFilename, szCurrentFilename); + lstrcpy(szBackupKml, szCurrentKml); + if (BackupChipset.Port0) HeapFree(hHeap,0,BackupChipset.Port0); + if (BackupChipset.Port1) HeapFree(hHeap,0,BackupChipset.Port1); + if (BackupChipset.Port2) HeapFree(hHeap,0,BackupChipset.Port2); + CopyMemory(&BackupChipset, &Chipset, sizeof(Chipset)); + BackupChipset.Port0 = HeapAlloc(hHeap,0,Chipset.Port0Size*2048); + CopyMemory(BackupChipset.Port0, Chipset.Port0, Chipset.Port0Size*2048); + BackupChipset.Port1 = HeapAlloc(hHeap,0,Chipset.Port1Size*2048); + CopyMemory(BackupChipset.Port1, Chipset.Port1, Chipset.Port1Size*2048); + BackupChipset.Port2 = NULL; + if (Chipset.Port2Size) // internal port2 + { + BackupChipset.Port2 = HeapAlloc(hHeap,0,Chipset.Port2Size*2048); + CopyMemory(BackupChipset.Port2, Chipset.Port2, Chipset.Port2Size*2048); + } + bBackup = TRUE; + UpdateWindowStatus(); + return TRUE; +} + +BOOL RestoreBackup(VOID) +{ + if (!bBackup) return FALSE; + ResetDocument(); + // need chipset for contrast setting in InitKML() + Chipset.contrast = BackupChipset.contrast; + if (!InitKML(szBackupKml,TRUE)) + { + InitKML(szCurrentKml,TRUE); + return FALSE; + } + lstrcpy(szCurrentKml, szBackupKml); + lstrcpy(szCurrentFilename, szBackupFilename); + if (szCurrentFilename[0]) + { + hCurrentFile = CreateFile(szCurrentFilename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (hCurrentFile == INVALID_HANDLE_VALUE) + { + hCurrentFile = NULL; + szCurrentFilename[0] = 0; + } + } + CopyMemory(&Chipset, &BackupChipset, sizeof(Chipset)); + Chipset.Port0 = HeapAlloc(hHeap,0,Chipset.Port0Size*2048); + CopyMemory(Chipset.Port0, BackupChipset.Port0, Chipset.Port0Size*2048); + Chipset.Port1 = HeapAlloc(hHeap,0,Chipset.Port1Size*2048); + CopyMemory(Chipset.Port1, BackupChipset.Port1, Chipset.Port1Size*2048); + if (Chipset.Port2Size) // internal port2 + { + Chipset.Port2 = HeapAlloc(hHeap,0,Chipset.Port2Size*2048); + CopyMemory(Chipset.Port2, BackupChipset.Port2, Chipset.Port2Size*2048); + } + // map port2 + else + { + if(cCurrentRomType=='S' || cCurrentRomType=='G') // HP48SX/GX + { + // use 2nd command line argument if defined + MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]); + } + } + SetWindowPathTitle(szCurrentFilename); // update window title line + SetWindowLocation(hWnd,Chipset.nPosX,Chipset.nPosY); + UpdateWindowStatus(); + Map(0x00,0xFF); + return TRUE; +} + +BOOL ResetBackup(VOID) +{ + if (!bBackup) return FALSE; + szBackupFilename[0] = 0; + szBackupKml[0] = 0; + if (BackupChipset.Port0) HeapFree(hHeap,0,BackupChipset.Port0); + if (BackupChipset.Port1) HeapFree(hHeap,0,BackupChipset.Port1); + if (BackupChipset.Port2) HeapFree(hHeap,0,BackupChipset.Port2); + ZeroMemory(&BackupChipset,sizeof(BackupChipset)); + bBackup = FALSE; + UpdateWindowStatus(); + return TRUE; +} + + + +//################ +//# +//# Open File Common Dialog Boxes +//# +//################ + +static VOID InitializeOFN(LPOPENFILENAME ofn) +{ + ZeroMemory((LPVOID)ofn, sizeof(OPENFILENAME)); + ofn->lStructSize = sizeof(OPENFILENAME); + ofn->hwndOwner = hWnd; + ofn->Flags = OFN_EXPLORER|OFN_HIDEREADONLY; + return; +} + +BOOL GetOpenFilename(VOID) +{ + TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)]; + OPENFILENAME ofn; + + InitializeOFN(&ofn); + ofn.lpstrFilter = + _T("Emu38 Document (*.E38)\0*.E38\0") + _T("Emu39 Document (*.E39)\0*.E39\0") + _T("Emu48 Document (*.E48)\0*.E48\0") + _T("Emu49 Document (*.E49)\0*.E49\0") + _T("Win48 Document (*.W48)\0*.W48\0") + _T("\0\0"); + ofn.lpstrDefExt = _T("E48"); // HP48SX/GX + ofn.nFilterIndex = 3; + if (cCurrentRomType=='6' || cCurrentRomType=='A') // HP38G + { + ofn.lpstrDefExt = _T("E38"); + ofn.nFilterIndex = 1; + } + if (cCurrentRomType=='E' || cCurrentRomType=='P') // HP39/40G/HP49G+ // CdB for HP: add apples + { + ofn.lpstrDefExt = _T("E39"); + ofn.nFilterIndex = 2; + } + if (cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q') // HP49G/HP48Gii/HP49G+ // CdB for HP: add apples + { + ofn.lpstrDefExt = _T("E49"); + ofn.nFilterIndex = 4; + } + ofn.lpstrFile = szBuffer; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szBuffer); + ofn.Flags |= OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST; + if (GetOpenFileName(&ofn) == FALSE) return FALSE; + _ASSERT(ARRAYSIZEOF(szBufferFilename) == ofn.nMaxFile); + lstrcpy(szBufferFilename, ofn.lpstrFile); + return TRUE; +} + +BOOL GetSaveAsFilename(VOID) +{ + TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)]; + OPENFILENAME ofn; + + InitializeOFN(&ofn); + ofn.lpstrFilter = + _T("Emu38 Document (*.E38)\0*.E38\0") + _T("Emu39 Document (*.E39)\0*.E39\0") + _T("Emu48 Document (*.E48)\0*.E48\0") + _T("Emu49 Document (*.E49)\0*.E49\0") + _T("Win48 Document (*.W48)\0*.W48\0") + _T("\0\0"); + ofn.lpstrDefExt = _T("E48"); // HP48SX/GX + ofn.nFilterIndex = 3; + if (cCurrentRomType=='6' || cCurrentRomType=='A') // HP38G + { + ofn.lpstrDefExt = _T("E38"); + ofn.nFilterIndex = 1; + } + if (cCurrentRomType=='E' || cCurrentRomType=='P') // HP39/40G/hp439G+ // CdB for HP: add apples + { + ofn.lpstrDefExt = _T("E39"); + ofn.nFilterIndex = 2; + } + if (cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q') // HP49G/HP48Gii/HP49G+ // CdB for HP: add apples + { + ofn.lpstrDefExt = _T("E49"); + ofn.nFilterIndex = 4; + } + ofn.lpstrFile = szBuffer; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szBuffer); + ofn.Flags |= OFN_CREATEPROMPT|OFN_OVERWRITEPROMPT; + if (GetSaveFileName(&ofn) == FALSE) return FALSE; + _ASSERT(ARRAYSIZEOF(szBufferFilename) == ofn.nMaxFile); + lstrcpy(szBufferFilename, ofn.lpstrFile); + return TRUE; +} + +BOOL GetLoadObjectFilename(VOID) +{ + TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)]; + OPENFILENAME ofn; + + InitializeOFN(&ofn); + ofn.lpstrFilter = _T("All Files (*.*)\0*.*\0") _T("\0\0"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = szBuffer; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szBuffer); + ofn.Flags |= OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST; + if (GetOpenFileName(&ofn) == FALSE) return FALSE; + _ASSERT(ARRAYSIZEOF(szBufferFilename) == ofn.nMaxFile); + lstrcpy(szBufferFilename, ofn.lpstrFile); + return TRUE; +} + +BOOL GetSaveObjectFilename(VOID) +{ + TCHAR szBuffer[ARRAYSIZEOF(szBufferFilename)]; + OPENFILENAME ofn; + + InitializeOFN(&ofn); + ofn.lpstrFilter = _T("All Files (*.*)\0*.*\0") _T("\0\0"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = szBuffer; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szBuffer); + ofn.Flags |= OFN_CREATEPROMPT|OFN_OVERWRITEPROMPT; + if (GetSaveFileName(&ofn) == FALSE) return FALSE; + _ASSERT(ARRAYSIZEOF(szBufferFilename) == ofn.nMaxFile); + lstrcpy(szBufferFilename, ofn.lpstrFile); + return TRUE; +} + + + +//################ +//# +//# Load and Save HP48 Objects +//# +//################ + +WORD WriteStack(UINT nStkLevel,LPBYTE lpBuf,DWORD dwSize) // separated from LoadObject() +{ + BOOL bBinary; + DWORD dwAddress, i; + + bBinary = ((lpBuf[dwSize+0]=='H') + && (lpBuf[dwSize+1]=='P') + && (lpBuf[dwSize+2]=='H') + && (lpBuf[dwSize+3]=='P') + && (lpBuf[dwSize+4]=='4') + && (lpBuf[dwSize+5]==((cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q') ? '9' : '8')) // CdB for HP: add apples + && (lpBuf[dwSize+6]=='-')); + + for (i = 0; i < dwSize; i++) + { + BYTE byTwoNibs = lpBuf[i+dwSize]; + lpBuf[i*2 ] = (BYTE)(byTwoNibs&0xF); + lpBuf[i*2+1] = (BYTE)(byTwoNibs>>4); + } + + if (bBinary == TRUE) + { // load as binary + dwSize = RPL_ObjectSize(lpBuf+16); + dwAddress = RPL_CreateTemp(dwSize); + if (dwAddress == 0) return S_ERR_BINARY; + + Nwrite(lpBuf+16,dwAddress,dwSize); + } + else + { // load as string + dwSize *= 2; + dwAddress = RPL_CreateTemp(dwSize+10); + if (dwAddress == 0) return S_ERR_ASCII; + Write5(dwAddress,0x02A2C); // String + Write5(dwAddress+5,dwSize+5); // length of String + Nwrite(lpBuf,dwAddress+10,dwSize); // data + } + RPL_Push(nStkLevel,dwAddress); + return S_ERR_NO; +} + +BOOL LoadObject(LPCTSTR szFilename) // separated stack writing part +{ + HANDLE hFile; + DWORD dwFileSizeLow; + DWORD dwFileSizeHigh; + LPBYTE lpBuf; + WORD wError; + + hFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + if (hFile == INVALID_HANDLE_VALUE) return FALSE; + dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh); + if (dwFileSizeHigh != 0) + { // file is too large. + CloseHandle(hFile); + return FALSE; + } + lpBuf = HeapAlloc(hHeap,0,dwFileSizeLow*2); + if (lpBuf == NULL) + { + CloseHandle(hFile); + return FALSE; + } + ReadFile(hFile, lpBuf+dwFileSizeLow, dwFileSizeLow, &dwFileSizeHigh, NULL); + CloseHandle(hFile); + + wError = WriteStack(1,lpBuf,dwFileSizeLow); + + if (wError == S_ERR_BINARY) + AbortMessage(_T("The calculator does not have enough\nfree memory to load this binary file.")); + + if (wError == S_ERR_ASCII) + AbortMessage(_T("The calculator does not have enough\nfree memory to load this text file.")); + + HeapFree(hHeap,0,lpBuf); + return (wError == S_ERR_NO); +} + +BOOL SaveObject(LPCTSTR szFilename) // separated stack reading part +{ + HANDLE hFile; + LPBYTE pbyHeader; + DWORD lBytesWritten; + DWORD dwAddress; + DWORD dwLength; + + dwAddress = RPL_Pick(1); + if (dwAddress == 0) + { + AbortMessage(_T("Too Few Arguments.")); + return FALSE; + } + dwLength = (RPL_SkipOb(dwAddress) - dwAddress + 1) / 2; + + hFile = CreateFile(szFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + AbortMessage(_T("Cannot open file.")); + return FALSE; + } + + pbyHeader = ((cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q')) + ? BINARYHEADER49 + : BINARYHEADER48; + + WriteFile(hFile, pbyHeader, 8, &lBytesWritten, NULL); + + while (dwLength--) + { + BYTE byByte = Read2(dwAddress); + WriteFile(hFile, &byByte, 1, &lBytesWritten, NULL); + dwAddress += 2; + } + CloseHandle(hFile); + return TRUE; +} + + + +//################ +//# +//# Load Bitmap +//# +//################ + +static __inline UINT DibNumColors(BITMAPINFOHEADER CONST *lpbi) +{ + if (lpbi->biClrUsed != 0) return (UINT)lpbi->biClrUsed; + + /* a 24 bitcount DIB has no color table */ + return (lpbi->biBitCount <= 8) ? (1 << lpbi->biBitCount) : 0; +} + +static HPALETTE CreateBIPalette(BITMAPINFOHEADER CONST *lpbi) +{ + LOGPALETTE* pPal; + HPALETTE hpal = NULL; + UINT nNumColors; + BYTE red; + BYTE green; + BYTE blue; + UINT i; + RGBQUAD* pRgb; + + if (!lpbi) + return NULL; + + if (lpbi->biSize != sizeof(BITMAPINFOHEADER)) + return NULL; + + // Get a pointer to the color table and the number of colors in it + pRgb = (RGBQUAD *)((LPBYTE)lpbi + (WORD)lpbi->biSize); + nNumColors = DibNumColors(lpbi); + + if (nNumColors) + { + // Allocate for the logical palette structure + pPal = HeapAlloc(hHeap,0,sizeof(LOGPALETTE) + nNumColors * sizeof(PALETTEENTRY)); + if (!pPal) return NULL; + + pPal->palNumEntries = nNumColors; + pPal->palVersion = 0x300; + + // Fill in the palette entries from the DIB color table and + // create a logical color palette. + for (i = 0; i < nNumColors; i++) + { + pPal->palPalEntry[i].peRed = pRgb[i].rgbRed; + pPal->palPalEntry[i].peGreen = pRgb[i].rgbGreen; + pPal->palPalEntry[i].peBlue = pRgb[i].rgbBlue; + pPal->palPalEntry[i].peFlags = 0; + } + hpal = CreatePalette(pPal); + HeapFree(hHeap,0,pPal); + } + else + { + // create halftone palette for 16, 24 and 32 bitcount bitmaps + + // 16, 24 and 32 bitcount DIB's have no color table entries so, set the + // number of to the maximum value (256). + nNumColors = 256; + pPal = HeapAlloc(hHeap,0,sizeof(LOGPALETTE) + nNumColors * sizeof(PALETTEENTRY)); + if (!pPal) return NULL; + + pPal->palNumEntries = nNumColors; + pPal->palVersion = 0x300; + + red = green = blue = 0; + + // Generate 256 (= 8*8*4) RGB combinations to fill the palette + // entries. + for (i = 0; i < pPal->palNumEntries; i++) + { + pPal->palPalEntry[i].peRed = red; + pPal->palPalEntry[i].peGreen = green; + pPal->palPalEntry[i].peBlue = blue; + pPal->palPalEntry[i].peFlags = 0; + + if (!(red += 32)) + if (!(green += 32)) + blue += 64; + } + hpal = CreatePalette(pPal); + HeapFree(hHeap,0,pPal); + } + return hpal; +} + +HBITMAP LoadBitmapFile(LPCTSTR szFilename) +{ + HANDLE hFile; + HANDLE hMap; + LPBYTE pbyFile; + HBITMAP hBitmap; + LPBITMAPFILEHEADER pBmfh; + LPBITMAPINFO pBmi; + + SetCurrentDirectory(szEmuDirectory); + hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + SetCurrentDirectory(szCurrentDirectory); + // opened with GENERIC_READ -> PAGE_READONLY + hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hMap == NULL) + { + CloseHandle(hFile); + return NULL; + } + // opened with GENERIC_READ -> PAGE_READONLY -> FILE_MAP_READ + pbyFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); + if (pbyFile == NULL) + { + CloseHandle(hMap); + CloseHandle(hFile); + return NULL; + } + + hBitmap = NULL; + pBmfh = (LPBITMAPFILEHEADER)pbyFile; + if (pBmfh->bfType != 0x4D42) goto quit; // "BM" + pBmi = (LPBITMAPINFO)(pbyFile+sizeof(BITMAPFILEHEADER)); + + _ASSERT(hPalette == NULL); // resource free + hPalette = CreateBIPalette(&pBmi->bmiHeader); + // save old palette + hOldPalette = SelectPalette(hWindowDC, hPalette, FALSE); + RealizePalette(hWindowDC); + + hBitmap = CreateDIBitmap( + hWindowDC, + &pBmi->bmiHeader, + CBM_INIT, + pbyFile + pBmfh->bfOffBits, + pBmi, DIB_RGB_COLORS); + _ASSERT(hBitmap != NULL); + +quit: + UnmapViewOfFile(pbyFile); + CloseHandle(hMap); + CloseHandle(hFile); + return hBitmap; +} diff --git a/SOURCE/I28F160.C b/SOURCE/I28F160.C new file mode 100644 index 0000000..d35e5f4 --- /dev/null +++ b/SOURCE/I28F160.C @@ -0,0 +1,716 @@ +/* + * i28f160.c + * + * This file is part of Emu48 + * + * Copyright (C) 2000 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "i28f160.h" + +#define ARRAYSIZEOF(a) (sizeof(a) / sizeof(a[0])) + +// Flash Command Set +#define READ_ARRAY 0xFF +#define READ_ID_CODES 0x90 +#define READ_QUERY 0x98 +#define READ_STATUS_REG 0x70 +#define CLEAR_STATUS_REG 0x50 +#define WRITE_BUFFER 0xE8 +#define WORD_BYTE_PROG1 0x40 +#define WORD_BYTE_PROG2 0x10 +#define BLOCK_ERASE 0x20 +#define BLOCK_ERASE_SUSPEND 0xB0 +#define BLOCK_ERASE_RESUME 0xD0 +#define STS_CONFIG 0xB8 +#define SET_CLR_BLOCK_LOCK 0x60 +#define FULL_CHIP_ERASE 0x30 + +#define CONFIRM 0xD0 + +// Status Register Definition +#define WSMS 0x80 // WRITE STATE MACHINE STATUS +#define ESS 0x40 // ERASE SUSPEND STATUS +#define ECLBS 0x20 // ERASE AND CLEAR LOCK-BIT STATUS +#define BWSLBS 0x10 // PROGRAM AND SET LOCK-BIT STATUS +#define VPPS 0x08 // Vpp STATUS +#define BWSS 0x04 // PROGRAM SUSPEND STATUS +#define DPS 0x02 // DEVICE PROTECT STATUS + +// Extended Status Register Definition +#define WBS 0x80 // WRITE BUFFER STATUS + +// write state defines +#define WRS_DATA 0 // idle state +#define WRS_WR_BUFFER_N 1 // write buffer no. of data +#define WRS_WR_BUFFER_D 2 // write buffer data +#define WRS_WR_BUFFER_C 3 // write buffer confirm +#define WRS_WR_BYTE 4 // write byte/word +#define WRS_BLOCK_ERASE 5 // block erase +#define WRS_CHIP_ERASE 6 // full chip erase +#define WRS_STS_PIN_CONFIG 7 // STS pin Configuration +#define WRS_LOCK_BITS 8 // Set/Clear Block Lock-Bits + +// read state defines +#define RDS_DATA 0 // data read +#define RDS_ID 1 // read identifier codes +#define RDS_QUERY 2 // read query +#define RDS_SR 3 // read status register +#define RDS_XSR 4 // read extended status register + +// global data +WSMSET WSMset; +BOOL bWP = FALSE; // WP# = low, locked blocks cannot be erased + +// function prototypes +// write function WSM state +static VOID WrStateIdle(BYTE a, DWORD d); +static VOID WrStateE8(DWORD d); +static VOID WrStateE8N(BYTE a, DWORD d); +static VOID WrStateE8D(BYTE a, DWORD d); +static VOID WrStateE8C(BYTE a, DWORD d); +static VOID WrState40(DWORD d); +static VOID WrState40D(BYTE a, DWORD d); +static VOID WrState20(DWORD d); +static VOID WrState20C(BYTE a, DWORD d); +static VOID WrState30(DWORD d); +static VOID WrState30C(BYTE a, DWORD d); +static VOID WrStateB8(DWORD d); +static VOID WrStateB8D(BYTE a, DWORD d); +static VOID WrState60(DWORD d); +static VOID WrState60D(BYTE a, DWORD d); + +static VOID (*CONST fnWrState[])(BYTE a, DWORD d) = +{ + WrStateIdle, + WrStateE8N, WrStateE8D, WrStateE8C, + WrState40D, + WrState20C, + WrState30C, + WrStateB8D, + WrState60D +}; + +// read function WSM state +static BYTE RdStateData(DWORD d); +static BYTE RdStateId(DWORD d); +static BYTE RdStateQuery(DWORD d); +static BYTE RdStateSR(DWORD d); +static BYTE RdStateXSR(DWORD d); + +static BYTE (*CONST fnRdState[])(DWORD d) = +{ + RdStateData, RdStateId, RdStateQuery, RdStateSR, RdStateXSR +}; + + +// read query table +// device address A16-A1, A0 unused +static CONST BYTE byQueryTab[] = +{ + // access with "Read Identifier Codes" command + // Identifier codes + 0xB0, // 00, Manufacturer Code + 0xD0, // 01, Device Code (16 Mbit) + 0x00, // 02, Block Lock Configuration + 0x02, // 03, ?? + + 0x00, // 04, Reserved for vendor-specific information + 0x00, // 05, " + 0x00, // 06, " + 0x00, // 07, " + 0x00, // 08, " + 0x00, // 09, " + 0x00, // 0A, " + 0x00, // 0B, " + 0x00, // 0C, " + 0x00, // 0D, " + 0x00, // 0E, " + 0x00, // 0F, " + + // access with "Read Query" command + // CFI query identification string + 0x51, // 10, Query-Unique ASCII string "Q" + 0x52, // 11, Query-Unique ASCII string "R" + 0x59, // 12, Query-Unique ASCII string "Y" + 0x01, // 13, Primary Vendor Command Set and Control Interface ID CODE + 0x00, // 14, " + 0x31, // 15, Address for Primary Algorithm Extended Query Table + 0x00, // 16, " + 0x00, // 17, Alternate Vendor Command Set and Control Interface ID Code + 0x00, // 18, " + 0x00, // 19, Address for Secondary Algorithm Extended Query Table + 0x00, // 1A, " + + // System interface information + 0x30, // 1B, Vcc Logic Supply Minimum Program/Erase Voltage (0x27 intel doc, 0x30 real chip) + 0x55, // 1C, Vcc Logic Supply Maximum Program/Erase Voltage + 0x30, // 1D, Vpp [Programming] Supply Minimum Program/Erase Voltage (0x27 intel doc, 0x30 real chip) + 0x55, // 1E, Vpp [Programming] Supply Maximum Program/Erase Voltage + 0x03, // 1F, Typical Time-Out per Single Byte/Word Program + 0x06, // 20, Typical Time-Out for Max. Buffer Write + 0x0A, // 21, Typical Time-Out per Individual Block Erase + 0x0F, // 22, Typical Time-Out for Full Chip Erase + 0x04, // 23, Maximum Time-Out for Byte/Word Program + 0x04, // 24, Maximum Time-Out for Buffer Write + 0x04, // 25, Maximum Time-Out per Individual Block Erase + 0x04, // 26, Maximum Time-Out for Full Chip Erase + 0x15, // 27, Device Size + 0x02, // 28, Flash Device Interface Description + 0x00, // 29, " + 0x05, // 2A, Maximum Number of Bytes in Write Buffer + 0x00, // 2B, " + 0x01, // 2C, Number of Erase Block Regions within Device + 0x1F, // 2D, Erase Block Region Information + 0x00, // 2E, " + 0x00, // 2F, " + 0x01, // 30, " + + // Intel-specific extended query table + 0x50, // 31, Primary Extended Query Table, Unique ASCII string "P" + 0x52, // 32, Primary Extended Query Table, Unique ASCII string "R" + 0x49, // 33, Primary Extended Query Table, Unique ASCII string "I" + 0x31, // 34, Major Version Number, ASCII + 0x30, // 35, Minor Version Number, ASCII + 0x0F, // 36, Optional Feature & Command Support + 0x00, // 37, " + 0x00, // 38, " + 0x00, // 39, " + 0x01, // 3A, Supported Functions after Suspend + 0x03, // 3B, Block Status Register Mask + 0x00, // 3C, " + 0x50, // 3D, Vcc Logic Supply Optimum Program/Erase voltage + 0x50, // 3E, Vpp [Programming] Supply Optimum Program/Erase voltage + 0x00 // 3F, ?? +}; + + +// +// write state functions +// + +static VOID WrStateIdle(BYTE a, DWORD d) +{ + WSMset.bRomArray = FALSE; // register access + + switch(a) + { + case READ_ARRAY: // read array mode, normal operation + WSMset.bRomArray = TRUE; // data array access + WSMset.uWrState = WRS_DATA; + WSMset.uRdState = RDS_DATA; + break; + case READ_ID_CODES: // read identifier codes register + WSMset.uRdState = RDS_ID; + break; + case READ_QUERY: // read query register + WSMset.uRdState = RDS_QUERY; + break; + case READ_STATUS_REG: // read status register + WSMset.uRdState = RDS_SR; + break; + case CLEAR_STATUS_REG: // clear status register + WSMset.byStatusReg = 0; + break; + case WRITE_BUFFER: // write to buffer + WrStateE8(d); + break; + case WORD_BYTE_PROG1: + case WORD_BYTE_PROG2: // byte/word program + WrState40(d); + break; + case BLOCK_ERASE: // block erase + WrState20(d); + break; + case BLOCK_ERASE_SUSPEND: // block erase, word/byte program suspend + WSMset.byStatusReg |= WSMS; // operation suspended + WSMset.byStatusReg &= ~ESS; // block erase completed (because no timing emulation) + WSMset.byStatusReg &= ~BWSS; // program completed (because no timing emulation) + WSMset.uRdState = RDS_SR; + break; + case BLOCK_ERASE_RESUME: // block erase, word/byte program resume + WSMset.byStatusReg &= ~WSMS; // operation in progress + WSMset.byStatusReg &= ~ESS; // block erase in progress + WSMset.byStatusReg &= ~BWSS; // program in progress + WSMset.byStatusReg |= WSMS; // operation completed (because no timing emulation) + WSMset.uRdState = RDS_SR; + break; + case STS_CONFIG: + WSMset.bRomArray = bFlashRomArray; // old access mode + WrStateB8(d); + break; + case SET_CLR_BLOCK_LOCK: // set/clear block lock-bits + WrState60(d); + break; + case FULL_CHIP_ERASE: // full chip erase + WrState30(d); + break; + default: // wrong command + WSMset.bRomArray = bFlashRomArray; // old access mode + break; + } + + if(bFlashRomArray != WSMset.bRomArray) // new access mode + { + bFlashRomArray = WSMset.bRomArray; // change register access + Map(0x00,0xFF); // update memory mapping + UpdatePatches(bFlashRomArray); // patch/unpatch ROM again + } + return; +} + +// write to buffer initial command +static VOID WrStateE8(DWORD d) +{ + // @todo add 2nd write buffer implementation + // @todo add program timing implementation + + WSMset.byExStatusReg = 0; // no write buffer + if (WSMset.byWrite1No == 0) // buffer1 available + { + WSMset.byWrite1No = 1; // buffer1 in use + WSMset.dwWrite1Addr = d; // byte block address of buffer1 + WSMset.byExStatusReg = WBS; // write buffer available + // fill write buffer + FillMemory(WSMset.pbyWrite1,ARRAYSIZEOF(WSMset.pbyWrite1),0xFF); + WSMset.uWrState = WRS_WR_BUFFER_N; // set state machine + WSMset.uRdState = RDS_XSR; + } + return; +} + +// write to buffer number of byte +static VOID WrStateE8N(BYTE a, DWORD d) +{ + if (a < (1 << byQueryTab[0x2A])) // byte is length information + { + WSMset.byWrite1No += a; // save no. of byte to program + WSMset.byWrite1Size = a; // save size to check write buffer boundaries + WSMset.dwWrite1Addr = d; // byte block address of buffer1 + WSMset.byStatusReg &= ~WSMS; // state machine busy + WSMset.uWrState = WRS_WR_BUFFER_D; + } + else + { + WSMset.byWrite1No = 0; // free write buffer + // improper command sequence + WSMset.byStatusReg |= (ECLBS | BWSLBS); + WSMset.byStatusReg |= WSMS; // data written + WSMset.uWrState = WRS_DATA; + } + WSMset.uRdState = RDS_SR; + return; +} + +// write to buffer data +static VOID WrStateE8D(BYTE a, DWORD d) +{ + // first data byte + if (WSMset.byWrite1No == WSMset.byWrite1Size + 1) + { + DWORD dwBlockMask = ~(((byQueryTab[0x30] << 8) | byQueryTab[0x2F]) * 256 - 1); + + // same block + if ((WSMset.dwWrite1Addr & dwBlockMask) == (d & dwBlockMask)) + { + WSMset.dwWrite1Addr = d; // byte block address of buffer1 + WSMset.pbyWrite1[0] = a; // save byte + } + else + { + WSMset.byWrite1No = 0; // free write buffer + // improper command sequence + WSMset.byStatusReg |= (ECLBS | BWSLBS); + WSMset.byStatusReg |= WSMS; // data written + WSMset.uWrState = WRS_DATA; + return; + } + } + else + { + // write address within buffer + if (d >= WSMset.dwWrite1Addr && d <= WSMset.dwWrite1Addr + WSMset.byWrite1Size) + { + // save byte in buffer + WSMset.pbyWrite1[d-WSMset.dwWrite1Addr] = a; + } + else + { + WSMset.byWrite1No = 0; // free write buffer + // improper command sequence + WSMset.byStatusReg |= (ECLBS | BWSLBS); + WSMset.byStatusReg |= WSMS; // data written + WSMset.uWrState = WRS_DATA; + return; + } + } + + if (--WSMset.byWrite1No == 0) // last byte written + WSMset.uWrState = WRS_WR_BUFFER_C; // goto confirm state + return; +} + +// write to buffer confirm +static VOID WrStateE8C(BYTE a, DWORD d) +{ + if (CONFIRM == a) // write buffer confirm? + { + BYTE byPos; + + d = WSMset.dwWrite1Addr << 1; // nibble start address + + for (byPos = 0; byPos <= WSMset.byWrite1Size; ++byPos) + { + a = WSMset.pbyWrite1[byPos]; // get char from buffer + + _ASSERT(d+1 < dwRomSize); // address valid? + // no error set in BWSLBS, because I could alway program a "0" + *(pbyRom+d++) &= (a & 0x0F); // write LSB + *(pbyRom+d++) &= (a >> 4); // write MSB + } + } + else + { + WSMset.byWrite1No = 0; // free write buffer + // improper command sequence + WSMset.byStatusReg |= (ECLBS | BWSLBS); + } + WSMset.byStatusReg |= WSMS; // data written + WSMset.uWrState = WRS_DATA; + return; +} + +// byte/word program initial command +static VOID WrState40(DWORD d) +{ + WSMset.byStatusReg &= ~WSMS; // state machine busy + WSMset.uWrState = WRS_WR_BYTE; + WSMset.uRdState = RDS_SR; + return; + UNREFERENCED_PARAMETER(d); +} + +// byte/word program data +static VOID WrState40D(BYTE a, DWORD d) +{ + d <<= 1; // nibble start address + _ASSERT(d+1 < dwRomSize); // address valid? + // no error set in BWSLBS, because I could alway program a "0" + *(pbyRom+d++) &= (a & 0x0F); // write LSB + *(pbyRom+d) &= (a >> 4); // write MSB + WSMset.byStatusReg |= WSMS; // data written + WSMset.uWrState = WRS_DATA; + return; +} + +// block erase initial command +static VOID WrState20(DWORD d) +{ + WSMset.byStatusReg &= ~WSMS; // state machine busy + WSMset.uWrState = WRS_BLOCK_ERASE; + WSMset.uRdState = RDS_SR; + return; + UNREFERENCED_PARAMETER(d); +} + +// block erase data & confirm +static VOID WrState20C(BYTE a, DWORD d) +{ + if (CONFIRM == a) // block erase confirm? + { + _ASSERT((d>>16) < ARRAYSIZEOF(WSMset.byLockCnfg)); + if (WSMset.byLockCnfg[d>>16] & 1) // lock bit of block is set + { + WSMset.byStatusReg |= ECLBS; // error in block erasure + WSMset.byStatusReg |= DPS; // lock bit detected + } + else + { + DWORD dwBlockSize = ((byQueryTab[0x30] << 8) | byQueryTab[0x2F]) * 256; + + d &= ~(dwBlockSize-1); // start of block + dwBlockSize *= 2; // block size in nibbles + _ASSERT(d+dwBlockSize <= dwRomSize); // address valid? + // write 128K nibble + FillMemory(pbyRom + (d << 1),dwBlockSize,0x0F); + } + } + else + { + // improper command sequence + WSMset.byStatusReg |= (ECLBS | BWSLBS); + } + WSMset.byStatusReg |= WSMS; // block erased + WSMset.uWrState = WRS_DATA; + return; +} + +// full chip erase initial command +static VOID WrState30(DWORD d) +{ + WSMset.byStatusReg &= ~WSMS; // state machine busy + WSMset.uWrState = WRS_CHIP_ERASE; + WSMset.uRdState = RDS_SR; + return; + UNREFERENCED_PARAMETER(d); +} + +// full chip erase confirm +static VOID WrState30C(BYTE a, DWORD d) +{ + if (CONFIRM == a) // chip erase confirm? + { + UINT i; + + WORD wNoOfBlocks = (byQueryTab[0x2E] << 8) | byQueryTab[0x2D]; + DWORD dwBlockSize = ((byQueryTab[0x30] << 8) | byQueryTab[0x2F]) * 256; + + LPBYTE pbyBlock = pbyRom; + + dwBlockSize *= 2; // block size in nibbles + + for (i = 0; i <= wNoOfBlocks; ++i) // check all blocks + { + _ASSERT((i+1)*dwBlockSize <= dwRomSize); + _ASSERT(i < ARRAYSIZEOF(WSMset.byLockCnfg)); + + // lock bit of block is set & WP# = low, locked blocks cannot be erased + if ((WSMset.byLockCnfg[i] & 1) == 0 || bWP != FALSE) + { + WSMset.byLockCnfg[i] = 0; // clear block lock bit + + // write 128K nibble + FillMemory(pbyBlock,dwBlockSize,0x0F); + } + + pbyBlock += dwBlockSize; // next block + } + } + else + { + // improper command sequence + WSMset.byStatusReg |= (ECLBS | BWSLBS); + } + WSMset.byStatusReg |= WSMS; // chip erased + WSMset.uWrState = WRS_DATA; + return; + UNREFERENCED_PARAMETER(d); +} + +// STS pin Configuration initial command +static VOID WrStateB8(DWORD d) +{ + WSMset.uWrState = WRS_STS_PIN_CONFIG; + return; + UNREFERENCED_PARAMETER(d); +} + +// STS pin Configuration data +static VOID WrStateB8D(BYTE a, DWORD d) +{ + // no emulation of STS pin Configuration + WSMset.uWrState = WRS_DATA; + return; + UNREFERENCED_PARAMETER(a); + UNREFERENCED_PARAMETER(d); +} + +// Set/Clear block Lock-Bits initial command +static VOID WrState60(DWORD d) +{ + WSMset.byStatusReg &= ~WSMS; // state machine busy + WSMset.uWrState = WRS_LOCK_BITS; + WSMset.uRdState = RDS_SR; + return; + UNREFERENCED_PARAMETER(d); +} + +// Set/Clear block Lock-Bits confirm +static VOID WrState60D(BYTE a, DWORD d) +{ + UINT i; + + switch(a) + { + case 0x01: // set block lock bit + _ASSERT((d>>16) < ARRAYSIZEOF(WSMset.byLockCnfg)); + if (bWP) // WP# = high, can change block lock status + WSMset.byLockCnfg[d>>16] = 1; // set block lock bit + else + WSMset.byStatusReg |= (BWSLBS | DPS); // device protect detect, WP# = low + break; + case CONFIRM: // clear block lock bits + if (bWP) // WP# = high, can change block lock status + { + WORD wNoOfBlocks = (byQueryTab[0x2E] << 8) | byQueryTab[0x2D]; + + for (i = 0; i <= wNoOfBlocks; ++i) // clear all lock bits + { + _ASSERT(i < ARRAYSIZEOF(WSMset.byLockCnfg)); + WSMset.byLockCnfg[i] = 0; // clear block lock bit + } + } + else + { + WSMset.byStatusReg |= (ECLBS | DPS); // device protect detect, WP# = low + } + break; + default: // improper command sequence + WSMset.byStatusReg |= (ECLBS | BWSLBS); + } + WSMset.byStatusReg |= WSMS; // block lock-bit changed + WSMset.uWrState = WRS_DATA; + return; +} + + +// +// read state functions +// + +// read array +static BYTE RdStateData(DWORD d) +{ + d <<= 1; // nibble address + _ASSERT(d+1 < dwRomSize); // address valid? + return *(pbyRom+d)|(*(pbyRom+d+1)<<4); // get byte +} + +// read identifier codes +static BYTE RdStateId(DWORD d) +{ + BYTE byData; + + d >>= 1; // A0 is not connected, ignore it + if ((d & 0x03) != 0x02) // id code request + { + d &= 0x03; // data repetition + byData = byQueryTab[d]; // get data from first 4 bytes id/query table + } + else // block lock table + { + UINT uIndex = d >> 15; // index into lock table + _ASSERT(uIndex < ARRAYSIZEOF(WSMset.byLockCnfg)); + byData = WSMset.byLockCnfg[uIndex]; // get data from block lock table + + d &= 0x1F; // data repetition + if (d >= 4) byData |= 0x02; // set bit 1 on wrong ID adresses + } + return byData; +} + +// read query +static BYTE RdStateQuery(DWORD d) +{ + BYTE byData; + + d >>= 1; // A0 is not connected, ignore it + if ((d & 0x7F) != 0x02) // query request + { + d &= 0x7F; // data repetition + + // get data from id/query table + byData = (d >= 0x40 && d < 0x50) ? 0 : byQueryTab[d&0x3F]; + } + else // block lock table + { + UINT uIndex = d >> 15; // index into lock table + _ASSERT(uIndex < ARRAYSIZEOF(WSMset.byLockCnfg)); + byData = WSMset.byLockCnfg[uIndex]; // get data from block lock table + } + return byData; +} + +// read status register +static BYTE RdStateSR(DWORD d) +{ + return WSMset.byStatusReg; + UNREFERENCED_PARAMETER(d); +} + +// read extended status register +static BYTE RdStateXSR(DWORD d) +{ + return WSMset.byExStatusReg; + UNREFERENCED_PARAMETER(d); +} + + +// +// public functions +// + +VOID FlashInit(VOID) +{ + ZeroMemory(&WSMset,sizeof(WSMset)); + strcpy(WSMset.byType,"WSM"); // Write State Machine header + WSMset.uSize = sizeof(WSMset); // size of this structure + WSMset.byVersion = WSMVER; // version of flash implementation structure + + // factory setting of locking bits + WSMset.byLockCnfg[0] = 0x01; // first 64KB block is locked + + WSMset.uWrState = WRS_DATA; + WSMset.uRdState = RDS_DATA; + + // data mode of ROM + WSMset.bRomArray = bFlashRomArray = TRUE; + return; +} + +VOID FlashRead(BYTE *a, DWORD d, UINT s) +{ + BYTE v; + + while (s) // each nibble + { + // output muliplexer + _ASSERT(WSMset.uRdState < ARRAYSIZEOF(fnRdState)); + v = fnRdState[WSMset.uRdState](d>>1); + + if ((d & 1) == 0) // even address + { + *a++ = v & 0xf; ++d; --s; + } + if (s && (d & 1)) // odd address + { + *a++ = v >> 4; ++d; --s; + } + } + return; +} + +VOID FlashWrite(BYTE *a, DWORD d, UINT s) +{ + BYTE v; + DWORD p; + + while (s) // each nibble + { + p = d >> 1; // byte address + if (s > 1 && (d & 1) == 0) // more than one byte on even address + { + v = *a++; // LSB + v |= *a++ << 4; // MSB + d += 2; s -= 2; + } + else + { + // get byte from output muliplexer + _ASSERT(WSMset.uRdState < ARRAYSIZEOF(fnRdState)); + v = fnRdState[WSMset.uRdState](p); + + if (d & 1) // odd address + v = (v & 0x0F) | (*a << 4); // replace MSB + else // even address + v = (v & 0xF0) | *a; // replace LSB + ++a; ++d; --s; + } + + _ASSERT(WSMset.uWrState < ARRAYSIZEOF(fnWrState)); + fnWrState[WSMset.uWrState](v,p); // WSM + } + return; +} diff --git a/SOURCE/I28F160.H b/SOURCE/I28F160.H new file mode 100644 index 0000000..57f8b2f --- /dev/null +++ b/SOURCE/I28F160.H @@ -0,0 +1,41 @@ +/* + * i28f160.h + * + * This file is part of Emu48 + * + * Copyright (C) 2000 Christoph Gießelink + * + */ + +#define WSMVER 0 // version of flash implementation structure + +#define WSMSET WSMset_t +typedef struct +{ + BYTE byType[4]; // "WSM" + UINT uSize; // size of this structure + BYTE byVersion; // WSM version + + BOOL bRomArray; // copy of bFlashRomArray + BYTE byLockCnfg[32]; // block lock table + UINT uWrState; // state of write function WSM + UINT uRdState; // state of read function WSM + BYTE byStatusReg; // status register + BYTE byExStatusReg; // extended status register + BYTE byWrite1No; // no. of written data in write buffer1 + BYTE byWrite1Size; // no. of valid data in write buffer1 + DWORD dwWrite1Addr; // destination address of buffer1 + BYTE pbyWrite1[32]; // write buffer1 +// BYTE byWrite2No; // no. of written data in write buffer2 +// BYTE byWrite2Size; // no. of valid data in write buffer2 +// DWORD dwWrite2Addr; // destination address of buffer2 +// BYTE pbyWrite2[32]; // write buffer2 +} WSMset_t; + +// i28f160.h +extern WSMSET WSMset; +extern BOOL bWP; +extern VOID FlashInit(VOID); +extern VOID FlashRead(BYTE *a, DWORD d, UINT s); +extern VOID FlashWrite(BYTE *a, DWORD d, UINT s); +extern DWORD FlashROMAddr(DWORD d); // CdB for HP: add apples diff --git a/SOURCE/IO.H b/SOURCE/IO.H new file mode 100644 index 0000000..c9378bb --- /dev/null +++ b/SOURCE/IO.H @@ -0,0 +1,150 @@ +/* + * io.h + * + * This file is part of Emu48 + * + * Copyright (C) 1999 Christoph Gießelink + * + */ + +// I/O addresses without mapping offset +#define BITOFFSET 0x00 // Display bit offset and DON +#define CRC 0x04 // Crc (16 bit, LSB first) +#define LPD 0x08 // Low Power Detection +#define LPE 0x09 // Low Power detection Enable +#define ANNCTRL 0x0b // Annunciator Control (2 nibble) +#define BAUD 0x0d // Baudrate (Bit 2-0) +#define CARDCTL 0x0e // card control +#define CARDSTAT 0x0f // card status +#define IOC 0x10 // IO CONTROL +#define RCS 0x11 // RCS +#define TCS 0x12 // TCS +#define CRER 0x13 // CRER +#define RBR_LSB 0x14 // RBR low nibble +#define RBR_MSB 0x15 // RBR high nibble +#define TBR_LSB 0x16 // TBR low nibble +#define TBR_MSB 0x17 // TBR high nibble +#define SRQ1 0x18 // SRQ1 +#define SRQ2 0x19 // SRQ2 +#define IR_CTRL 0x1a // IR CONTROL +#define LCR 0x1c // Led Control Register +#define DISP1CTL 0x20 // Display Start Address +#define LINENIBS 0x25 // Display Line Offset +#define LINECOUNT 0x28 // Display Line Counter +#define TIMER1_CTRL 0x2e // Timer1 Control +#define TIMER2_CTRL 0x2f // Timer2 Control +#define DISP2CTL 0x30 // Display Secondary Start Address +#define TIMER1 0x37 // Timer1 (4 bit) +#define TIMER2 0x38 // Timer2 (32 bit, LSB first) + +// 0x00 Display bit offset and DON [DON OFF2 OFF1 OFF0] +#define DON 0x08 // Display On +#define OFF2 0x04 // Display OFFset Bit2 +#define OFF1 0x02 // Display OFFset Bit1 +#define OFF0 0x01 // Display OFFset Bit0 + +// 0x08 Low Power Detection [LB2 LB1 LB0 VLBI] +#define LB2 0x08 // Low Battery indicator memory port 2 +#define LB1 0x04 // Low Battery indicator memory port 1 +#define LB0 0x02 // Low Battery indicator system battery +#define VLBI 0x01 // Very Low Battery Indicator + +// 0x09 Low Power detection Enable [ELBI EVLBI GRAM RST] +#define ELBI 0x08 // Enable Low Battery Indicator +#define EVLBI 0x04 // Enable Very Low Battery Indicator +#define GRAM 0x02 // Glitch sensitive RAM +#define RST 0x01 // ReSeT + +// 0x0b Annunciator Control [AON XTRA LA6 LA5] [LA4 LA3 LA2 LA1] +#define AON 0x80 // Annunciators on +#define LXTRA 0x40 // does nothing +#define LA6 0x20 // LA6 - Transmitting +#define LA5 0x10 // LA5 - Busy +#define LA4 0x08 // LA4 - Alert +#define LA3 0x04 // LA3 - Alpha +#define LA2 0x02 // LA2 - ALT Shift +#define LA1 0x01 // LA1 - Shift + +// 0x0d SERIAL Baud Rate [UCK BD2 BD1 BD0] +#define UCK 0x08 // Uart ClocK +#define BD2 0x04 // BauDrate Bit2 +#define BD1 0x02 // BauDrate Bit1 +#define BD0 0x01 // BauDrate Bit0 + +// 0x0e Card Control [ECDT RCDT SMP SWINT] +#define ECDT 0x08 // Enable Card Detect +#define RCDT 0x04 // Run Card Detect +#define SMP 0x02 // Set module pulled +#define SWINT 0x01 // Software Interrupt + +// 0x0f Card Status [P2W P1W P2C P1C] +#define P2W 0x08 // High when Port2 writeable +#define P1W 0x04 // High when Port1 writeable +#define P2C 0x02 // High when Card in Port2 inserted +#define P1C 0x01 // High when Card in Port1 inserted + +// 0x10 Serial I/O Control [SON ETBE ERBF ERBZ] +#define SON 0x08 // Serial on +#define ETBE 0x04 // Interrupt on transmit buffer empty +#define ERBF 0x02 // Interrupt on receive buffer full +#define ERBZ 0x01 // Interrupt on receiver busy + +// 0x11 Serial Receive Control/Status [RX RER RBZ RBF] +#define RX 0x08 // Rx pin state (read-only) +#define RER 0x04 // Receiver error +#define RBZ 0x02 // Receiver busy +#define RBF 0x01 // Receive buffer full + +// 0x12 Serial Transmit Control/Status [BRK LPB TBZ TBF] +#define BRK 0x08 // Break +#define LPB 0x04 // Loopback +#define TBZ 0x02 // Transmitter busy +#define TBF 0x01 // Transmit buffer full + +// 0x18 Service Request Register 1 [ISRQ TSRQ USRQ VSRQ] +#define ISRQ 0x08 // IR receiver pulls NINT2 +#define TSRQ 0x04 // Timer pulls NINT2 +#define USRQ 0x02 // UART pulls NINT2 +#define VSRQ 0x01 // VLBI pulls NINT2 + +// 0x19 Service Request Register 2 [KDN NINT2 NINT LSRQ] +#define KDN 0x08 // Bit set when ON Key or Key Interrupt +#define NINT2 0x04 // State of NINT2 +#define NINT 0x02 // State of NINT +#define LSRQ 0x01 // LED driver pulls NINT2 + +// 0x1a IR Control Register [IRI EIRU EIRI IRE] +#define IRI 0x08 // IR input (read-only) +#define EIRU 0x04 // Enable IR UART mode +#define EIRI 0x02 // Enable IR interrupt +#define IRE 0x01 // IR event + +// 0x1c Led Control Register [LED ELBE LBZ LBF] +#define LED 0x08 // Turn on LED +#define ELBE 0x04 // Enable Interrupt on Led Buffer empty +#define LBZ 0x02 // Led Port Busy +#define LBF 0x01 // Led Buffer Full + +// 0x28 Display Line Counter LSB [LC3 LC2 LC1 LC0] +#define LC3 0x08 // LC3 - Line Counter Bit3 +#define LC2 0x04 // LC2 - Line Counter Bit2 +#define LC1 0x02 // LC1 - Line Counter Bit1 +#define LC0 0x01 // LC0 - Line Counter Bit0 + +// 0x29 Display Line Counter MSB [DA19 M32 LC5 LC4] +#define DA19 0x08 // Drive A[19] +#define M32 0x04 // Multiplex 32 way +#define LC5 0x02 // LC5 - Line Counter Bit5 +#define LC4 0x01 // LC4 - Line Counter Bit4 + +// 0x2e Timer1 Control [SRQ WKE INT XTRA] +#define SRQ 0x08 // Service request +#define WKE 0x04 // Wake up +#define INTR 0x02 // Interrupt +#define XTRA 0x01 // Extra function + +// 0x2f Timer2 Control [SRQ WKE INT RUN] +#define SRQ 0x08 // Service request +#define WKE 0x04 // Wake up +#define INTR 0x02 // Interrupt +#define RUN 0x01 // Timer run diff --git a/SOURCE/KEYBOARD.C b/SOURCE/KEYBOARD.C new file mode 100644 index 0000000..53d3dab --- /dev/null +++ b/SOURCE/KEYBOARD.C @@ -0,0 +1,129 @@ +/* + * keyboard.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "Emu48.h" +#include "io.h" // I/O definitions + +static WORD Keyboard_GetIR(VOID) +{ + WORD r = 0; + + // OR[0:8] are wired on Clarke/Yorke chip + if (Chipset.out==0) return 0; + if (Chipset.out&0x001) r|=Chipset.Keyboard_Row[0]; + if (Chipset.out&0x002) r|=Chipset.Keyboard_Row[1]; + if (Chipset.out&0x004) r|=Chipset.Keyboard_Row[2]; + if (Chipset.out&0x008) r|=Chipset.Keyboard_Row[3]; + if (Chipset.out&0x010) r|=Chipset.Keyboard_Row[4]; + if (Chipset.out&0x020) r|=Chipset.Keyboard_Row[5]; + if (Chipset.out&0x040) r|=Chipset.Keyboard_Row[6]; + if (Chipset.out&0x080) r|=Chipset.Keyboard_Row[7]; + if (Chipset.out&0x100) r|=Chipset.Keyboard_Row[8]; + return r; +} + +VOID ScanKeyboard(BOOL bActive, BOOL bReset) +{ + // bActive = TRUE -> function called by direct read (A=IN, C=IN, RSI) + // FALSE -> function called by 1ms keyboard poll simulation + // bReset = TRUE -> Reset Chipset.in interrupt state register + // FALSE -> generate interrupt only for new pressed keys + + // keyboard read not active? + if (!( bActive || Chipset.Shutdn || Chipset.IR15X + || (Chipset.intk && (Chipset.IORam[TIMER2_CTRL]&RUN) != 0))) + { + EnterCriticalSection(&csKeyLock); + { + Chipset.in &= ~0x8000; // remove ON key + } + LeaveCriticalSection(&csKeyLock); + return; + } + + EnterCriticalSection(&csKeyLock); // synchronize + { + BOOL bKbdInt; + + WORD wOldIn = Chipset.in; // save old Chipset.in state + + UpdateKdnBit(); // update KDN bit + Chipset.dwKdnCycles = (DWORD) (Chipset.cycles & 0xFFFFFFFF); + + Chipset.in = Keyboard_GetIR(); // update Chipset.in register + Chipset.in |= Chipset.IR15X; // add ON key + + // interrupt for any new pressed keys? + bKbdInt = (Chipset.in && (wOldIn & 0x1FF) == 0) || Chipset.IR15X || bReset; + + // update keyboard interrupt pending flag when 1ms keyboard scan is disabled + Chipset.intd = Chipset.intd || (bKbdInt && !Chipset.intk); + + // keyboard interrupt enabled? + bKbdInt = bKbdInt && Chipset.intk; + + // interrupt at ON key pressed + bKbdInt = bKbdInt || Chipset.IR15X != 0; + + // no interrupt if still inside interrupt service routine + bKbdInt = bKbdInt && Chipset.inte; + + if (Chipset.in != 0) // any key pressed + { + if (bKbdInt) // interrupt enabled + { + Chipset.SoftInt = TRUE; // interrupt request + bInterrupt = TRUE; // exit emulation loop + } + + if (Chipset.Shutdn) // cpu sleeping + { + Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode + SetEvent(hEventShutdn); // wake up emulation thread + } + } + else + { + Chipset.intd = FALSE; // no keyboard interrupt pending + } + } + LeaveCriticalSection(&csKeyLock); + + return; +} + +VOID KeyboardEvent(BOOL bPress, UINT out, UINT in) +{ + if (nState != SM_RUN) // not in running state + return; // ignore key + + KeyMacroRecord(bPress,out,in); // save all keyboard events + + if (in == 0x8000) // ON key ? + { + Chipset.IR15X = bPress?0x8000:0x0000; // refresh special ON key flag + } + else + { + // "out" is outside Keyboard_Row + if (out >= ARRAYSIZEOF(Chipset.Keyboard_Row)) return; + + // in &= 0x1FF; // only IR[0:8] are wired on Clarke/Yorke chip + + _ASSERT(out < ARRAYSIZEOF(Chipset.Keyboard_Row)); + if (bPress) // key pressed + Chipset.Keyboard_Row[out] |= in; // set key marker in keyboard row + else + Chipset.Keyboard_Row[out] &= (~in); // clear key marker in keyboard row + } + AdjKeySpeed(); // adjust key repeat speed + ScanKeyboard(FALSE,FALSE); // update Chipset.in register by 1ms keyboard poll + Sleep(50); // hold key state for a definite time + return; +} diff --git a/SOURCE/KEYMACRO.C b/SOURCE/KEYMACRO.C new file mode 100644 index 0000000..b3f08ac --- /dev/null +++ b/SOURCE/KEYMACRO.C @@ -0,0 +1,339 @@ +/* + * Keymacro.c + * + * This file is part of Emu48 + * + * Copyright (C) 2004 Christoph Gießelink + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "kml.h" + +#define KEYMACROHEAD "Emu-KeyMacro" // macro signature + +#define KEYHOLDTIME 50 + +#define MIN_SPEED 0 +#define MAX_SPEED 500 + +typedef struct +{ + DWORD dwTime; // elapsed time + DWORD dwKeyEvent; // key code +} KeyData; + +INT nMacroState = MACRO_OFF; +INT nMacroTimeout = MIN_SPEED; +BOOL bMacroRealSpeed = TRUE; + +static DWORD dwTimeRef; + +static HANDLE hMacroFile = INVALID_HANDLE_VALUE; +static HANDLE hEventPlay = NULL; +static HANDLE hThreadEv = NULL; + +static VOID InitializeOFN(LPOPENFILENAME ofn) +{ + ZeroMemory((LPVOID)ofn, sizeof(OPENFILENAME)); + ofn->lStructSize = sizeof(OPENFILENAME); + ofn->hwndOwner = hWnd; + ofn->Flags = OFN_EXPLORER|OFN_HIDEREADONLY; + return; +} + +// +// thread playing keys +// +static DWORD WINAPI EventThread(LPVOID pParam) +{ + DWORD dwRead = 0; + DWORD dwData,dwTime = 0; + + while (WaitForSingleObject(hEventPlay,dwTime) == WAIT_TIMEOUT) + { + if (dwRead != 0) // data read + { + UINT nIn = (dwData >> 0) & 0xFFFF; + UINT nOut = (dwData >> 16) & 0xFF; + BOOL bPress = (dwData >> 24) & 0xFF; + + PlayKey(nOut,nIn,bPress); +// KeyboardEvent(bPress,nOut,nIn); + } + + dwTime = nMacroTimeout; // set default speed + + while (TRUE) + { + // read next data element + if ( !ReadFile(hMacroFile,&dwData,sizeof(dwData),&dwRead,NULL) + || dwRead != sizeof(dwData)) + { + // play record empty -> quit + PostMessage(hWnd,WM_COMMAND,ID_TOOL_MACRO_STOP,0); + return 0; // exit on file end + } + + if ((dwData & 0x80000000) != 0) // time information + { + if (bMacroRealSpeed) // realspeed from data + { + dwTime = dwData & 0x7FFFFFFF; + } + continue; + } + break; // got key information + } + } + return 0; // exit on stop + UNREFERENCED_PARAMETER(pParam); +} + +// +// callback function for recording keys +// +VOID KeyMacroRecord(BOOL bPress, UINT out, UINT in) +{ + if (nMacroState == MACRO_NEW) // save key event + { + KeyData Data; + DWORD dwWritten; + + dwWritten = GetTickCount(); // time reference + Data.dwTime = (dwWritten - dwTimeRef - KEYHOLDTIME); + // set negative number to zero + if ((Data.dwTime & 0x80000000) != 0) Data.dwTime = 0; + Data.dwTime |= 0x80000000; // set time marker + dwTimeRef = dwWritten; + + Data.dwKeyEvent = (bPress & 0xFF); + Data.dwKeyEvent = (Data.dwKeyEvent << 8) | (out & 0xFF); + Data.dwKeyEvent = (Data.dwKeyEvent << 16) | (in & 0xFFFF); + + // save key event in file + WriteFile(hMacroFile,&Data,sizeof(Data),&dwWritten,NULL); + _ASSERT(dwWritten == sizeof(Data)); + } + return; +} + +// +// message handler for save new keyboard macro +// +LRESULT OnToolMacroNew(VOID) +{ + TCHAR szMacroFile[MAX_PATH]; + OPENFILENAME ofn; + DWORD dwExtensionLength,dwWritten; + + // get filename for saving + InitializeOFN(&ofn); + ofn.lpstrFilter = + _T("Keyboard Macro Files (*.MAC)\0*.MAC\0") + _T("All Files (*.*)\0*.*\0") + _T("\0\0"); + ofn.lpstrDefExt = _T("MAC"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = szMacroFile; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szMacroFile); + ofn.Flags |= OFN_CREATEPROMPT|OFN_OVERWRITEPROMPT; + if (GetSaveFileName(&ofn) == FALSE) return 0; + + // open file for writing + hMacroFile = CreateFile(szMacroFile, + GENERIC_READ|GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hMacroFile == INVALID_HANDLE_VALUE) return 0; + + // write header + WriteFile(hMacroFile,KEYMACROHEAD,sizeof(KEYMACROHEAD) - 1,&dwWritten,NULL); + _ASSERT(dwWritten == (DWORD) strlen(KEYMACROHEAD)); + + // write extension length + dwExtensionLength = 0; // no extension + WriteFile(hMacroFile,&dwExtensionLength,sizeof(dwExtensionLength),&dwWritten,NULL); + _ASSERT(dwWritten == sizeof(dwExtensionLength)); + + nMacroState = MACRO_NEW; + UpdateWindowStatus(); + + MessageBox(hWnd, + _T("Press OK to begin to record the Macro."), + _T("Macro Recorder"), + MB_OK|MB_ICONINFORMATION); + + dwTimeRef = GetTickCount() + KEYHOLDTIME; // time reference + return 0; +} + +// +// message handler for play keyboard macro +// +LRESULT OnToolMacroPlay(VOID) +{ + BYTE byHeader[sizeof(KEYMACROHEAD)-1]; + TCHAR szMacroFile[MAX_PATH]; + OPENFILENAME ofn; + DWORD dwExtensionLength,dwRead,dwThreadId; + + InitializeOFN(&ofn); + ofn.lpstrFilter = + _T("Keyboard Macro Files (*.MAC)\0*.MAC\0") + _T("All Files (*.*)\0*.*\0") + _T("\0\0"); + ofn.lpstrDefExt = _T("MAC"); + ofn.nFilterIndex = 1; + ofn.lpstrFile = szMacroFile; + ofn.lpstrFile[0] = 0; + ofn.nMaxFile = ARRAYSIZEOF(szMacroFile); + ofn.Flags |= OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST; + if (GetOpenFileName(&ofn) == FALSE) return 0; + + // open file for Reading + hMacroFile = CreateFile(szMacroFile, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hMacroFile == INVALID_HANDLE_VALUE) return 0; + + // read header + ReadFile(hMacroFile,byHeader,sizeof(byHeader),&dwRead,NULL); + if ( dwRead != sizeof(byHeader) + || memcmp(byHeader,KEYMACROHEAD,dwRead) != 0) + { + MessageBox(hWnd, + _T("Wrong keyboard macro file format."), + _T("Macro Recorder"), + MB_OK|MB_ICONSTOP); + CloseHandle(hMacroFile); + return 0; + } + + // read extension length + ReadFile(hMacroFile,&dwExtensionLength,sizeof(dwExtensionLength),&dwRead,NULL); + if (dwRead != sizeof(dwExtensionLength)) + { + CloseHandle(hMacroFile); + return 0; + } + + // read extension + while (dwExtensionLength-- > 0) + { + BYTE byData; + + ReadFile(hMacroFile,&byData,sizeof(byData),&dwRead,NULL); + if (dwRead != sizeof(byData)) + { + CloseHandle(hMacroFile); + return 0; + } + } + + // event for quit playing + hEventPlay = CreateEvent(NULL,FALSE,FALSE,NULL); + + nMacroState = MACRO_PLAY; + UpdateWindowStatus(); + + // start playing thread + VERIFY(hThreadEv = CreateThread(NULL,0,&EventThread,NULL,0,&dwThreadId)); + return 0; +} + +// +// message handler for stop recording/playing +// +LRESULT OnToolMacroStop(VOID) +{ + if (nMacroState != MACRO_OFF) + { + if (hEventPlay) // playing keys + { + // stop playing thread + SetEvent(hEventPlay); // quit play loop + + WaitForSingleObject(hThreadEv,INFINITE); + CloseHandle(hThreadEv); + hThreadEv = NULL; + + CloseHandle(hEventPlay); // close playing keys event + hEventPlay = NULL; + } + + // macro file open + if (hMacroFile != INVALID_HANDLE_VALUE) CloseHandle(hMacroFile); + + nMacroState = MACRO_OFF; + UpdateWindowStatus(); + } + return 0; +} + +// +// activate/deactivate slider +// +static VOID SliderEnable(HWND hDlg,BOOL bEnable) +{ + EnableWindow(GetDlgItem(hDlg,IDC_MACRO_SLOW),bEnable); + EnableWindow(GetDlgItem(hDlg,IDC_MACRO_FAST),bEnable); + EnableWindow(GetDlgItem(hDlg,IDC_MACRO_SLIDER),bEnable); + return; +} + +// +// Macro settings dialog +// +static INT_PTR CALLBACK MacroProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_INITDIALOG: + // set slider + SendDlgItemMessage(hDlg,IDC_MACRO_SLIDER,TBM_SETRANGE,FALSE,MAKELONG(0,MAX_SPEED-MIN_SPEED)); + SendDlgItemMessage(hDlg,IDC_MACRO_SLIDER,TBM_SETTICFREQ,MAX_SPEED/10,0); + SendDlgItemMessage(hDlg,IDC_MACRO_SLIDER,TBM_SETPOS,TRUE,MAX_SPEED-nMacroTimeout); + + // set button + CheckDlgButton(hDlg,bMacroRealSpeed ? IDC_MACRO_REAL : IDC_MACRO_MANUAL,BST_CHECKED); + SliderEnable(hDlg,!bMacroRealSpeed); + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_MACRO_REAL: + SliderEnable(hDlg,FALSE); + return TRUE; + case IDC_MACRO_MANUAL: + SliderEnable(hDlg,TRUE); + return TRUE; + case IDOK: + // get macro data + nMacroTimeout = MAX_SPEED - SendDlgItemMessage(hDlg,IDC_MACRO_SLIDER,TBM_GETPOS,0,0); + bMacroRealSpeed = IsDlgButtonChecked(hDlg,IDC_MACRO_REAL); + // no break + case IDCANCEL: + EndDialog(hDlg, LOWORD(wParam)); + } + break; + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +LRESULT OnToolMacroSettings(VOID) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_MACROSET), hWnd, (DLGPROC)MacroProc) == -1) + AbortMessage(_T("Macro Dialog Box Creation Error !")); + return 0; +} diff --git a/SOURCE/KML.C b/SOURCE/KML.C new file mode 100644 index 0000000..3e3779a --- /dev/null +++ b/SOURCE/KML.C @@ -0,0 +1,2269 @@ +/* + * kml.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "kml.h" + +static VOID FatalError(VOID); +static VOID InitLex(LPTSTR szScript); +static VOID CleanLex(VOID); +static VOID SkipWhite(UINT nMode); +static TokenId ParseToken(UINT nMode); +static DWORD ParseInteger(VOID); +static LPTSTR ParseString(VOID); +static TokenId Lex(UINT nMode); +static KmlLine* ParseLine(TokenId eCommand); +static KmlLine* IncludeLines(LPCTSTR szFilename); +static KmlLine* ParseLines(VOID); +static KmlBlock* ParseBlock(TokenId eBlock); +static KmlBlock* IncludeBlocks(LPCTSTR szFilename); +static KmlBlock* ParseBlocks(VOID); +static VOID FreeLines(KmlLine* pLine); +static VOID PressButton(UINT nId); +static VOID ReleaseButton(UINT nId); +static VOID PressButtonById(UINT nId); +static VOID ReleaseButtonById(UINT nId); +static LPTSTR GetStringParam(KmlBlock* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam); +static DWORD GetIntegerParam(KmlBlock* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam); +static KmlLine* SkipLines(KmlLine* pLine, TokenId eCommand); +static KmlLine* If(KmlLine* pLine, BOOL bCondition); +static KmlLine* RunLine(KmlLine* pLine); +static KmlBlock* LoadKMLGlobal(LPCTSTR szFilename); + +KmlBlock* pKml = NULL; +static KmlBlock* pVKey[256]; +static BYTE byVKeyMap[256]; +static KmlButton pButton[256]; +static KmlAnnunciator pAnnunciator[6]; +static UINT nButtons = 0; +static UINT nScancodes = 0; +static UINT nAnnunciators = 0; +static BOOL bDebug = TRUE; +static UINT nLexLine; +static UINT nLexInteger; +static UINT nBlocksIncludeLevel; +static UINT nLinesIncludeLevel; +static DWORD nKMLFlags = 0; +static LPTSTR szLexString; +static LPTSTR szText; +static LPCTSTR szLexDelim[] = +{ + _T(" \t\n\r"), // valid whitespaces for LEX_BLOCK + _T(" \t\n\r"), // valid whitespaces for LEX_COMMAND + _T(" \t\r") // valid whitespaces for LEX_PARAM +}; + +static KmlToken pLexToken[] = +{ + {TOK_ANNUNCIATOR,000001,11,_T("Annunciator")}, + {TOK_BACKGROUND, 000000,10,_T("Background")}, + {TOK_IFPRESSED, 000001, 9,_T("IfPressed")}, + {TOK_RESETFLAG, 000001, 9,_T("ResetFlag")}, + {TOK_SCANCODE, 000001, 8,_T("Scancode")}, + {TOK_HARDWARE, 000002, 8,_T("Hardware")}, + {TOK_MENUITEM, 000001, 8,_T("MenuItem")}, + {TOK_SETFLAG, 000001, 7,_T("SetFlag")}, + {TOK_RELEASE, 000001, 7,_T("Release")}, + {TOK_VIRTUAL, 000000, 7,_T("Virtual")}, + {TOK_INCLUDE, 000002, 7,_T("Include")}, + {TOK_NOTFLAG, 000001, 7,_T("NotFlag")}, + {TOK_GLOBAL, 000000, 6,_T("Global")}, + {TOK_AUTHOR, 000002, 6,_T("Author")}, + {TOK_BITMAP, 000002, 6,_T("Bitmap")}, + {TOK_OFFSET, 000011, 6,_T("Offset")}, + {TOK_BUTTON, 000001, 6,_T("Button")}, + {TOK_IFFLAG, 000001, 6,_T("IfFlag")}, + {TOK_ONDOWN, 000000, 6,_T("OnDown")}, + {TOK_NOHOLD, 000000, 6,_T("NoHold")}, + {TOK_TITLE, 000002, 5,_T("Title")}, + {TOK_OUTIN, 000011, 5,_T("OutIn")}, + {TOK_PATCH, 000002, 5,_T("Patch")}, + {TOK_PRINT, 000002, 5,_T("Print")}, + {TOK_DEBUG, 000001, 5,_T("Debug")}, + {TOK_COLOR, 001111, 5,_T("Color")}, + {TOK_MODEL, 000002, 5,_T("Model")}, + {TOK_CLASS, 000001, 5,_T("Class")}, + {TOK_PRESS, 000001, 5,_T("Press")}, + {TOK_TYPE, 000001, 4,_T("Type")}, + {TOK_SIZE, 000011, 4,_T("Size")}, + {TOK_ZOOM, 000001, 4,_T("Zoom")}, + {TOK_DOWN, 000011, 4,_T("Down")}, + {TOK_ELSE, 000000, 4,_T("Else")}, + {TOK_ONUP, 000000, 4,_T("OnUp")}, + {TOK_MAP, 000011, 3,_T("Map")}, + {TOK_ROM, 000002, 3,_T("Rom")}, + {TOK_LCD, 000000, 3,_T("Lcd")}, + {TOK_END, 000000, 3,_T("End")}, + {0, 000000, 0,_T("")}, +}; + +static CONST TokenId eIsGlobalBlock[] = +{ + TOK_GLOBAL, + TOK_BACKGROUND, + TOK_LCD, + TOK_ANNUNCIATOR, + TOK_BUTTON, + TOK_SCANCODE +}; + +static CONST TokenId eIsBlock[] = +{ + TOK_IFFLAG, + TOK_IFPRESSED, + TOK_ONDOWN, + TOK_ONUP +}; + +static BOOL bClicking = FALSE; +static UINT uButtonClicked = 0; + +static BOOL bPressed = FALSE; // no key pressed +static UINT uLastPressedKey = 0; // var for last pressed key + +//################ +//# +//# Compilation Result +//# +//################ + +static UINT nLogLength = 0; +static LPTSTR szLog = NULL; + +static VOID ClearLog() +{ + nLogLength = 0; + if (szLog != NULL) + { + HeapFree(hHeap,0,szLog); + szLog = NULL; + } + return; +} + +static VOID AddToLog(LPCTSTR szString) +{ + UINT nLength = lstrlen(szString) + 2; // CR+LF + if (szLog == NULL) + { + nLogLength = nLength + 1; // \0 + szLog = HeapAlloc(hHeap,0,nLogLength*sizeof(szLog[0])); + if (szLog==NULL) + { + nLogLength = 0; + return; + } + lstrcpy(szLog,szString); + } + else + { + LPTSTR szLogTmp = HeapReAlloc(hHeap,0,szLog,(nLogLength+nLength)*sizeof(szLog[0])); + if (szLogTmp == NULL) + { + ClearLog(); + return; + } + szLog = szLogTmp; + lstrcpy(&szLog[nLogLength-1],szString); + nLogLength += nLength; + } + szLog[nLogLength-3] = _T('\r'); + szLog[nLogLength-2] = _T('\n'); + szLog[nLogLength-1] = 0; + return; +} + +static VOID __cdecl PrintfToLog(LPCTSTR lpFormat, ...) +{ + TCHAR cOutput[1024]; + va_list arglist; + + va_start(arglist,lpFormat); + wvsprintf(cOutput,lpFormat,arglist); + AddToLog(cOutput); + va_end(arglist); + return; +} + +static INT_PTR CALLBACK KMLLogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + LPCTSTR szString; + + switch (message) + { + case WM_INITDIALOG: + // set OK + EnableWindow(GetDlgItem(hDlg,IDOK),(BOOL) lParam); + // set IDC_TITLE + szString = GetStringParam(pKml, TOK_GLOBAL, TOK_TITLE, 0); + if (szString == NULL) szString = _T("Untitled"); + SetDlgItemText(hDlg,IDC_TITLE,szString); + // set IDC_AUTHOR + szString = GetStringParam(pKml, TOK_GLOBAL, TOK_AUTHOR, 0); + if (szString == NULL) szString = _T(""); + SetDlgItemText(hDlg,IDC_AUTHOR,szString); + // set IDC_KMLLOG + szString = szLog; + if (szString == NULL) szString = _T("Memory Allocation Failure."); + SetDlgItemText(hDlg,IDC_KMLLOG,szString); + // set IDC_ALWAYSDISPLOG + CheckDlgButton(hDlg,IDC_ALWAYSDISPLOG,bAlwaysDisplayLog); + return TRUE; + case WM_COMMAND: + wParam = LOWORD(wParam); + if ((wParam==IDOK)||(wParam==IDCANCEL)) + { + bAlwaysDisplayLog = IsDlgButtonChecked(hDlg, IDC_ALWAYSDISPLOG); + EndDialog(hDlg, wParam); + return TRUE; + } + break; + } + return FALSE; +} + +BOOL DisplayKMLLog(BOOL bOkEnabled) +{ + return IDOK == DialogBoxParam(hApp, + MAKEINTRESOURCE(IDD_KMLLOG), + hWnd, + (DLGPROC)KMLLogProc, + bOkEnabled); +} + + + +//################ +//# +//# Choose Script +//# +//################ + +typedef struct _KmlScript +{ + LPTSTR szFilename; + LPTSTR szTitle; + DWORD nId; + struct _KmlScript* pNext; +} KmlScript; + +static KmlScript* pKmlList = NULL; +static CHAR cKmlType; + +static VOID DestroyKmlList(VOID) +{ + KmlScript* pList; + + while (pKmlList) + { + pList = pKmlList->pNext; + HeapFree(hHeap,0,pKmlList->szFilename); + HeapFree(hHeap,0,pKmlList->szTitle); + HeapFree(hHeap,0,pKmlList); + pKmlList = pList; + } + return; +} + +static VOID CreateKmlList(VOID) +{ + HANDLE hFindFile; + WIN32_FIND_DATA pFindFileData; + UINT nKmlFiles; + + _ASSERT(pKmlList == NULL); // KML file list must be empty + SetCurrentDirectory(szEmuDirectory); + hFindFile = FindFirstFile(_T("*.KML"),&pFindFileData); + SetCurrentDirectory(szCurrentDirectory); + if (hFindFile == INVALID_HANDLE_VALUE) return; + nKmlFiles = 0; + do + { + KmlScript* pScript; + KmlBlock* pBlock; + LPTSTR szTitle; + + pBlock = LoadKMLGlobal(pFindFileData.cFileName); + if (pBlock == NULL) continue; + // check for correct KML script platform + szTitle = GetStringParam(pBlock,TOK_GLOBAL,TOK_HARDWARE,0); + if (szTitle && lstrcmpi(_T(HARDWARE),szTitle) != 0) + { + FreeBlocks(pBlock); + continue; + } + // check for supported Model + szTitle = GetStringParam(pBlock,TOK_GLOBAL,TOK_MODEL,0); + // skip all scripts with invalid or different Model statement + if ( (szTitle == NULL) + || (cKmlType && szTitle[0] != cKmlType) + || !isModelValid(szTitle[0])) + { + FreeBlocks(pBlock); + continue; + } + VERIFY(pScript = HeapAlloc(hHeap,0,sizeof(KmlScript))); + pScript->szFilename = DuplicateString(pFindFileData.cFileName); + szTitle = GetStringParam(pBlock,TOK_GLOBAL,TOK_TITLE,0); + if (szTitle == NULL) szTitle = pScript->szFilename; + pScript->szTitle = DuplicateString(szTitle); + FreeBlocks(pBlock); + pScript->nId = nKmlFiles; + pScript->pNext = pKmlList; + pKmlList = pScript; + nKmlFiles++; + } while (FindNextFile(hFindFile,&pFindFileData)); + FindClose(hFindFile); + return; +}; + +static INT CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lp, LPARAM pData) +{ + TCHAR szDir[MAX_PATH]; + + switch(uMsg) + { + case BFFM_INITIALIZED: + SendMessage(hwnd,BFFM_SETSELECTION,TRUE,pData); + break; + case BFFM_SELCHANGED: + // Set the status window to the currently selected path. + if (SHGetPathFromIDList((LPITEMIDLIST) lp,szDir)) + { + SendMessage(hwnd,BFFM_SETSTATUSTEXT,0,(LPARAM) szDir); + } + break; + } + return 0; +} + +static VOID BrowseFolder(HWND hDlg) +{ + TCHAR szDir[MAX_PATH]; + BROWSEINFO bi; + LPITEMIDLIST pidl; + LPMALLOC pMalloc; + + // gets the shell's default allocator + if (SUCCEEDED(SHGetMalloc(&pMalloc))) + { + GetDlgItemText(hDlg,IDC_EMUDIR,szDir,ARRAYSIZEOF(szDir)); + + ZeroMemory(&bi,sizeof(bi)); + bi.hwndOwner = hDlg; + bi.pidlRoot = NULL; + bi.pszDisplayName = NULL; + bi.lpszTitle = _T("Choose a folder:"); + bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT; + bi.lpfn = BrowseCallbackProc; + bi.lParam = (LPARAM) szDir; // current setting + + pidl = SHBrowseForFolder(&bi); + if (pidl) + { + if (SHGetPathFromIDList(pidl,szDir)) + { + SetDlgItemText(hDlg,IDC_EMUDIR,szDir); + } + // free the PIDL allocated by SHBrowseForFolder + pMalloc->lpVtbl->Free(pMalloc,pidl); + } + // release the shell's allocator + pMalloc->lpVtbl->Release(pMalloc); + } + return; +} + +static INT_PTR CALLBACK ChooseKMLProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + HWND hList; + KmlScript* pList; + UINT nIndex; + + switch (message) + { + case WM_INITDIALOG: + SetDlgItemText(hDlg,IDC_EMUDIR,szEmuDirectory); + hList = GetDlgItem(hDlg,IDC_KMLSCRIPT); + SendMessage(hList, CB_RESETCONTENT, 0, 0); + pList = pKmlList; + while (pList) + { + nIndex = (UINT) SendMessage(hList, CB_ADDSTRING, 0, (LPARAM)pList->szTitle); + SendMessage(hList, CB_SETITEMDATA, nIndex, (LPARAM)pList->nId); + pList = pList->pNext; + } + SendMessage(hList, CB_SETCURSEL, 0, 0); + return TRUE; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_EMUDIRSEL: + BrowseFolder(hDlg); // select new folder for IDC_EMUDIR + // fall into IDC_UPDATE to search for KML files in new folder + case IDC_UPDATE: + DestroyKmlList(); + GetDlgItemText(hDlg,IDC_EMUDIR,szEmuDirectory,ARRAYSIZEOF(szEmuDirectory)); + CreateKmlList(); + hList = GetDlgItem(hDlg,IDC_KMLSCRIPT); + SendMessage(hList, CB_RESETCONTENT, 0, 0); + pList = pKmlList; + while (pList) + { + nIndex = (UINT) SendMessage(hList, CB_ADDSTRING, 0, (LPARAM)pList->szTitle); + SendMessage(hList, CB_SETITEMDATA, nIndex, (LPARAM)pList->nId); + pList = pList->pNext; + } + SendMessage(hList, CB_SETCURSEL, 0, 0); + return TRUE; + case IDOK: + GetDlgItemText(hDlg,IDC_EMUDIR,szEmuDirectory,ARRAYSIZEOF(szEmuDirectory)); + hList = GetDlgItem(hDlg,IDC_KMLSCRIPT); + nIndex = (UINT) SendMessage(hList, CB_GETCURSEL, 0, 0); + nIndex = (UINT) SendMessage(hList, CB_GETITEMDATA, nIndex, 0); + pList = pKmlList; + while (pList) + { + if (pList->nId == nIndex) + { + lstrcpy(szCurrentKml, pList->szFilename); + EndDialog(hDlg, IDOK); + break; + } + pList = pList->pNext; + } + return TRUE; + case IDCANCEL: + EndDialog(hDlg, IDCANCEL); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +BOOL DisplayChooseKml(CHAR cType) +{ + INT_PTR nResult; + cKmlType = cType; + CreateKmlList(); + nResult = DialogBox(hApp, MAKEINTRESOURCE(IDD_CHOOSEKML), hWnd, (DLGPROC)ChooseKMLProc); + DestroyKmlList(); + return (nResult == IDOK); +} + + + +//################ +//# +//# KML File Mapping +//# +//################ + +static LPTSTR MapKMLFile(HANDLE hFile) +{ + DWORD lBytesRead; + DWORD dwFileSizeLow; + DWORD dwFileSizeHigh; + LPTSTR lpBuf = NULL; + + dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh); + if (dwFileSizeHigh != 0) + { + AddToLog(_T("File is too large.")); + goto fail; + } + + lpBuf = HeapAlloc(hHeap,0,(dwFileSizeLow+1)*sizeof(lpBuf[0])); + if (lpBuf == NULL) + { + PrintfToLog(_T("Cannot allocate %i bytes."), (dwFileSizeLow+1)*sizeof(lpBuf[0])); + goto fail; + } + #if defined _UNICODE + { + LPSTR szTmp = HeapAlloc(hHeap,0,dwFileSizeLow+1); + if (szTmp == NULL) + { + HeapFree(hHeap,0,lpBuf); + lpBuf = NULL; + PrintfToLog(_T("Cannot allocate %i bytes."), dwFileSizeLow+1); + goto fail; + } + ReadFile(hFile, szTmp, dwFileSizeLow, &lBytesRead, NULL); + MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szTmp, lBytesRead, lpBuf, dwFileSizeLow+1); + HeapFree(hHeap,0,szTmp); + } + #else + { + ReadFile(hFile, lpBuf, dwFileSizeLow, &lBytesRead, NULL); + } + #endif + lpBuf[dwFileSizeLow] = 0; + +fail: + CloseHandle(hFile); + return lpBuf; +} + + + +//################ +//# +//# Script Parsing +//# +//################ + +static VOID FatalError(VOID) +{ + PrintfToLog(_T("Fatal Error at line %i"), nLexLine); + szText[0] = 0; + return; +} + +static VOID InitLex(LPTSTR szScript) +{ + nLexLine = 1; + szText = szScript; + return; +} + +static VOID CleanLex(VOID) +{ + nLexLine = 0; + nLexInteger = 0; + szLexString = NULL; + szText = NULL; + return; +} + +static BOOL IsGlobalBlock(TokenId eId) +{ + UINT i; + + for (i = 0; i < ARRAYSIZEOF(eIsGlobalBlock); ++i) + { + if (eId == eIsGlobalBlock[i]) return TRUE; + } + return FALSE; +} + +static BOOL IsBlock(TokenId eId) +{ + UINT i; + + for (i = 0; i < ARRAYSIZEOF(eIsBlock); ++i) + { + if (eId == eIsBlock[i]) return TRUE; + } + return FALSE; +} + +static LPCTSTR GetStringOf(TokenId eId) +{ + UINT i = 0; + while (pLexToken[i].nLen) + { + if (pLexToken[i].eId == eId) return pLexToken[i].szName; + i++; + } + return _T(""); +} + +static VOID SkipWhite(UINT nMode) +{ + UINT i; +loop: + i = 0; + while (szLexDelim[nMode][i]) + { + if (*szText == szLexDelim[nMode][i]) break; + i++; + } + if (szLexDelim[nMode][i] != 0) + { + if (szLexDelim[nMode][i]==_T('\n')) nLexLine++; + szText++; + goto loop; + } + if (*szText==_T('#')) + { + do szText++; while (*szText != _T('\n') && *szText != 0); + if (nMode != LEX_PARAM) goto loop; + } + return; +} + +static TokenId ParseToken(UINT nMode) +{ + UINT i, j, k; + i = 0; + while (szText[i]) + { + j = 0; + while (szLexDelim[nMode][j]) + { + if (szLexDelim[nMode][j] == szText[i]) break; + j++; + } + if (szLexDelim[nMode][j] == _T('\n')) nLexLine++; + if (szLexDelim[nMode][j] != 0) break; + i++; + } + if (i==0) + { + return TOK_NONE; + } + j = 0; + while (pLexToken[j].nLen) + { + if (pLexToken[j].nLen>i) + { + j++; + continue; + } + if (pLexToken[j].nLen= LEX_BLOCK && nMode <= LEX_PARAM); + _ASSERT(nMode >= 0 && nMode < ARRAYSIZEOF(szLexDelim)); + + SkipWhite(nMode); + if (_istdigit(*szText)) + { + nLexInteger = ParseInteger(); + return TOK_INTEGER; + } + if (*szText == _T('"')) + { + szLexString = ParseString(); + return TOK_STRING; + } + if (nMode == LEX_PARAM) + { + if (*szText == _T('\n')) // end of line + { + nLexLine++; // next line + szText++; // skip LF + return TOK_EOL; + } + if (*szText == 0) // end of file + { + return TOK_EOL; + } + } + return ParseToken(nMode); +} + +static KmlLine* ParseLine(TokenId eCommand) +{ + UINT i, j; + DWORD nParams; + TokenId eToken; + KmlLine* pLine; + + i = 0; + while (pLexToken[i].nLen) + { + if (pLexToken[i].eId == eCommand) break; + i++; + } + if (pLexToken[i].nLen == 0) return NULL; + + j = 0; + pLine = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sizeof(KmlLine)); + pLine->eCommand = eCommand; + nParams = pLexToken[i].nParams; +loop: + eToken = Lex(LEX_PARAM); + if ((nParams&7)==TYPE_NONE) + { + if (eToken != TOK_EOL) + { + PrintfToLog(_T("%i: Too many parameters (%i expected)."), nLexLine, j); + goto errline; // free memory of arguments + } + return pLine; + } + if ((nParams&7)==TYPE_INTEGER) + { + if (eToken != TOK_INTEGER) + { + PrintfToLog(_T("%i: Parameter %i of %s must be an integer."), nLexLine, j+1, pLexToken[i].szName); + goto errline; // free memory of arguments + } + pLine->nParam[j++] = nLexInteger; + nParams >>= 3; + goto loop; + } + if ((nParams&7)==TYPE_STRING) + { + if (eToken != TOK_STRING) + { + PrintfToLog(_T("%i: Parameter %i of %s must be a string."), nLexLine, j+1, pLexToken[i].szName); + goto errline; // free memory of arguments + } + pLine->nParam[j++] = (DWORD_PTR) szLexString; + nParams >>= 3; + goto loop; + } + AddToLog(_T("Oops...")); +errline: + // if last argument was string, free it + if (eToken == TOK_STRING) HeapFree(hHeap,0,szLexString); + + nParams = pLexToken[i].nParams; // get argument types of command + for (i=0; inParam[i]); + } + nParams >>= 3; // next argument type + } + HeapFree(hHeap,0,pLine); + return NULL; +} + +static KmlLine* IncludeLines(LPCTSTR szFilename) +{ + HANDLE hFile; + LPTSTR lpbyBuf; + UINT uOldLine; + LPTSTR szOldText; + KmlLine* pLine; + + SetCurrentDirectory(szEmuDirectory); + hFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + SetCurrentDirectory(szCurrentDirectory); + if (hFile == INVALID_HANDLE_VALUE) + { + PrintfToLog(_T("Error while opening include file %s."), szFilename); + FatalError(); + return NULL; + } + if ((lpbyBuf = MapKMLFile(hFile)) == NULL) + { + FatalError(); + return NULL; + } + + uOldLine = nLexLine; + szOldText = szText; + + nLinesIncludeLevel++; + PrintfToLog(_T("l%i:Including %s"), nLinesIncludeLevel, szFilename); + InitLex(lpbyBuf); + pLine = ParseLines(); + CleanLex(); + nLinesIncludeLevel--; + + nLexLine = uOldLine; + szText = szOldText; + HeapFree(hHeap,0,lpbyBuf); + + return pLine; +} + +static KmlLine* ParseLines(VOID) +{ + KmlLine* pFirst = NULL; + KmlLine* pLine = NULL; + TokenId eToken; + UINT nLevel = 0; + + while ((eToken = Lex(LEX_COMMAND))) + { + if (IsGlobalBlock(eToken)) // check for block command + { + PrintfToLog(_T("%i: Invalid Command %s."), nLexLine, GetStringOf(eToken)); + goto abort; + } + if (IsBlock(eToken)) nLevel++; + if (eToken == TOK_INCLUDE) + { + LPTSTR szFilename; + eToken = Lex(LEX_PARAM); // get include parameter in 'szLexString' + if (eToken != TOK_STRING) // not a string (token don't begin with ") + { + AddToLog(_T("Include: string expected as parameter.")); + FatalError(); + goto abort; + } + szFilename = szLexString; // save pointer to allocated memory + if (pFirst) + { + pLine->pNext = IncludeLines(szLexString); + } + else + { + pLine = pFirst = IncludeLines(szLexString); + } + HeapFree(hHeap,0,szFilename); // free memory + if (pLine == NULL) // parsing error + goto abort; + while (pLine->pNext) pLine=pLine->pNext; + continue; + } + if (eToken == TOK_END) + { + if (nLevel) + { + nLevel--; + } + else + { + if (pLine) pLine->pNext = NULL; + return pFirst; + } + } + if (pFirst) + { + pLine = pLine->pNext = ParseLine(eToken); + } + else + { + pLine = pFirst = ParseLine(eToken); + } + if (pLine == NULL) // parsing error + goto abort; + } + if (nLinesIncludeLevel) + { + if (pLine) pLine->pNext = NULL; + return pFirst; + } +abort: + if (pFirst) + { + FreeLines(pFirst); + } + return NULL; +} + +static KmlBlock* ParseBlock(TokenId eType) +{ + UINT u1; + KmlBlock* pBlock; + TokenId eToken; + + nLinesIncludeLevel = 0; + + pBlock = HeapAlloc(hHeap,HEAP_ZERO_MEMORY,sizeof(KmlBlock)); + pBlock->eType = eType; + + u1 = 0; + while (pLexToken[u1].nLen) + { + if (pLexToken[u1].eId == eType) break; + u1++; + } + if (pLexToken[u1].nParams) + { + eToken = Lex(LEX_COMMAND); + switch (eToken) + { + case TOK_NONE: + AddToLog(_T("Open Block at End Of File.")); + HeapFree(hHeap,0,pBlock); + FatalError(); + return NULL; + case TOK_INTEGER: + if ((pLexToken[u1].nParams&7)!=TYPE_INTEGER) + { + AddToLog(_T("Wrong block argument.")); + HeapFree(hHeap,0,pBlock); + FatalError(); + return NULL; + } + pBlock->nId = nLexInteger; + break; + default: + AddToLog(_T("Wrong block argument.")); + HeapFree(hHeap,0,pBlock); + FatalError(); + return NULL; + } + } + + pBlock->pFirstLine = ParseLines(); + + if (pBlock->pFirstLine == NULL) // break on ParseLines error + { + HeapFree(hHeap,0,pBlock); + pBlock = NULL; + } + + return pBlock; +} + +static KmlBlock* IncludeBlocks(LPCTSTR szFilename) +{ + HANDLE hFile; + LPTSTR lpbyBuf; + UINT uOldLine; + LPTSTR szOldText; + KmlBlock* pFirst; + + SetCurrentDirectory(szEmuDirectory); + hFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + SetCurrentDirectory(szCurrentDirectory); + if (hFile == INVALID_HANDLE_VALUE) + { + PrintfToLog(_T("Error while opening include file %s."), szFilename); + FatalError(); + return NULL; + } + if ((lpbyBuf = MapKMLFile(hFile)) == NULL) + { + FatalError(); + return NULL; + } + + uOldLine = nLexLine; + szOldText = szText; + + nBlocksIncludeLevel++; + PrintfToLog(_T("b%i:Including %s"), nBlocksIncludeLevel, szFilename); + InitLex(lpbyBuf); + pFirst = ParseBlocks(); + CleanLex(); + nBlocksIncludeLevel--; + + nLexLine = uOldLine; + szText = szOldText; + HeapFree(hHeap,0,lpbyBuf); + + return pFirst; +} + +static KmlBlock* ParseBlocks(VOID) +{ + TokenId eToken; + KmlBlock* pFirst = NULL; + KmlBlock* pBlock = NULL; + + while ((eToken=Lex(LEX_BLOCK))!=TOK_NONE) + { + if (eToken == TOK_INCLUDE) + { + LPTSTR szFilename; + eToken = Lex(LEX_PARAM); // get include parameter in 'szLexString' + if (eToken != TOK_STRING) // not a string (token don't begin with ") + { + AddToLog(_T("Include: string expected as parameter.")); + FatalError(); + goto abort; + } + szFilename = szLexString; // save pointer to allocated memory + if (pFirst) + pBlock = pBlock->pNext = IncludeBlocks(szLexString); + else + pBlock = pFirst = IncludeBlocks(szLexString); + HeapFree(hHeap,0,szFilename); // free memory + if (pBlock == NULL) // parsing error + goto abort; + while (pBlock->pNext) pBlock=pBlock->pNext; + continue; + } + if (!IsGlobalBlock(eToken)) // check for valid block commands + { + PrintfToLog(_T("%i: Invalid Block %s."), nLexLine, GetStringOf(eToken)); + FatalError(); + goto abort; + } + if (pFirst) + pBlock = pBlock->pNext = ParseBlock(eToken); + else + pBlock = pFirst = ParseBlock(eToken); + if (pBlock == NULL) + { + AddToLog(_T("Invalid block.")); + FatalError(); + goto abort; + } + } + if (pFirst) pBlock->pNext = NULL; + if (*szText != 0) // still KML text left + { + FatalError(); // error unknown block token + goto abort; + } + return pFirst; +abort: + if (pFirst) FreeBlocks(pFirst); + return NULL; +} + + + +//################ +//# +//# Initialization Phase +//# +//################ + +static VOID InitGlobal(KmlBlock* pBlock) +{ + KmlLine* pLine = pBlock->pFirstLine; + while (pLine) + { + switch (pLine->eCommand) + { + case TOK_TITLE: + PrintfToLog(_T("Title: %s"), (LPTSTR)pLine->nParam[0]); + break; + case TOK_AUTHOR: + PrintfToLog(_T("Author: %s"), (LPTSTR)pLine->nParam[0]); + break; + case TOK_PRINT: + AddToLog((LPTSTR)pLine->nParam[0]); + break; + case TOK_HARDWARE: + PrintfToLog(_T("Hardware Platform: %s"), (LPTSTR)pLine->nParam[0]); + break; + case TOK_MODEL: + cCurrentRomType = ((BYTE *)pLine->nParam[0])[0]; + PrintfToLog(_T("Calculator Model : %c"), cCurrentRomType); + break; + case TOK_CLASS: + nCurrentClass = (UINT) pLine->nParam[0]; + PrintfToLog(_T("Calculator Class : %u"), nCurrentClass); + break; + case TOK_DEBUG: + bDebug = (BOOL) pLine->nParam[0]&1; + PrintfToLog(_T("Debug %s"), bDebug?_T("On"):_T("Off")); + break; + case TOK_ROM: + if (pbyRom != NULL) + { + PrintfToLog(_T("Rom %s Ignored."), (LPTSTR)pLine->nParam[0]); + AddToLog(_T("Please put only one Rom command in the Global block.")); + break; + } + if (!MapRom((LPTSTR)pLine->nParam[0])) + { + PrintfToLog(_T("Cannot open Rom %s"), (LPTSTR)pLine->nParam[0]); + break; + } + PrintfToLog(_T("Rom %s Loaded."), (LPTSTR)pLine->nParam[0]); + break; + case TOK_PATCH: + if (pbyRom == NULL) + { + PrintfToLog(_T("Patch %s ignored."), (LPTSTR)pLine->nParam[0]); + AddToLog(_T("Please put the Rom command before any Patch.")); + break; + } + if (PatchRom((LPTSTR)pLine->nParam[0]) == TRUE) + PrintfToLog(_T("Patch %s Loaded"), (LPTSTR)pLine->nParam[0]); + else + PrintfToLog(_T("Patch %s is Wrong or Missing"), (LPTSTR)pLine->nParam[0]); + break; + case TOK_BITMAP: + if (hMainDC != NULL) + { + PrintfToLog(_T("Bitmap %s Ignored."), (LPTSTR)pLine->nParam[0]); + AddToLog(_T("Please put only one Bitmap command in the Global block.")); + break; + } + if (!CreateMainBitmap((LPTSTR)pLine->nParam[0])) + { + PrintfToLog(_T("Cannot Load Bitmap %s."), (LPTSTR)pLine->nParam[0]); + break; + } + PrintfToLog(_T("Bitmap %s Loaded."), (LPTSTR)pLine->nParam[0]); + break; + default: + PrintfToLog(_T("Command %s Ignored in Block %s"), GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType)); + } + pLine = pLine->pNext; + } + return; +} + +static KmlLine* InitBackground(KmlBlock* pBlock) +{ + KmlLine* pLine = pBlock->pFirstLine; + while (pLine) + { + switch (pLine->eCommand) + { + case TOK_OFFSET: + nBackgroundX = (UINT) pLine->nParam[0]; + nBackgroundY = (UINT) pLine->nParam[1]; + break; + case TOK_SIZE: + nBackgroundW = (UINT) pLine->nParam[0]; + nBackgroundH = (UINT) pLine->nParam[1]; + break; + case TOK_END: + return pLine; + default: + PrintfToLog(_T("Command %s Ignored in Block %s"), GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType)); + } + pLine = pLine->pNext; + } + return NULL; +} + +static KmlLine* InitLcd(KmlBlock* pBlock) +{ + KmlLine* pLine = pBlock->pFirstLine; + while (pLine) + { + switch (pLine->eCommand) + { + case TOK_OFFSET: + nLcdX = (UINT) pLine->nParam[0]; + nLcdY = (UINT) pLine->nParam[1]; + break; + case TOK_ZOOM: + nLcdZoom = (UINT) pLine->nParam[0]; + if (!(nLcdZoom >= 1 && nLcdZoom <= 4)) + nLcdZoom = 1; + break; + case TOK_COLOR: + SetLcdColor((UINT) pLine->nParam[0],(UINT) pLine->nParam[1], + (UINT) pLine->nParam[2],(UINT) pLine->nParam[3]); + break; + case TOK_END: + return pLine; + default: + PrintfToLog(_T("Command %s Ignored in Block %s"), GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType)); + } + pLine = pLine->pNext; + } + return NULL; +} + +static KmlLine* InitAnnunciator(KmlBlock* pBlock) +{ + KmlLine* pLine = pBlock->pFirstLine; + UINT nId = pBlock->nId-1; + if (nId >= ARRAYSIZEOF(pAnnunciator)) + { + PrintfToLog(_T("Wrong Annunciator Id %i"), nId); + return NULL; + } + nAnnunciators++; + while (pLine) + { + switch (pLine->eCommand) + { + case TOK_OFFSET: + pAnnunciator[nId].nOx = (UINT) pLine->nParam[0]; + pAnnunciator[nId].nOy = (UINT) pLine->nParam[1]; + break; + case TOK_DOWN: + pAnnunciator[nId].nDx = (UINT) pLine->nParam[0]; + pAnnunciator[nId].nDy = (UINT) pLine->nParam[1]; + break; + case TOK_SIZE: + pAnnunciator[nId].nCx = (UINT) pLine->nParam[0]; + pAnnunciator[nId].nCy = (UINT) pLine->nParam[1]; + break; + case TOK_END: + return pLine; + default: + PrintfToLog(_T("Command %s Ignored in Block %s"), GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType)); + } + pLine = pLine->pNext; + } + return NULL; +} + +static VOID InitButton(KmlBlock* pBlock) +{ + KmlLine* pLine = pBlock->pFirstLine; + UINT nLevel = 0; + if (nButtons>=256) + { + AddToLog(_T("Only the first 256 buttons will be defined.")); + return; + } + pButton[nButtons].nId = pBlock->nId; + pButton[nButtons].bDown = FALSE; + pButton[nButtons].nType = 0; // default : user defined button + while (pLine) + { + if (nLevel) + { + if (IsBlock(pLine->eCommand)) nLevel++; + if (pLine->eCommand == TOK_END) nLevel--; + pLine = pLine->pNext; + continue; + } + if (IsBlock(pLine->eCommand)) nLevel++; + switch (pLine->eCommand) + { + case TOK_TYPE: + pButton[nButtons].nType = (UINT) pLine->nParam[0]; + break; + case TOK_OFFSET: + pButton[nButtons].nOx = (UINT) pLine->nParam[0]; + pButton[nButtons].nOy = (UINT) pLine->nParam[1]; + break; + case TOK_DOWN: + pButton[nButtons].nDx = (UINT) pLine->nParam[0]; + pButton[nButtons].nDy = (UINT) pLine->nParam[1]; + break; + case TOK_SIZE: + pButton[nButtons].nCx = (UINT) pLine->nParam[0]; + pButton[nButtons].nCy = (UINT) pLine->nParam[1]; + break; + case TOK_OUTIN: + pButton[nButtons].nOut = (UINT) pLine->nParam[0]; + pButton[nButtons].nIn = (UINT) pLine->nParam[1]; + break; + case TOK_ONDOWN: + pButton[nButtons].pOnDown = pLine; + break; + case TOK_ONUP: + pButton[nButtons].pOnUp = pLine; + break; + case TOK_NOHOLD: + pButton[nButtons].dwFlags &= ~(BUTTON_VIRTUAL); + pButton[nButtons].dwFlags |= BUTTON_NOHOLD; + break; + case TOK_VIRTUAL: + pButton[nButtons].dwFlags &= ~(BUTTON_NOHOLD); + pButton[nButtons].dwFlags |= BUTTON_VIRTUAL; + break; + default: + PrintfToLog(_T("Command %s Ignored in Block %s %i"), GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType), pBlock->nId); + } + pLine = pLine->pNext; + } + if (nLevel) + PrintfToLog(_T("%i Open Block(s) in Block %s %i"), nLevel, GetStringOf(pBlock->eType), pBlock->nId); + nButtons++; + return; +} + + + +//################ +//# +//# Execution +//# +//################ + +static KmlLine* SkipLines(KmlLine* pLine, TokenId eCommand) +{ + UINT nLevel = 0; + while (pLine) + { + if (IsBlock(pLine->eCommand)) nLevel++; + if (pLine->eCommand==eCommand) + { + if (nLevel == 0) return pLine->pNext; + } + if (pLine->eCommand == TOK_END) + { + if (nLevel) + nLevel--; + else + break; + } + pLine = pLine->pNext; + } + return pLine; +} + +static KmlLine* If(KmlLine* pLine, BOOL bCondition) +{ + pLine = pLine->pNext; + if (bCondition) + { + while (pLine) + { + if (pLine->eCommand == TOK_END) + { + pLine = pLine->pNext; + break; + } + if (pLine->eCommand == TOK_ELSE) + { + pLine = SkipLines(pLine, TOK_END); + break; + } + pLine = RunLine(pLine); + } + } + else + { + pLine = SkipLines(pLine, TOK_ELSE); + while (pLine) + { + if (pLine->eCommand == TOK_END) + { + pLine = pLine->pNext; + break; + } + pLine = RunLine(pLine); + } + } + return pLine; +} + +static KmlLine* RunLine(KmlLine* pLine) +{ + switch (pLine->eCommand) + { + case TOK_MAP: + if (byVKeyMap[pLine->nParam[0]&0xFF]&1) + PressButtonById((UINT) pLine->nParam[1]); + else + ReleaseButtonById((UINT) pLine->nParam[1]); + break; + case TOK_PRESS: + PressButtonById((UINT) pLine->nParam[0]); + break; + case TOK_RELEASE: + ReleaseButtonById((UINT) pLine->nParam[0]); + break; + case TOK_MENUITEM: + PostMessage(hWnd, WM_COMMAND, 0x19C40+(pLine->nParam[0]&0xFF), 0); + break; + case TOK_SETFLAG: + nKMLFlags |= 1<<(pLine->nParam[0]&0x1F); + break; + case TOK_RESETFLAG: + nKMLFlags &= ~(1<<(pLine->nParam[0]&0x1F)); + break; + case TOK_NOTFLAG: + nKMLFlags ^= 1<<(pLine->nParam[0]&0x1F); + break; + case TOK_IFPRESSED: + return If(pLine,byVKeyMap[pLine->nParam[0]&0xFF]); + break; + case TOK_IFFLAG: + return If(pLine,(nKMLFlags>>(pLine->nParam[0]&0x1F))&1); + default: + break; + } + return pLine->pNext; +} + + + +//################ +//# +//# Clean Up +//# +//################ + +static VOID FreeLines(KmlLine* pLine) +{ + while (pLine) + { + KmlLine* pThisLine = pLine; + UINT i = 0; + DWORD nParams; + while (pLexToken[i].nLen) // search in all token definitions + { + // break when token definition found + if (pLexToken[i].eId == pLine->eCommand) break; + i++; // next token definition + } + nParams = pLexToken[i].nParams; // get argument types of command + i = 0; // first parameter + while ((nParams&7)) // argument left + { + if ((nParams&7) == TYPE_STRING) // string type + { + HeapFree(hHeap,0,(LPVOID)pLine->nParam[i]); + } + i++; // incr. parameter buffer index + nParams >>= 3; // next argument type + } + pLine = pLine->pNext; // get next line + HeapFree(hHeap,0,pThisLine); + } + return; +} + +VOID FreeBlocks(KmlBlock* pBlock) +{ + while (pBlock) + { + KmlBlock* pThisBlock = pBlock; + pBlock = pBlock->pNext; + FreeLines(pThisBlock->pFirstLine); + HeapFree(hHeap,0,pThisBlock); + } + return; +} + +VOID KillKML(VOID) +{ + if ((nState==SM_RUN)||(nState==SM_SLEEP)) + { + AbortMessage(_T("FATAL: KillKML while emulator is running !!!")); + SwitchToState(SM_RETURN); + DestroyWindow(hWnd); + } + UnmapRom(); + DestroyLcdBitmap(); + DestroyMainBitmap(); + if (hPalette) + { + BOOL err; + + if (hWindowDC) SelectPalette(hWindowDC, hOldPalette, FALSE); + err = DeleteObject(hPalette); + _ASSERT(err != FALSE); // freed resource memory + hPalette = NULL; + } + bClicking = FALSE; + uButtonClicked = 0; + FreeBlocks(pKml); + pKml = NULL; + nButtons = 0; + nScancodes = 0; + nAnnunciators = 0; + bDebug = TRUE; + nKMLFlags = 0; + ZeroMemory(pButton, sizeof(pButton)); + ZeroMemory(pAnnunciator, sizeof(pAnnunciator)); + ZeroMemory(pVKey, sizeof(pVKey)); + ClearLog(); + nBackgroundX = 0; + nBackgroundY = 0; + nBackgroundW = 256; + nBackgroundH = 0; + nLcdZoom = 1; + UpdateWindowStatus(); + ResizeWindow(); + return; +} + + + +//################ +//# +//# Extract Keyword's Parameters +//# +//################ + +static LPTSTR GetStringParam(KmlBlock* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam) +{ + while (pBlock) + { + if (pBlock->eType == eBlock) + { + KmlLine* pLine = pBlock->pFirstLine; + while (pLine) + { + if (pLine->eCommand == eCommand) + { + return (LPTSTR)pLine->nParam[nParam]; + } + pLine = pLine->pNext; + } + } + pBlock = pBlock->pNext; + } + return NULL; +} + +static DWORD GetIntegerParam(KmlBlock* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam) +{ + while (pBlock) + { + if (pBlock->eType == eBlock) + { + KmlLine* pLine = pBlock->pFirstLine; + while (pLine) + { + if (pLine->eCommand == eCommand) + { + return (DWORD) pLine->nParam[nParam]; + } + pLine = pLine->pNext; + } + } + pBlock = pBlock->pNext; + } + return 0; +} + + + +//################ +//# +//# Buttons +//# +//################ + +static INT iSqrt(INT nNumber) // integer y=sqrt(x) function +{ + INT m, b = 0, t = nNumber; + + do + { + m = (b + t + 1) / 2; // median number + if (m * m - nNumber > 0) // calculate x^2-y + t = m; // adjust upper border + else + b = m; // adjust lower border + } + while(t - b > 1); + + return b; +} + +static VOID AdjustPixel(UINT x, UINT y, BYTE byOffset) +{ + COLORREF rgb; + WORD wB, wG, wR; + + rgb = GetPixel(hWindowDC, x, y); + + // adjust color red + wR = (((WORD) rgb) & 0x00FF) + byOffset; + if (wR > 0xFF) wR = 0xFF; + rgb >>= 8; + // adjust color green + wG = (((WORD) rgb) & 0x00FF) + byOffset; + if (wG > 0xFF) wG = 0xFF; + rgb >>= 8; + // adjust color blue + wB = (((WORD) rgb) & 0x00FF) + byOffset; + if (wB > 0xFF) wB = 0xFF; + + SetPixel(hWindowDC, x, y, RGB(wR,wG,wB)); + return; +} + +// draw transparent circle with center coordinates and radius in pixel +static __inline VOID TransparentCircle(UINT cx, UINT cy, UINT r) +{ + #define HIGHADJ 0x80 // color incr. at center + #define LOWADJ 0x10 // color incr. at border + + UINT x, y, rr, rrc; + + if (r < 2) return; // radius 2 pixel minimum + + rr = r * r; // calculate r^2 + rrc = (r-1) * (r-1); // calculate (r-1)^2 for color steps + + // y-rows of circle + for (y = 0; y < r; ++y) + { + UINT yy = y * y; // calculate y^2 + + // x-columns of circle + UINT nXWidth = iSqrt(rr-yy); + + for (x = 0; x < nXWidth; ++x) + { + // color offset, sqrt(x*x+y*y) < r !!! + BYTE byOff = HIGHADJ - (BYTE) (iSqrt((x*x+yy) * (HIGHADJ-LOWADJ)*(HIGHADJ-LOWADJ) / rrc)); + + AdjustPixel(cx+x, cy+y, byOff); + if (x != 0) AdjustPixel(cx-x, cy+y, byOff); + if (y != 0) AdjustPixel(cx+x, cy-y, byOff); + if (x != 0 && y != 0) AdjustPixel(cx-x, cy-y, byOff); + } + } + return; + + #undef HIGHADJ + #undef LOWADJ +} + +static VOID DrawButton(UINT nId) +{ + UINT x0 = pButton[nId].nOx; + UINT y0 = pButton[nId].nOy; + + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + switch (pButton[nId].nType) + { + case 0: // bitmap key + if (pButton[nId].bDown) + { + BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, pButton[nId].nDx, pButton[nId].nDy, SRCCOPY); + } + else + { + // update background only + BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY); + } + break; + case 1: // shift key to right down + if (pButton[nId].bDown) + { + UINT x1 = x0+pButton[nId].nCx-1; + UINT y1 = y0+pButton[nId].nCy-1; + BitBlt(hWindowDC, x0+3,y0+3,pButton[nId].nCx-5,pButton[nId].nCy-5,hMainDC,x0+2,y0+2,SRCCOPY); + SelectObject(hWindowDC, GetStockObject(BLACK_PEN)); + MoveToEx(hWindowDC, x0, y0, NULL); LineTo(hWindowDC, x1, y0); + MoveToEx(hWindowDC, x0, y0, NULL); LineTo(hWindowDC, x0, y1); + SelectObject(hWindowDC, GetStockObject(WHITE_PEN)); + MoveToEx(hWindowDC, x1, y0, NULL); LineTo(hWindowDC, x1, y1); + MoveToEx(hWindowDC, x0, y1, NULL); LineTo(hWindowDC, x1+1, y1); + } + else + { + BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY); + } + break; + case 2: // do nothing + break; + case 3: // invert key color, even in display + if (pButton[nId].bDown) + { + PatBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, DSTINVERT); + } + else + { + RECT Rect; + Rect.left = x0 - nBackgroundX; + Rect.top = y0 - nBackgroundY; + Rect.right = Rect.left + pButton[nId].nCx; + Rect.bottom = Rect.top + pButton[nId].nCy; + InvalidateRect(hWnd, &Rect, FALSE); // call WM_PAINT for background and display redraw + } + break; + case 4: // bitmap key, even in display + if (pButton[nId].bDown) + { + // update background only + BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY); + } + else + { + RECT Rect; + Rect.left = x0 - nBackgroundX; + Rect.top = y0 - nBackgroundY; + Rect.right = Rect.left + pButton[nId].nCx; + Rect.bottom = Rect.top + pButton[nId].nCy; + InvalidateRect(hWnd, &Rect, FALSE); // call WM_PAINT for background and display redraw + } + break; + case 5: // transparent circle + if (pButton[nId].bDown) + { + TransparentCircle(x0 + pButton[nId].nCx / 2, // x-center coordinate + y0 + pButton[nId].nCy / 2, // y-center coordinate + min(pButton[nId].nCx,pButton[nId].nCy) / 2); // radius + } + else + { + // update background only + BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY); + } + break; + default: // black key, default drawing on illegal types + if (pButton[nId].bDown) + { + PatBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, BLACKNESS); + } + else + { + // update background only + BitBlt(hWindowDC, x0, y0, pButton[nId].nCx, pButton[nId].nCy, hMainDC, x0, y0, SRCCOPY); + } + } + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + return; +} + +static VOID PressButton(UINT nId) +{ + if (pButton[nId].bDown) return; // key already pressed -> exit + + pButton[nId].bDown = TRUE; + DrawButton(nId); + if (pButton[nId].nIn) + { + KeyboardEvent(TRUE,pButton[nId].nOut,pButton[nId].nIn); + } + else + { + KmlLine* pLine = pButton[nId].pOnDown; + while ((pLine)&&(pLine->eCommand!=TOK_END)) + { + pLine = RunLine(pLine); + } + } + return; +} + +static VOID ReleaseButton(UINT nId) +{ + pButton[nId].bDown = FALSE; + DrawButton(nId); + if (pButton[nId].nIn) + { + KeyboardEvent(FALSE,pButton[nId].nOut,pButton[nId].nIn); + } + else + { + KmlLine* pLine = pButton[nId].pOnUp; + while ((pLine)&&(pLine->eCommand!=TOK_END)) + { + pLine = RunLine(pLine); + } + } + return; +} + +static VOID PressButtonById(UINT nId) +{ + UINT i; + for (i=0; iright > (LONG) (pButton[i].nOx) + && rc->bottom > (LONG) (pButton[i].nOy) + && rc->left <= (LONG) (pButton[i].nOx + pButton[i].nCx) + && rc->top <= (LONG) (pButton[i].nOy + pButton[i].nCy)) + { + // on button type 3 and 5 clear complete key area before drawing + if (pButton[i].nType == 3 || pButton[i].nType == 5) + { + UINT x0 = pButton[i].nOx; + UINT y0 = pButton[i].nOy; + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + BitBlt(hWindowDC, x0, y0, pButton[i].nCx, pButton[i].nCy, hMainDC, x0, y0, SRCCOPY); + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + } + DrawButton(i); // redraw pressed button + } + } + return; +} + + +//################ +//# +//# Annunciators +//# +//################ + +VOID DrawAnnunciator(UINT nId, BOOL bOn) +{ + UINT nSx,nSy; + + --nId; // zero based ID + if (nId >= ARRAYSIZEOF(pAnnunciator)) return; + if (bOn) + { + nSx = pAnnunciator[nId].nDx; // position of annunciator + nSy = pAnnunciator[nId].nDy; + } + else + { + nSx = pAnnunciator[nId].nOx; // position of background + nSy = pAnnunciator[nId].nOy; + } + EnterCriticalSection(&csGDILock); // solving NT GDI problems + { + BitBlt(hWindowDC, + pAnnunciator[nId].nOx, pAnnunciator[nId].nOy, + pAnnunciator[nId].nCx, pAnnunciator[nId].nCy, + hMainDC, + nSx, nSy, + SRCCOPY); + GdiFlush(); + } + LeaveCriticalSection(&csGDILock); + return; +} + + + +//################ +//# +//# Mouse +//# +//################ + +static BOOL ClipButton(UINT x, UINT y, UINT nId) +{ + x += nBackgroundX; // source display offset + y += nBackgroundY; + + return (pButton[nId].nOx<=x) + && (pButton[nId].nOy<=y) + &&(x<(pButton[nId].nOx+pButton[nId].nCx)) + &&(y<(pButton[nId].nOy+pButton[nId].nCy)); +} + +VOID MouseButtonDownAt(UINT nFlags, DWORD x, DWORD y) +{ + UINT i; + for (i=0; i quit + if ((bPressed) && !(ClipButton(x,y,uLastPressedKey))) // not on last pressed key + ReleaseAllButtons(); // release all buttons + if (!bClicking) return; // normal emulation key -> quit + + if (pButton[uButtonClicked].dwFlags&BUTTON_NOHOLD) + { + if (ClipButton(x,y, uButtonClicked) != pButton[uButtonClicked].bDown) + { + pButton[uButtonClicked].bDown = !pButton[uButtonClicked].bDown; + DrawButton(uButtonClicked); + } + return; + } + if (pButton[uButtonClicked].dwFlags&BUTTON_VIRTUAL) + { + if (!ClipButton(x,y, uButtonClicked)) + { + ReleaseButton(uButtonClicked); + bClicking = FALSE; + uButtonClicked = 0; + } + return; + } + return; +} + + + +//################ +//# +//# Keyboard +//# +//################ + +VOID RunKey(BYTE nId, BOOL bPressed) +{ + if (pVKey[nId]) + { + KmlLine* pLine = pVKey[nId]->pFirstLine; + byVKeyMap[nId] = bPressed; + while (pLine) pLine = RunLine(pLine); + } + else + { + if (bDebug&&bPressed) + { + TCHAR szTemp[128]; + wsprintf(szTemp,_T("Scancode %i"),nId); + InfoMessage(szTemp); + } + } + return; +} + + + +//################ +//# +//# Macro player +//# +//################ + +VOID PlayKey(UINT nOut, UINT nIn, BOOL bPressed) +{ + // scan from last buttons because LCD buttons mostly defined first + INT i = nButtons; + while (--i >= 0) + { + if (pButton[i].nOut == nOut && pButton[i].nIn == nIn) + { + if (bPressed) + PressButton(i); + else + ReleaseButton(i); + return; + } + } + return; +} + + + +//################ +//# +//# Load and Initialize Script +//# +//################ + +static KmlBlock* LoadKMLGlobal(LPCTSTR szFilename) +{ + HANDLE hFile; + LPTSTR lpBuf; + KmlBlock* pBlock; + DWORD eToken; + + SetCurrentDirectory(szEmuDirectory); + hFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + SetCurrentDirectory(szCurrentDirectory); + if (hFile == INVALID_HANDLE_VALUE) return NULL; + if ((lpBuf = MapKMLFile(hFile)) == NULL) + return NULL; + + InitLex(lpBuf); + pBlock = NULL; + while ((eToken = Lex(LEX_BLOCK)) != TOK_NONE) + { + if (eToken == TOK_GLOBAL) + { + pBlock = ParseBlock(eToken); + if (pBlock) pBlock->pNext = NULL; + break; + } + } + CleanLex(); + ClearLog(); + HeapFree(hHeap,0,lpBuf); + return pBlock; +} + +BOOL InitKML(LPCTSTR szFilename, BOOL bNoLog) +{ + HANDLE hFile; + LPTSTR lpBuf; + KmlBlock* pBlock; + BOOL bOk = FALSE; + + KillKML(); + + nBlocksIncludeLevel = 0; + PrintfToLog(_T("Reading %s"), szFilename); + SetCurrentDirectory(szEmuDirectory); + hFile = CreateFile(szFilename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + SetCurrentDirectory(szCurrentDirectory); + if (hFile == INVALID_HANDLE_VALUE) + { + AddToLog(_T("Error while opening the file.")); + goto quit; + } + if ((lpBuf = MapKMLFile(hFile)) == NULL) + goto quit; + + InitLex(lpBuf); + pKml = ParseBlocks(); + CleanLex(); + + HeapFree(hHeap,0,lpBuf); + if (pKml == NULL) goto quit; + + pBlock = pKml; + while (pBlock) + { + switch (pBlock->eType) + { + case TOK_BUTTON: + InitButton(pBlock); + break; + case TOK_SCANCODE: + nScancodes++; + pVKey[pBlock->nId] = pBlock; + break; + case TOK_ANNUNCIATOR: + InitAnnunciator(pBlock); + break; + case TOK_GLOBAL: + InitGlobal(pBlock); + break; + case TOK_LCD: + InitLcd(pBlock); + break; + case TOK_BACKGROUND: + InitBackground(pBlock); + break; + default: + PrintfToLog(_T("Block %s Ignored."), GetStringOf(pBlock->eType)); + pBlock = pBlock->pNext; + } + pBlock = pBlock->pNext; + } + + if (!isModelValid(cCurrentRomType)) + { + AddToLog(_T("This KML Script doesn't specify a valid model.")); + goto quit; + } + if (pbyRom == NULL) + { + AddToLog(_T("This KML Script doesn't specify the ROM to use, or the ROM could not be loaded.")); + goto quit; + } + if (hMainDC == NULL) + { + AddToLog(_T("This KML Script doesn't specify the background bitmap, or bitmap could not be loaded.")); + goto quit; + } + + CreateLcdBitmap(); + + PrintfToLog(_T("%i Buttons Defined"), nButtons); + PrintfToLog(_T("%i Scancodes Defined"), nScancodes); + PrintfToLog(_T("%i Annunciators Defined"), nAnnunciators); + + bOk = TRUE; + +quit: + if (bOk) + { + // HP38G/HP39G(+)/HP40G have no object loading, ignore // CdB for HP: add apples + DragAcceptFiles(hWnd,cCurrentRomType != '6' && cCurrentRomType != 'A' && cCurrentRomType != 'E' && cCurrentRomType != 'P'); + + if (!bNoLog) + { + AddToLog(_T("Press Ok to Continue.")); + if (bAlwaysDisplayLog&&(!DisplayKMLLog(bOk))) + { + KillKML(); + return FALSE; + } + } + } + else + { + AddToLog(_T("Press Cancel to Abort.")); + if (!DisplayKMLLog(bOk)) + { + KillKML(); + return FALSE; + } + } + + ResizeWindow(); + ClearLog(); + return bOk; +} diff --git a/SOURCE/KML.H b/SOURCE/KML.H new file mode 100644 index 0000000..c797f87 --- /dev/null +++ b/SOURCE/KML.H @@ -0,0 +1,125 @@ +/* + * kml.h + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ + +#define LEX_BLOCK 0 +#define LEX_COMMAND 1 +#define LEX_PARAM 2 + +typedef enum eTokenId +{ + TOK_NONE, //0 + TOK_ANNUNCIATOR, //1 + TOK_BACKGROUND, //2 + TOK_IFPRESSED, //3 + TOK_RESETFLAG, //4 + TOK_SCANCODE, //5 + TOK_HARDWARE, //6 + TOK_MENUITEM, //7 + TOK_INTEGER, //8 + TOK_SETFLAG, //9 + TOK_RELEASE, //10 + TOK_VIRTUAL, //11 + TOK_INCLUDE, //12 + TOK_STRING, //13 + TOK_GLOBAL, //14 + TOK_AUTHOR, //15 + TOK_BITMAP, //16 + TOK_OFFSET, //17 + TOK_BUTTON, //18 + TOK_IFFLAG, //19 + TOK_ONDOWN, //20 + TOK_NOHOLD, //21 + TOK_TOPBAR, //22 + TOK_TITLE, //23 + TOK_OUTIN, //24 + TOK_PATCH, //25 + TOK_PRINT, //26 + TOK_DEBUG, //27 + TOK_COLOR, //28 + TOK_MODEL, //29 + TOK_CLASS, //30 + TOK_PRESS, //31 + TOK_TYPE, //32 + TOK_SIZE, //33 + TOK_DOWN, //34 + TOK_ZOOM, //35 + TOK_ELSE, //36 + TOK_ONUP, //37 + TOK_EOL, //38 + TOK_MAP, //39 + TOK_ROM, //40 + TOK_VGA, //41 + TOK_LCD, //42 + TOK_NOTFLAG, //43 + TOK_END //44 +} TokenId; + +#define TYPE_NONE 00 +#define TYPE_INTEGER 01 +#define TYPE_STRING 02 + +typedef struct KmlToken +{ + TokenId eId; + DWORD nParams; + DWORD nLen; + TCHAR szName[20]; +} KmlToken; + +typedef struct KmlLine +{ + struct KmlLine* pNext; + TokenId eCommand; + DWORD_PTR nParam[6]; +} KmlLine; + +typedef struct KmlBlock +{ + TokenId eType; + DWORD nId; + struct KmlLine* pFirstLine; + struct KmlBlock* pNext; +} KmlBlock; + +#define BUTTON_NOHOLD 0x0001 +#define BUTTON_VIRTUAL 0x0002 +typedef struct KmlButton +{ + UINT nId; + BOOL bDown; + UINT nType; + DWORD dwFlags; + UINT nOx, nOy; + UINT nDx, nDy; + UINT nCx, nCy; + UINT nOut, nIn; + KmlLine* pOnDown; + KmlLine* pOnUp; +} KmlButton; + +typedef struct KmlAnnunciator +{ + UINT nOx, nOy; + UINT nDx, nDy; + UINT nCx, nCy; +} KmlAnnunciator; + +extern KmlBlock* pKml; +extern BOOL DisplayChooseKml(CHAR cType); +extern VOID FreeBlocks(KmlBlock* pBlock); +extern VOID DrawAnnunciator(UINT nId, BOOL bOn); +extern VOID ReloadButtons(BYTE *Keyboard_Row, UINT nSize); +extern VOID RefreshButtons(RECT *rc); +extern VOID MouseButtonDownAt(UINT nFlags, DWORD x, DWORD y); +extern VOID MouseButtonUpAt(UINT nFlags, DWORD x, DWORD y); +extern VOID MouseMovesTo(UINT nFlags, DWORD x, DWORD y); +extern VOID RunKey(BYTE nId, BOOL bPressed); +extern VOID PlayKey(UINT nOut, UINT nIn, BOOL bPressed); +extern BOOL InitKML(LPCTSTR szFilename, BOOL bNoLog); +extern VOID KillKML(VOID); diff --git a/SOURCE/MOPS.C b/SOURCE/MOPS.C new file mode 100644 index 0000000..a94749b --- /dev/null +++ b/SOURCE/MOPS.C @@ -0,0 +1,1762 @@ +/* + * mops.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "Emu48.h" +#include "ops.h" +#include "opcodes.h" +#include "io.h" +#include "i28f160.h" // flash support + +// #define DEBUG_SERIAL // switch for SERIAL debug purpose +// #define DEBUG_IO // switch for I/O debug purpose +// #define DEBUG_FLASH // switch for FLASH MEMORY debug purpose + +// defines for reading an open data bus +#define READEVEN 0x0D +#define READODD 0x0E + +// on mapping boundary adjusted base addresses +#define P0MAPBASE ((BYTE)(Chipset.P0Base & ~Chipset.P0Size)) +#define P1MAPBASE ((BYTE)(Chipset.P1Base & ~Chipset.P1Size)) +#define P2MAPBASE ((BYTE)(Chipset.P2Base & ~Chipset.P2Size)) +#define BSMAPBASE ((BYTE)(Chipset.BSBase & ~Chipset.BSSize)) + +BOOL bFlashRomArray = TRUE; // flag ROM mode + +BYTE disp = 0; // flag for update display area + +static LPBYTE pbyRomView[2] = {NULL, NULL}; // HP49G ROM views + +// CRC calculation +static WORD crc_table[] = +{ + 0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387, + 0x8408, 0x9489, 0xA50A, 0xB58B, 0xC60C, 0xD68D, 0xE70E, 0xF78F +}; +static __inline VOID UpCRC(BYTE nib) +{ + Chipset.crc = (WORD)((Chipset.crc>>4)^crc_table[(Chipset.crc^nib)&0xf]); +} + +static __inline UINT MIN(UINT a, UINT b) +{ + return (ab)?a:b; +} + +// generate UCK signal +static __inline BYTE UckBit(BYTE byBaudIndex) +{ + // table content = baudrate * 16 + const DWORD dwBaudrates[] = { 19200, 30720, 38400, 61440, 76800, 122880, 153600, 245760 }; + + LARGE_INTEGER lLC; + + _ASSERT(byBaudIndex < ARRAYSIZEOF(dwBaudrates)); + + if ((Chipset.IORam[IOC] & SON) == 0) // UART off + return UCK; // UCK bit always set + + QueryPerformanceCounter(&lLC); // get counter value + + // calculate UCK frequency + return (((BYTE)(((lLC.QuadPart - lAppStart.QuadPart) * dwBaudrates[byBaudIndex]) + / lFreq.QuadPart) & 0x1) << 3); +} + +// calculate nibble based linear flash address +DWORD FlashROMAddr(DWORD d) +{ + DWORD dwLinAddr; + + // 6 bit of latch (was A6-A1 of address bus) + dwLinAddr = (Chipset.Bank_FF >> 1) & 0x3f; + // decode A21-A18 + dwLinAddr = ((d & 0x40000) ? (dwLinAddr & 0xf) : (dwLinAddr >> 4)) << 18; + // decode A21-A18, A17-A0 + dwLinAddr |= d & 0x3FFFF; + return dwLinAddr; +} + +// update display +static __inline VOID UpdateDisplay(DWORD d, UINT s) +{ + BYTE p[16]; + DWORD u; + UINT c; + + // address in display main area? + if ((dChipset.start12)) + { + // write to display main area + u = d; // copy destination ptr + c = MIN(s,Chipset.end1-d); // number of nibbles to copy + + if (d < Chipset.start12) // first address is out of display area + { + u = Chipset.start12; // set destination ptr to start of display area + c -= Chipset.start12 - d; // - number of bytes that aren't in display area + } + + _ASSERT(c <= ARRAYSIZEOF(p)); + Npeek(p,u,c); // get source data + WriteToMainDisplay(p,u,c); + } + // address in display menu area? + if ((dChipset.start2)) + { + // write to display menu area + u = d; // copy destination ptr + c = MIN(s,Chipset.end2-d); // number of nibbles to copy + + if (d < Chipset.start2) // first address is out of display area + { + u = Chipset.start2; // set destination ptr to start of display area + c -= Chipset.start2 - d; // - number of bytes that are not in display area + } + + _ASSERT(c <= ARRAYSIZEOF(p)); + Npeek(p,u,c); // get source data + WriteToMenuDisplay(p,u,c); + } + return; +} + +// port mapping + +LPBYTE RMap[256] = {NULL,}; +LPBYTE WMap[256] = {NULL,}; + +static VOID MapP0(BYTE a, BYTE b) +{ + UINT i; + DWORD p, m; + + a = (BYTE)MAX(a,P0MAPBASE); // adjust base to mapping boundary + b = (BYTE)MIN(b,Chipset.P0End); + m = (Chipset.Port0Size*2048)-1; + p = (a<<12)&m; // offset to begin of P0 in nibbles + for (i=a; i<=b; i++) + { + // mapping area may have holes + if (((i ^ Chipset.P0Base) & ~Chipset.P0Size) == 0) + { + RMap[i]=Chipset.Port0 + p; + WMap[i]=Chipset.Port0 + p; + } + p = (p+0x1000)&m; + } + return; +} + +static VOID MapBS(BYTE a, BYTE b) +{ + UINT i; + + a = (BYTE)MAX(a,BSMAPBASE); // adjust base to mapping boundary + b = (BYTE)MIN(b,Chipset.BSEnd); + for (i=a;i<=b;i++) + { + // mapping area may have holes + if (((i ^ Chipset.BSBase) & ~Chipset.BSSize) == 0) + { + RMap[i]=NULL; // no read cycle, open data bus + WMap[i]=NULL; + } + } + return; +} + +static VOID MapP1(BYTE a, BYTE b) +{ + UINT i; + DWORD p, m; + + // clear mapping area if port1 is configured but not plugged + a = (BYTE)MAX(a,P1MAPBASE); // lowest address for use is P1Base + b = (BYTE)MIN(b,Chipset.P1End); // highest address for use is P1End + + // port1 not plugged + if (Chipset.Port1 == NULL || !(Chipset.cards_status & PORT1_PRESENT)) + { + for (i=a; i<=b; i++) // scan each 2KB page + { + if (((i ^ Chipset.P1Base) & ~Chipset.P1Size) == 0) + { + RMap[i]=NULL; + WMap[i]=NULL; + } + } + return; + } + + m = (Chipset.Port1Size*2048)-1; // real size of module, address mask for mirroring + p = (a<<12)&m; // offset to begin of P1 in nibbles + + if (Chipset.cards_status & PORT1_WRITE) // port1 write enabled + { + for (i=a; i<=b; i++) // scan each 2KB page + { + // mapping area may have holes + if (((i ^ Chipset.P1Base) & ~Chipset.P1Size) == 0) + { + RMap[i]=Chipset.Port1 + p; // save page address for read + WMap[i]=Chipset.Port1 + p; // save page address for write + } + p = (p+0x1000)&m; // next page, mirror page if real size smaller allocated size + } + } + else // port1 read only + { + for (i=a; i<=b; i++) // scan each 2KB page + { + // mapping area may have holes + if (((i ^ Chipset.P1Base) & ~Chipset.P1Size) == 0) + { + RMap[i]=Chipset.Port1 + p; // save page address for read + WMap[i]=NULL; // no writing + } + p = (p+0x1000)&m; // next page, mirror page if real size smaller allocated size + } + } + return; +} + +static VOID MapP2(BYTE a, BYTE b) +{ + UINT i; + DWORD p, m; + LPBYTE pbyTemp; + + // clear mapping area if port2 is configured but not plugged + a = (BYTE)MAX(a,P2MAPBASE); // adjust base to mapping boundary + b = (BYTE)MIN(b,Chipset.P2End); + + if (Chipset.Port2Size) // internal port2 + { + m = (Chipset.Port2Size*2048)-1; + p = (a<<12)&m; // offset to begin of P0 in nibbles + for (i=a; i<=b; i++) + { + // mapping area may have holes + if (((i ^ Chipset.P2Base) & ~Chipset.P2Size) == 0) + { + RMap[i]=Chipset.Port2 + p; + WMap[i]=Chipset.Port2 + p; + } + p = (p+0x1000)&m; + } + return; + } + + // HP48SX / HP48GX + // only fill mapping table when CE2.2 is set + for (i=a; i<=b; i++) // fill mapping area with not configured + { + // mapping area may have holes + if (((i ^ Chipset.P2Base) & ~Chipset.P2Size) == 0) + { + RMap[i]=NULL; + WMap[i]=NULL; + } + } + + // port2 not plugged + if (pbyPort2 == NULL || !(Chipset.cards_status & PORT2_PRESENT)) + return; + + pbyTemp = pbyPort2; + if (cCurrentRomType != 'S') // bank switching only with GX + { + // Chipset.Port2_Bank is the saved port2 FF content + pbyTemp += (((Chipset.Bank_FF>>1)-1)&dwPort2Mask) << 18; + } + + // max. size per bank is 128KB + m = (dwPort2Size > 128) ? 128 : dwPort2Size; + + m = (m * 2048) - 1; // real size of module, address mask for mirroring + p = (a << 12) & m; // offset to begin of P2 in nibbles + + // SX: CE2.2 = CE2 + // GX: CE2.2 = BEN & /DA19 & /NCE3 + if (cCurrentRomType == 'S' || ((Chipset.IORam[0x29]&DA19) == 0 && (Chipset.Bank_FF&0x40))) + { + if (bPort2Writeable) + { + for (i=a; i<=b; i++) + { + // mapping area may have holes + if (((i ^ Chipset.P2Base) & ~Chipset.P2Size) == 0) + { + RMap[i]=pbyTemp + p; + WMap[i]=pbyTemp + p; + } + p = (p+0x1000)&m; + } + } + else + { + for (i=a; i<=b; i++) + { + // mapping area may have holes + if (((i ^ Chipset.P2Base) & ~Chipset.P2Size) == 0) + { + RMap[i]=pbyTemp + p; + } + p = (p+0x1000)&m; + } + } + } + return; +} + +static VOID MapROM(BYTE a, BYTE b) +{ + UINT i; + DWORD p, m; + + // HP39(+)/40G, HP49G(+) HP48Gii // CdB for HP: add apples memory + if (cCurrentRomType == 'E' || cCurrentRomType == 'X' || cCurrentRomType == 'P' || cCurrentRomType == '2' || cCurrentRomType == 'Q') + { + if (bFlashRomArray) // view flash ROM data + { + _ASSERT(pbyRomView[0]); // check ROM bank set + _ASSERT(pbyRomView[1]); + + m = (128*1024*2)-1; // mapped in 128KB pages + p = (a<<12)&m; // offset to the begin of ROM in nibbles + for (i=a; i<=b; i++) // scan each 2KB page + { + RMap[i]=pbyRomView[(i & 0x40)!=0] + p; + WMap[i]=NULL; // no writing + p = (p+0x1000)&m; + } + } + else // view flash ROM register + { + for (i=a; i<=b; i++) // scan each 2KB page + { + RMap[i]=NULL; // view flash register + WMap[i]=NULL; // no writing + } + } + return; + } + + // HP38G / HP48SX / HP48GX + m = dwRomSize - 1; // ROM address mask for mirroring + // when 512KB ROM and DA19=0 (ROM disabled) + if ((m & 0x80000) != 0 && (Chipset.IORam[0x29]&DA19) == 0) + m >>= 1; // mirror ROM at #80000 (AR18=0) + p = (a*0x1000)&m; // data offset in nibbles + for (i=a;i<=b;i++) // scan each 2KB page + { + RMap[i]=pbyRom + p; // save page address for read + WMap[i]=NULL; // no writing + p = (p+0x1000)&m; // next page, mirror page if real size smaller allocated size + } + return; +} + +VOID Map(BYTE a, BYTE b) // maps 2KB pages with priority +{ + // On HP39/40G and HP49G Chipset.cards_status must be 0xF + _ASSERT((cCurrentRomType!='E' && cCurrentRomType!='X' && cCurrentRomType!='P' && cCurrentRomType!='2' && cCurrentRomType!='Q') || !Chipset.P1Cfig || Chipset.cards_status == 0xF); // CdB for HP: add apples + + // priority order is HDW, RAM, CE2, CE1, NCE3, ROM + MapROM(a,b); // ROM, lowest priority, always mapped + if (cCurrentRomType == 'S') // HP48SX + { + if (Chipset.BSCfig) MapBS(a,b); // NCE3, not used in S(X) + if (Chipset.P1Cfig) MapP1(a,b); // CE1, port1 (lower priority than CE2) + if (Chipset.P2Cfig) MapP2(a,b); // CE2, port2 (higher priority than CE1) + } + else // HP48GX / HP49G + { + if (Chipset.P2Cfig) // NCE3, port2 + { + // LED bit set on a HP49 + if ((cCurrentRomType=='X' || cCurrentRomType=='Q') && (Chipset.IORam[LCR]&LED)) // CdB for HP: add apples + MapROM(a,b); // NCE3, ROM + else + MapP2(a,b); // NCE3, port2 + } + if (Chipset.BSCfig) MapBS(a,b); // CE1, bank select (lower priority than CE2) + if (Chipset.P1Cfig) MapP1(a,b); // CE2, port1 (higher priority than CE1) + } + if (Chipset.P0Cfig) MapP0(a,b); // RAM, highest priority (execpt HDW) + // CdB for HP: add apples header +// @todo cg, bug if display header area is mapped to addr 0 + if (Chipset.d0address!=0) + { + RMap[Chipset.d0address]=&(Chipset.d0memory[0]); RMap[Chipset.d0address+1]=&(Chipset.d0memory[2048*2]); + WMap[Chipset.d0address]=&(Chipset.d0memory[0]); WMap[Chipset.d0address+1]=&(Chipset.d0memory[2048*2]); + } + return; +} + +VOID RomSwitch(DWORD adr) +{ + // only HP39/40G, HP49G + if (cCurrentRomType=='E' || cCurrentRomType=='X' || cCurrentRomType=='P' || cCurrentRomType=='2' || cCurrentRomType=='Q') // CdB for HP: add apples + { + Chipset.Bank_FF = adr; // save address line + adr = (adr >> 1) & 0x3f; // 6 bit of latch (was A6-A1 of address bus) + // lower 4 bit (16 banks) for 2nd ROM view + pbyRomView[1] = pbyRom + (((adr & 0xf) * 128 * 1024 * 2) & (dwRomSize - 1)); + // higher 2 bit (4 banks) for 1st ROM view + pbyRomView[0] = pbyRom + (((adr >> 4) * 128 * 1024 * 2) & (dwRomSize - 1)); + } + Map(0x00,0xFF); // update memory mapping + return; +} + +//////////////////////////////////////////////////////////////////////////////// +// +// Bus Commands +// +//////////////////////////////////////////////////////////////////////////////// + +VOID Config() // configure modules in fixed order +{ + DWORD d = Npack(Chipset.C,5); // decode size or address + BYTE b = (BYTE)(d>>12); // number of 2KB pages or page address + BYTE s = (BYTE)(b^0xFF); // size in pages-1, offset to last page + + // config order is HDW, RAM, CE1, CE2, NCE3 + if (!Chipset.IOCfig) // address of HDW, first module, ROM always configured + { + Chipset.IOCfig = TRUE; + Chipset.IOBase = d&0xFFFC0; // save HDW base on a 64 nib boundary + Map(b,b); + return; + } + if (!Chipset.P0Cfg2) // RAM size, port0 + { + Chipset.P0Cfg2 = TRUE; + Chipset.P0Size = s; // offset to last used page + return; + } + if (!Chipset.P0Cfig) // RAM address, port0 + { + Chipset.P0Cfig = TRUE; + Chipset.P0Base = b; // save first page address + b &= ~Chipset.P0Size; // adjust base to mapping boundary + Chipset.P0End = b+Chipset.P0Size; // save last page address + Map(b,Chipset.P0End); // refresh mapping + return; + } + if (cCurrentRomType=='S') // HP48SX + { + if (!Chipset.P1Cfg2) // CE1 size, port1 + { + Chipset.P1Cfg2 = TRUE; + Chipset.P1Size = s; + return; + } + if (!Chipset.P1Cfig) // CE1 address, port1 + { + Chipset.P1Cfig = TRUE; + Chipset.P1Base = b; + b &= ~Chipset.P1Size; // adjust base to mapping boundary + Chipset.P1End = b+Chipset.P1Size; + Map(b,Chipset.P1End); // refresh mapping + return; + } + if (!Chipset.P2Cfg2) // CE2 size, port2 + { + Chipset.P2Cfg2 = TRUE; + Chipset.P2Size = s; + return; + } + if (!Chipset.P2Cfig) // CE2 address, port2 + { + Chipset.P2Cfig = TRUE; + Chipset.P2Base = b; + b &= ~Chipset.P2Size; // adjust base to mapping boundary + Chipset.P2End = b+Chipset.P2Size; + Map(b,Chipset.P2End); // refresh mapping + return; + } + if (!Chipset.BSCfg2) // NCE3 size, not used in S(X) + { + Chipset.BSCfg2 = TRUE; + Chipset.BSSize = s; + return; + } + if (!Chipset.BSCfig) // NCE3 address, not used in S(X) + { + Chipset.BSCfig = TRUE; + Chipset.BSBase = b; + b &= ~Chipset.BSSize; // adjust base to mapping boundary + Chipset.BSEnd = b+Chipset.BSSize; + Map(b,Chipset.BSEnd); // refresh mapping + return; + } + } + else // HP48GX / HP49G + { + if (!Chipset.BSCfg2) // CE1 size, bank select + { + Chipset.BSCfg2 = TRUE; + Chipset.BSSize = s; + return; + } + if (!Chipset.BSCfig) // CE1 address, bank select + { + Chipset.BSCfig = TRUE; + Chipset.BSBase = b; + b &= ~Chipset.BSSize; // adjust base to mapping boundary + Chipset.BSEnd = b+Chipset.BSSize; + Map(b,Chipset.BSEnd); // refresh mapping + return; + } + if (!Chipset.P1Cfg2) // CE2 size, port1 + { + Chipset.P1Cfg2 = TRUE; + Chipset.P1Size = s; + return; + } + if (!Chipset.P1Cfig) // CE2 address, port1 + { + Chipset.P1Cfig = TRUE; + Chipset.P1Base = b; + b &= ~Chipset.P1Size; // adjust base to mapping boundary + Chipset.P1End = b+Chipset.P1Size; + Map(b,Chipset.P1End); // refresh mapping + return; + } + if (!Chipset.P2Cfg2) // NCE3 size, port2 + { + Chipset.P2Cfg2 = TRUE; + Chipset.P2Size = s; + return; + } + if (!Chipset.P2Cfig) // NCE3 address, port2 + { + Chipset.P2Cfig = TRUE; + Chipset.P2Base = b; + b &= ~Chipset.P2Size; // adjust base to mapping boundary + Chipset.P2End = b+Chipset.P2Size; + Map(b,Chipset.P2End); // refresh mapping + return; + } + } + return; +} + +VOID Uncnfg() +{ + DWORD d=Npack(Chipset.C,5); // decode address + BYTE b=(BYTE)(d>>12); // page address + + // unconfig order is HDW, RAM, CE2, CE1, NCE3 + if ((Chipset.IOCfig)&&((d&0xFFFC0)==Chipset.IOBase)) + {Chipset.IOCfig=FALSE;Map(b,b);return;} + if ((Chipset.P0Cfig)&&((b&~Chipset.P0Size)==P0MAPBASE)) + {Chipset.P0Cfig=FALSE;Chipset.P0Cfg2=FALSE;Map(P0MAPBASE,Chipset.P0End);return;} + if (cCurrentRomType=='S') // HP48SX + { + if ((Chipset.P2Cfig)&&((b&~Chipset.P2Size)==P2MAPBASE)) + {Chipset.P2Cfig=FALSE;Chipset.P2Cfg2=FALSE;Map(P2MAPBASE,Chipset.P2End);return;} + if ((Chipset.P1Cfig)&&((b&~Chipset.P1Size)==P1MAPBASE)) + {Chipset.P1Cfig=FALSE;Chipset.P1Cfg2=FALSE;Map(P1MAPBASE,Chipset.P1End);return;} + if ((Chipset.BSCfig)&&((b&~Chipset.BSSize)==BSMAPBASE)) + {Chipset.BSCfig=FALSE;Chipset.BSCfg2=FALSE;Map(BSMAPBASE,Chipset.BSEnd);return;} + } + else // HP48GX / HP49G + { + if ((Chipset.P1Cfig)&&((b&~Chipset.P1Size)==P1MAPBASE)) + {Chipset.P1Cfig=FALSE;Chipset.P1Cfg2=FALSE;Map(P1MAPBASE,Chipset.P1End);return;} + if ((Chipset.BSCfig)&&((b&~Chipset.BSSize)==BSMAPBASE)) + {Chipset.BSCfig=FALSE;Chipset.BSCfg2=FALSE;Map(BSMAPBASE,Chipset.BSEnd);return;} + if ((Chipset.P2Cfig)&&((b&~Chipset.P2Size)==P2MAPBASE)) + {Chipset.P2Cfig=FALSE;Chipset.P2Cfg2=FALSE;Map(P2MAPBASE,Chipset.P2End);return;} + } + return; +} + +VOID Reset() +{ + Chipset.IOCfig=FALSE;Chipset.IOBase=0x100000; + Chipset.P0Cfig=FALSE;Chipset.P0Cfg2=FALSE;Chipset.P0Base=0;Chipset.P0Size=0;Chipset.P0End=0; + Chipset.BSCfig=FALSE;Chipset.BSCfg2=FALSE;Chipset.BSBase=0;Chipset.BSSize=0;Chipset.BSEnd=0; + Chipset.P1Cfig=FALSE;Chipset.P1Cfg2=FALSE;Chipset.P1Base=0;Chipset.P1Size=0;Chipset.P1End=0; + Chipset.P2Cfig=FALSE;Chipset.P2Cfg2=FALSE;Chipset.P2Base=0;Chipset.P2Size=0;Chipset.P2End=0; + Map(0x00,0xFF); // refresh mapping + return; +} + +VOID C_Eq_Id() +{ + // config order is HDW, RAM, CE1, CE2, NCE3 + if (!Chipset.IOCfig) {Nunpack(Chipset.C,(Chipset.IOBase) ^0x00019,5);return;} + if (!Chipset.P0Cfg2) {Nunpack(Chipset.C,(Chipset.P0Size*0x1000)^0xFF003,5);return;} + if (!Chipset.P0Cfig) {Nunpack(Chipset.C,(Chipset.P0Base*0x1000)^0x000F4,5);return;} + if (cCurrentRomType=='S') // HP48SX + { + if (!Chipset.P1Cfg2) {Nunpack(Chipset.C,(Chipset.P1Size*0x1000)^0xFF005,5);return;} + if (!Chipset.P1Cfig) {Nunpack(Chipset.C,(Chipset.P1Base*0x1000)^0x000F6,5);return;} + if (!Chipset.P2Cfg2) {Nunpack(Chipset.C,(Chipset.P2Size*0x1000)^0xFF007,5);return;} + if (!Chipset.P2Cfig) {Nunpack(Chipset.C,(Chipset.P2Base*0x1000)^0x000F8,5);return;} + if (!Chipset.BSCfg2) {Nunpack(Chipset.C,(Chipset.BSSize*0x1000)^0xFF001,5);return;} + if (!Chipset.BSCfig) {Nunpack(Chipset.C,(Chipset.BSBase*0x1000)^0x000F2,5);return;} + } + else // HP48GX / HP49G + { + if (!Chipset.BSCfg2) {Nunpack(Chipset.C,(Chipset.BSSize*0x1000)^0xFF005,5);return;} + if (!Chipset.BSCfig) {Nunpack(Chipset.C,(Chipset.BSBase*0x1000)^0x000F6,5);return;} + if (!Chipset.P1Cfg2) {Nunpack(Chipset.C,(Chipset.P1Size*0x1000)^0xFF007,5);return;} + if (!Chipset.P1Cfig) {Nunpack(Chipset.C,(Chipset.P1Base*0x1000)^0x000F8,5);return;} + if (!Chipset.P2Cfg2) {Nunpack(Chipset.C,(Chipset.P2Size*0x1000)^0xFF001,5);return;} + if (!Chipset.P2Cfig) {Nunpack(Chipset.C,(Chipset.P2Base*0x1000)^0x000F2,5);return;} + } + memset(Chipset.C,0,5); + return; +} + +enum MMUMAP MapData(DWORD d) // check MMU area +{ + BYTE u = (BYTE) (d>>12); + + if (Chipset.IOCfig && ((d&0xFFFC0)==Chipset.IOBase)) return M_IO; + if (Chipset.P0Cfig && (((u^Chipset.P0Base) & ~Chipset.P0Size) == 0)) return M_RAM; + if (cCurrentRomType == 'S') + { + if (Chipset.P2Cfig && (((u^Chipset.P2Base) & ~Chipset.P2Size) == 0)) return M_P2; + if (Chipset.P1Cfig && (((u^Chipset.P1Base) & ~Chipset.P1Size) == 0)) return M_P1; + if (Chipset.BSCfig && (((u^Chipset.BSBase) & ~Chipset.BSSize) == 0)) return M_BS; + } + else + { + if (Chipset.P1Cfig && (((u^Chipset.P1Base) & ~Chipset.P1Size) == 0)) return M_P1; + if (Chipset.BSCfig && (((u^Chipset.BSBase) & ~Chipset.BSSize) == 0)) return M_BS; + if (Chipset.P2Cfig && (((u^Chipset.P2Base) & ~Chipset.P2Size) == 0)) return M_P2; + } + return M_ROM; +} + +VOID CpuReset(VOID) // register setting after Cpu Reset +{ + StopTimers(); // stop timer, do here because function change Chipset.t2 + + Chipset.pc = 0; + Chipset.rstkp = 0; + ZeroMemory(Chipset.rstk,sizeof(Chipset.rstk)); + Chipset.HST = 0; + Chipset.SoftInt = FALSE; + Chipset.Shutdn = TRUE; + Chipset.inte = TRUE; // enable interrupts + Chipset.intk = TRUE; // INTON + Chipset.intd = FALSE; // no keyboard interrupts pending + Chipset.crc = 0; + Chipset.Bank_FF = 0; // state of bank switcher FF + Chipset.FlashRomState = 0; // WSM state of flash memory + ZeroMemory(Chipset.IORam,sizeof(Chipset.IORam)); + Chipset.IORam[LPE] = RST; // set ReSeT bit at hardware reset + Reset(); // reset MMU + Chipset.t1 = 0; // reset timer values + Chipset.t2 = 0; + Chipset.loffset = 0; // right margin + Chipset.boffset = 0; // left margin + Chipset.lcounter = 0; // number of main display lines + Chipset.contrast = 0; // contrast dark + + UpdateContrast(Chipset.contrast); // update contrast + // display update when changing to run state + CommSetBaud(); // new baudrate + CheckSerial(); // close serial port + + RomSwitch(Chipset.Bank_FF); // force new memory mapping + return; +} + +VOID Npeek(BYTE *a, DWORD d, UINT s) +{ + enum MMUMAP eMap; + DWORD u, v; + UINT c; + BYTE *p; + + do + { + eMap = MapData(d); // get active memory controller + if (M_IO == eMap) // I/O access + { + v = d&0x3F; + + do + { + if (v == LPE) + { + // don't read LPE content with the function ReadIO() + c = 1; + memcpy(a, Chipset.IORam+v, c); + break; + } + if (v >= RBR_LSB && v <= RBR_MSB) + { + // don't read RBR content with the function ReadIO() + c = MIN(s,RBR_MSB-v+1); + memcpy(a, Chipset.IORam+v, c); + break; + } + // all others registers + do + { + if (v < LPE) + { + c = MIN(s,LPE-v); + break; + } + if (v < RBR_LSB && (v+s) > RBR_LSB) + { + c = MIN(s,RBR_LSB-v); + break; + } + c = MIN(s,0x40-v); + } + while (0); + ReadIO(a,v,c,FALSE); + } + while (0); + } + else + { + u = d>>12; + v = d&0xFFF; + c = MIN(s,0x1000-v); + // Flash memory Read access + if ((cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='P' || cCurrentRomType=='Q') && (Chipset.IORam[LCR] & LED) && M_P2 == eMap) // CdB for HP: add apples + { + FlashRead(a, FlashROMAddr(d), c); + } + else + { + if ((p=RMap[u]) != NULL) // module mapped + { + memcpy(a, p+v, c); + } + else // open data bus + { + for (u=0; u>12; + v = d&0xFFF; + c = MIN(s,0x1000-v); + // bank switcher access + if (cCurrentRomType!='S' && M_BS == eMap) + { + if (cCurrentRomType=='G') // HP48GX + { + Chipset.Bank_FF = v+c; // save FF value + Map(Chipset.P2Base,Chipset.P2End); + } + if (cCurrentRomType=='E' || cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='P' || cCurrentRomType=='Q') // HP39/40G, HP49G // CdB for HP: add apples + { + RomSwitch(v+c); + } + } + // Flash memory Read access + if ((cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='P' || cCurrentRomType=='Q') && (Chipset.IORam[LCR] & LED) && M_P2 == eMap) // CdB for HP: add apples + { + DWORD dwLinAddr = FlashROMAddr(d); + + FlashRead(a, dwLinAddr, c); + + #if defined DEBUG_FLASH + { + TCHAR buffer[256]; + DWORD j; + int i; + + i = wsprintf(buffer,_T("%.5lx: Flash Read : %.5x (%.6x),%u = "),Chipset.pc,d,dwLinAddr,c); + for (j = 0;j < c;++j,++i) + { + buffer[i] = a[j]; + if (buffer[i] > 9) buffer[i] += _T('a') - _T('9') - 1; + buffer[i] += _T('0'); + } + buffer[i++] = _T('\n'); + buffer[i] = 0; + OutputDebugString(buffer); + } + #endif + } + else + { + if ((p=RMap[u]) != NULL) // module mapped + { + memcpy(a, p+v, c); + } + // simulate open data bus + else // open data bus + { + for (u=0; u>12; + v = d&0xFFF; + c = MIN(s,0x1000-v); + // bank switcher access + if (cCurrentRomType!='S' && M_BS == eMap) + { + BOOL bWrite = FALSE; + + // write enabled + if (Chipset.cards_status & PORT2_WRITE) + { + Chipset.Bank_FF = v+c-1;// save FF value + bWrite = TRUE; // bank switched + } + else // write disabled, so latch last read cycle + { + if ((v & 1) != 0) // low address odd + { + Chipset.Bank_FF = v;// save FF value + bWrite = TRUE; // bank switched + } + + if (((v+c) & 1) != 0) // high address odd + { + Chipset.Bank_FF = v+c-1;// save FF value + bWrite = TRUE; // bank switched + } + } + + if (bWrite) // write cycle? + { + // HP48GX + if (cCurrentRomType=='G') Map(Chipset.P2Base,Chipset.P2End); + // HP39/40G, HP49G + if (cCurrentRomType=='E' || cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='P' || cCurrentRomType=='Q') RomSwitch(Chipset.Bank_FF); // CdB for HP: add apples + } + } + // Flash memory Write access + if ((cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='P' || cCurrentRomType=='Q') && (Chipset.IORam[LCR] & LED) && M_P2 == eMap) // CdB for HP: add apples + { + DWORD dwLinAddr = FlashROMAddr(d); + + FlashWrite(a, dwLinAddr, c); + + #if defined DEBUG_FLASH + { + TCHAR buffer[256]; + DWORD j; + int i; + + i = wsprintf(buffer,_T("%.5lx: Flash Write: %.5x (%.6x),%u = "),Chipset.pc,d,dwLinAddr,c); + for (j = 0;j < c;++j,++i) + { + buffer[i] = a[j]; + if (buffer[i] > 9) buffer[i] += _T('a') - _T('9') - 1; + buffer[i] += _T('0'); + } + buffer[i++] = _T('\n'); + buffer[i] = 0; + OutputDebugString(buffer); + } + #endif + } + else + { + if ((p=WMap[u]) != NULL) memcpy(p+v, a, c); + } + } + if (!bGrayscale) + UpdateDisplay(d, c); // update display + a+=c; + d=(d+c)&0xFFFFF; + } while (s-=c); + return; +} + +DWORD Read5(DWORD d) +{ + BYTE p[5]; + + Npeek(p,d,5); + return Npack(p,5); +} + +BYTE Read2(DWORD d) +{ + BYTE p[2]; + + Npeek(p,d,2); + return (BYTE)(p[0]|(p[1]<<4)); +} + +VOID Write5(DWORD d, DWORD n) +{ + BYTE p[5]; + + Nunpack(p,n,5); + Nwrite(p,d,5); + return; +} + +VOID Write2(DWORD d, BYTE n) +{ + BYTE p[2]; + + Nunpack(p,n,2); + Nwrite(p,d,2); + return; +} + +VOID IOBit(DWORD d, BYTE b, BOOL s) // set/clear bit in I/O section +{ + EnterCriticalSection(&csIOLock); + { + if (s) + Chipset.IORam[d] |= b; // set bit + else + Chipset.IORam[d] &= ~b; // clear bit + } + LeaveCriticalSection(&csIOLock); +} + +VOID ReadIO(BYTE *a, DWORD d, DWORD s, BOOL bUpdate) +{ + BOOL bNINT; + BOOL bNINT2; + + BYTE c = 0xFF; // LINECOUNT not initialized + BOOL rbr_acc = FALSE; // flag to receive data + + #if defined DEBUG_IO + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: IO read : %02x,%u\n"),Chipset.pc,d,s); + OutputDebugString(buffer); + } + #endif + + do + { + switch (d) + { + case 0x00: *a = (Chipset.IORam[d]&DON)|Chipset.boffset; break; + case 0x01: *a = Chipset.contrast&0xF; break; + case 0x02: *a = Chipset.contrast>>4; break; + case 0x03: *a = 0; + case 0x04: *a = (Chipset.crc )&0xF; break; + case 0x05: *a = (Chipset.crc>> 4)&0xF; break; + case 0x06: *a = (Chipset.crc>> 8)&0xF; break; + case 0x07: *a = (Chipset.crc>>12)&0xF; break; + case 0x08: *a = 0; break; + case 0x09: // LPE + *a = Chipset.IORam[d]; + if (bUpdate) + { + Chipset.IORam[d] &= ~RST; // clear RST bit after reading + } + break; + case 0x0A: *a = 0; break; +// case 0x0B: *a = Chipset.IORam[d]; break; +// case 0x0C: *a = Chipset.IORam[d]; break; + case 0x0D: // BAUD + *a = Chipset.IORam[d] & 0x7; + #if defined DEBUG_SERIAL // return BAUD value + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: BAUD Read: %x\n"),Chipset.pc,*a); + OutputDebugString(buffer); + } + #endif + *a |= UckBit(*a); // add UCK bit to BAUD rate register + break; + case 0x0E: + // SMP is !NINT and SWINT is always 0 + // clear SMP and SWINT bit + Chipset.IORam[d] &= (ECDT | RCDT); + // SMP is !NINT + if ((Chipset.IORam[SRQ2] & NINT) == 0) + Chipset.IORam[d] |= SMP; + *a = Chipset.IORam[d]; + break; + case 0x0F: + // card detection disabled + if ((Chipset.IORam[CARDCTL] & ECDT) == 0) + { + *a = 0; // no cards + } + else + { + // on a HP39/40G and HP49G Chipset.cards_status bust always be 0xF +// _ASSERT((cCurrentRomType!='E' && cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='P' && cCurrentRomType!='Q') || Chipset.cards_status == 0xF); // CdB for HP: add apples + *a = Chipset.cards_status; + } + break; + case 0x10: // IO CONTROL + *a = Chipset.IORam[d]; // return IO CONTROL value + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: IOC Read: %x\n"),Chipset.pc,*a); + OutputDebugString(buffer); + } + #endif + break; + case 0x11: // RCS + *a = Chipset.IORam[d] | RX; // return RCS value + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: RCS Read: %x\n"),Chipset.pc,*a); + OutputDebugString(buffer); + } + #endif + break; + case 0x12: // TCS + *a = Chipset.IORam[d]; // return TCS value + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: TCS Read: %x\n"),Chipset.pc,*a); + OutputDebugString(buffer); + } + #endif + break; + case 0x13: // CRER + *a = 0; + break; + case 0x14: // RBR LSB + case 0x15: // RBR MSB + if (bUpdate) + { + Chipset.IORam[RCS] &= ~RBF; + *a = Chipset.IORam[d]; // return RBR value + UpdateUSRQ(); // update USRQ + rbr_acc = TRUE; // search for new RBR value + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: RBR %s Read: %x\n"),Chipset.pc,(d==0x14) ? "LSB" : "MSB",*a); + OutputDebugString(buffer); + } + #endif + } + else + { + *a = Chipset.IORam[d]; // return RBR value + UpdateUSRQ(); // update USRQ + } + break; +// case 0x16: *a = Chipset.IORam[d]; break; // TBR LSB +// case 0x17: *a = Chipset.IORam[d]; break; // TBR MSB + case 0x19: // SREQ? MSB + UpdateKdnBit(); // update KDN bit + bNINT2 = Chipset.IORam[SRQ1] == 0 && (Chipset.IORam[SRQ2] & LSRQ) == 0; + bNINT = (Chipset.IORam[SRQ2] & NINT) != 0; + // card detection off and timer running + if ((Chipset.IORam[CARDCTL] & ECDT) == 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0) + { + // state of CDT2 + bNINT2 = bNINT2 && (Chipset.cards_status & (P2W|P2C)) != P2C; + // state of CDT1 + bNINT = bNINT && (Chipset.cards_status & (P1W|P1C)) != P1C; + } + IOBit(SRQ2,NINT2,bNINT2); + IOBit(SRQ2,NINT,bNINT); + // no break! + case 0x18: // SREQ? LSB + *a = Chipset.IORam[d]; // return SREQ value + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: SEQ %s Read: %x\n"),Chipset.pc,(d==0x18) ? "LSB" : "MSB",*a); + OutputDebugString(buffer); + } + #endif + break; + case 0x1A: // IR CONTROL + if (cCurrentRomType=='E') // HP39/40G + { + Chipset.IORam[d] = (nCurrentClass != 40) + ? (Chipset.IORam[d] & ~IRI) // HP39G + : (Chipset.IORam[d] | IRI); // HP40G + } + *a = Chipset.IORam[d]; // return IR CONTROL value + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: IRC Read: %x\n"),Chipset.pc,*a); + OutputDebugString(buffer); + } + #endif + break; + case 0x1B: *a = 0; break; + case 0x1C: *a = 0; break; // LED CONTROL + case 0x1D: *a = 0; break; // LED BUFFER +// case 0x1E: *a = Chipset.IORam[d]; break; +// case 0x1F: *a = Chipset.IORam[d]; break; + case 0x20: *a = 3; break; + case 0x21: *a = 3; break; + case 0x22: *a = 3; break; + case 0x23: *a = 3; break; + case 0x24: *a = 3; break; + case 0x25: *a = 3; break; + case 0x26: *a = 3; break; + case 0x27: *a = 3; break; + case 0x28: // LINECOUNT LSB + case 0x29: // LINECOUNT MSB + DA19 M32 + if (Chipset.IORam[0x00]&DON) // display on + { + if (c == 0xFF) // no actual line information + { + c = GetLineCounter(); // get LCD update line + // save line information in IO registers + Chipset.IORam[0x28] = c & 0xF; + Chipset.IORam[0x29] = (Chipset.IORam[0x29] & (DA19|M32)) | (c >> 4); + } + } + *a = Chipset.IORam[d]; + + if (d==0x29) // address 0x29 is mirrored to 0x2A-0x2D + { + Chipset.IORam[0x2A] = *a; + Chipset.IORam[0x2B] = *a; + Chipset.IORam[0x2C] = *a; + Chipset.IORam[0x2D] = *a; + } + break; +// case 0x2A: *a = 0; break; +// case 0x2B: *a = 0; break; +// case 0x2C: *a = 0; break; +// case 0x2D: *a = 0; break; + case 0x2E: + ReadT1(); // dummy read for update timer1 control register + *a = Chipset.IORam[d]; + break; + case 0x2F: + ReadT2(); // dummy read for update timer2 control register + *a = Chipset.IORam[d]; + break; + case 0x30: *a = 3; break; + case 0x31: *a = 3; break; + case 0x32: *a = 3; break; + case 0x33: *a = 3; break; + case 0x34: *a = 3; break; + case 0x35: *a = 0; break; + case 0x36: *a = 0; break; + case 0x37: *a = ReadT1(); break; + case 0x38: Nunpack(a, ReadT2() , s); return; + case 0x39: Nunpack(a, ReadT2()>> 4, s); return; + case 0x3A: Nunpack(a, ReadT2()>> 8, s); return; + case 0x3B: Nunpack(a, ReadT2()>>12, s); return; + case 0x3C: Nunpack(a, ReadT2()>>16, s); return; + case 0x3D: Nunpack(a, ReadT2()>>20, s); return; + case 0x3E: Nunpack(a, ReadT2()>>24, s); return; + case 0x3F: Nunpack(a, ReadT2()>>28, s); return; + default: *a = Chipset.IORam[d]; + } + d++; a++; + } while (--s); + if (rbr_acc) CommReceive(); // look for new character + return; +} + +VOID WriteIO(BYTE *a, DWORD d, DWORD s) +{ + DWORD b; + BYTE c; + BOOL tbr_acc = FALSE; // flag to transmit data + BOOL bDISPADDR = FALSE; // flag addr 0x120-0x124 changed + BOOL bLINEOFFS = FALSE; // flag addr 0x125-0x127 changed + BOOL bMENUADDR = FALSE; // flag addr 0x130-0x134 changed + + #if defined DEBUG_IO + { + TCHAR buffer[256]; + DWORD j; + int i; + + i = wsprintf(buffer,_T("%.5lx: IO write: %02x,%u = "),Chipset.pc,d,s); + for (j = 0;j < s;++j,++i) + { + buffer[i] = a[j]; + if (buffer[i] > 9) buffer[i] += _T('a') - _T('9') - 1; + buffer[i] += _T('0'); + } + buffer[i++] = _T('\n'); + buffer[i] = 0; + OutputDebugString(buffer); + } + #endif + + do + { + c = *a; + switch (d) + { +// 00100 = NS:DISPIO +// 00100 @ Display bit offset and DON [DON OFF2 OFF1 OFF0] +// 00100 @ 3 nibs for display offset (scrolling), DON=Display ON + case 0x00: + if ((c^Chipset.IORam[d])&DON) // DON bit changed + { + disp |= (DISP_POINTER | DISP_MAIN | DISP_MENUE); + + // adjust VBL counter start/stop values + if ((c & DON) != 0) // set display on + { + Chipset.IORam[d] |= DON; // for StartDisplay(); + UpdateContrast(Chipset.contrast); + StartDisplay((BYTE) Chipset.lcounter); // start display update + } + else // display is off + { + Chipset.IORam[d] &= ~DON; + UpdateContrast(Chipset.contrast); + StopDisplay(); // stop display update + } + } + // OFF bits changed + if ((c^Chipset.IORam[d]) & (OFF2 | OFF1 | OFF0)) + { + Chipset.boffset = c & (OFF2 | OFF1 | OFF0); + disp |= (DISP_POINTER | DISP_MAIN); + } + Chipset.IORam[d] = c; + break; + +// 00101 = NS:CONTRLSB +// 00101 @ Contrast Control [CON3 CON2 CON1 CON0] +// 00101 @ Higher value = darker screen + case 0x01: + if (c!=Chipset.IORam[d]) + { + Chipset.IORam[d]=c; + Chipset.contrast = (Chipset.contrast&0x10)|c; + UpdateContrast(Chipset.contrast); + disp |= (DISP_MAIN | DISP_MENUE); + } + break; + +// 00102 = NS:DISPTEST +// 00102 @ Display test [VDIG LID TRIM CON4] [LRT LRTD LRTC BIN] +// 00102 @ Normally zeros + case 0x02: + if (c!=Chipset.IORam[d]) + { + Chipset.IORam[d]=c; + Chipset.contrast = (Chipset.contrast&0x0f)|((c&1)<<4); + UpdateContrast(Chipset.contrast); + disp |= (DISP_MAIN | DISP_MENUE); + } + break; + + case 0x03: Chipset.IORam[d]=c; break; + +// 00104 = HP:CRC +// 00104 @ 16 bit hardware CRC (104-107) (X^16+X^12+X^5+1) +// 00104 @ crc = ( crc >> 4 ) ^ ( ( ( crc ^ nib ) & 0x000F ) * 0x1081 ); + case 0x04: Chipset.crc = (Chipset.crc&0xfff0)|(c*0x0001); break; + case 0x05: Chipset.crc = (Chipset.crc&0xff0f)|(c*0x0010); break; + case 0x06: Chipset.crc = (Chipset.crc&0xf0ff)|(c*0x0100); break; + case 0x07: Chipset.crc = (Chipset.crc&0x0fff)|(c*0x1000); break; + +// 00108 = NS:POWERSTATUS +// 00108 @ Low power registers (108-109) +// 00108 @ [LB2 LB1 LB0 VLBI] (read only) +// 00108 @ LowBat(2) LowBat(1) LowBat(S) VeryLowBat + case 0x08: break; // read-only + +// 00109 = NS:POWERCTRL +// 00109 @ [ELBI EVLBI GRST RST] (read/write) + case 0x09: + Chipset.IORam[d]=c; + if (c & RST) + { + CpuReset(); // emulate NRES signal + disp |= (DISP_POINTER | DISP_MAIN | DISP_MENUE | DISP_ANNUN); + bInterrupt = TRUE; // SHUTDN + } + break; + +// 0010A = NS:MODE +// 0010A @ Mode Register (read-only) + case 0x0A: break; // read-only + +// 0010B = HP:ANNCTRL +// 0010B @ Annunciator control [LA4 LA3 LA2 LA1] = [ alarm alpha -> <- ] + case 0x0B: + case 0x0C: + if (c!=Chipset.IORam[d]) + { + Chipset.IORam[d] = c; + disp |= DISP_ANNUN; + } + break; + +// 0010D = NS:BAUD +// 0010D @ Serial baud rate [UCK BD2 BD1 BD0] (bit 3 is read-only) +// 0010D @ 3 bits = {1200 1920 2400 3840 4800 7680 9600 15360} + case 0x0D: + Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); // bit 3 is read-only + CommSetBaud(); // set baudrate + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: BAUD write: %x\n"),Chipset.pc,Chipset.IORam[d]); + OutputDebugString(buffer); + } + #endif + break; + +// 0010E = NS:CARDCTL +// 0010E @ [ECDT RCDT SMP SWINT] (read/write) +// 0010E @ Enable Card Det., Run Card Det., Set Module Pulled, Software interrupt + case 0x0E: + if (c & SWINT) // SWINT bit set + { + c &= (ECDT | RCDT | SMP); // clear SWINT bit + Chipset.SoftInt = TRUE; + bInterrupt = TRUE; + } + if ((c & SMP) == 0) // SMP bit cleared + { + BOOL bNINT = TRUE; // ack NINT interrupt -> NINT high + // card detect disabled and CDT1 low -> retrigger + if ((c & ECDT) == 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0) + bNINT = (Chipset.cards_status & (P1W|P1C)) != P1C; + IOBit(SRQ2,NINT,bNINT); + } + // falling edge of Enable Card Detect bit and timer running + if ( ((c^Chipset.IORam[d]) & ECDT) != 0 && (c & ECDT) == 0 + && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0) + { + BOOL bNINT = (Chipset.IORam[SRQ2] & NINT) != 0; + // card in slot1 isn't Read Only + if ((Chipset.cards_status & (P1W|P1C)) != P1C) + { + // use random state of NINT line + bNINT = bNINT && (ReadT2() & 0x1) != 0; + } + IOBit(SRQ2,NINT,bNINT); + + Chipset.HST |= MP; // set Module Pulled + + // Port1 and Port2 plugged and writeable or NINT2/NINT interrupt + if ( Chipset.cards_status != (P2W|P1W|P2C|P1C) + || (Chipset.IORam[SRQ2] & NINT2) == 0 + || (Chipset.IORam[SRQ2] & NINT ) == 0 + ) + { + Chipset.SoftInt = TRUE; // set interrupt + bInterrupt = TRUE; + } + } + Chipset.IORam[d]=c; + break; + +// 0010F = NS:CARDSTATUS +// 0010F @ [P2W P1W P2C P1C] (read-only) Port 2 writable .. Port 1 inserted + case 0x0F: break; // read-only + +// 00110 = HP:IOC +// 00110 @ Serial I/O Control [SON ETBE ERBF ERBZ] +// 00110 @ Serial On, Interrupt On Recv.Buf.Empty, Full, Buzy + case 0x10: + Chipset.IORam[d]=c; + CheckSerial(); // handle UART on/off + if ((c & SON) == 0) // SON bit cleared + { + Chipset.IORam[IOC] = 0; // clear IOC + Chipset.IORam[RCS] = 0; // clear RCS + Chipset.IORam[TCS] = 0; // clear TCS + Chipset.IORam[RBR_LSB] = 0; // clear RBR + Chipset.IORam[RBR_MSB] = 0; + Chipset.IORam[TBR_LSB] = 0; // clear TBR + Chipset.IORam[TBR_MSB] = 0; + } + if (UpdateUSRQ()) INTERRUPT; // update USRQ bit + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: IOC write: %x\n"),Chipset.pc,Chipset.IORam[d]); + OutputDebugString(buffer); + } + #endif + break; + +// 00111 = HP:RCS +// 00111 Serial Receive Control/Status [RX RER RBZ RBF] (bit 3 is read-only) + case 0x11: + if (Chipset.IORam[IOC] & SON) + { + EnterCriticalSection(&csIOLock); + { + // critical section because of RER access + Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); + } + LeaveCriticalSection(&csIOLock); + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: RCS write: %x\n"),Chipset.pc,Chipset.IORam[d]); + OutputDebugString(buffer); + } + #endif + } + break; + +// 00112 = HP:TCS +// 00112 @ Serial Transmit Control/Status [BRK LPB TBZ TBF] + case 0x12: + if (Chipset.IORam[IOC] & SON) + { + Chipset.IORam[d]=c; + CommTxBRK(); // update BRK condition + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: TCS write: %x\n"),Chipset.pc,Chipset.IORam[d]); + OutputDebugString(buffer); + } + #endif + } + break; + +// 00113 = HP:CRER +// 00113 @ Serial Clear RER (writing anything clears RER bit) + case 0x13: + IOBit(RCS,RER,FALSE); + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: CRER write: %x\n"),Chipset.pc,Chipset.IORam[d]); + OutputDebugString(buffer); + } + #endif + break; + +// 00114 = HP:RBR +// 00114 @ Serial Receive Buffer Register (Reading clears RBF bit) + case 0x14: break; // read-only + case 0x15: break; // read-only + +// 00116 = HP:TBR +// 00116 @ Serial Transmit Buffer Register (Writing sets TBF bit) + case 0x16: + case 0x17: + if (Chipset.IORam[IOC] & SON) + { + Chipset.IORam[d]=c; + tbr_acc = TRUE; // new TBR value + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: TBR %s write: %x\n"),Chipset.pc,(d==0x16) ? "LSB" : "MSB",*a); + OutputDebugString(buffer); + } + #endif + } + break; + +// 00118 = NS:SRR +// 00118 @ Service Request Register (read-only) +// 00118 @ [ISRQ TSRQ USRQ VSRQ] [KDN NINT2 NINT LSRQ] + case 0x18: break; // read-only + case 0x19: break; // read-only + +// 0011A = HP:IRC +// 0011A @ IR Control Register [IRI EIRU EIRI IRE] (bit 3 is read-only) +// 0011A @ IR Input, Enable IR UART mode, Enable IR Interrupt, IR Event + case 0x1A: + // COM port open and EIRU bit changed + if (bCommInit && ((c^Chipset.IORam[d]) & EIRU) != 0) + { + // save new value for COM open + Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); + // reopen COM port with new setting + bCommInit = CommOpen(szSerialWire,szSerialIr); + } + Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: IRC write: %x\n"),Chipset.pc,Chipset.IORam[d]); + OutputDebugString(buffer); + } + #endif + break; + +// 0011B = NS:BASENIBOFF +// 0011B @ Used as addressto get BASENIB from 11F to the 5th nibble + case 0x1B: break; + +// 0011C = NS:LCR +// 0011C @ Led Control Register [LED ELBE LBZ LBF] (Setting LED is draining) + case 0x1C: + // HP49G new mapping on LED bit change + if (cCurrentRomType=='X' && ((c^Chipset.IORam[d])&LED)) + { + Chipset.IORam[d]=c; // save new value for mapping + Map(Chipset.P2Base,Chipset.P2End); // new ROM mapping + #if defined DEBUG_FLASH + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: NCE3: R%cM\n"),Chipset.pc,(c&LED) ? 'O' : 'A'); + OutputDebugString(buffer); + } + #endif + } + Chipset.IORam[d]=c; + break; + +// 0011D = NS:LBR +// 0011D @ Led Buffer Register [0 0 0 LBO] (bits 1-3 read zero) + case 0x1D: Chipset.IORam[d]=c&1; break; + +// 0011E = NS:SCRATCHPAD +// 0011E @ Scratch pad + case 0x1E: Chipset.IORam[d]=c; break; + +// 0011F = NS:BASENIB +// 0011F @ 7 or 8 for base memory + case 0x1F: Chipset.IORam[d]=c; break; + +// 00120 = NS:DISPADDR +// 00120 @ Display Start Address (write only) +// 00120 @ bit 0 is ignored (display must start on byte boundary) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + Chipset.IORam[d]=c; + bDISPADDR = TRUE; // addr 0x120-0x124 changed + break; + +// 00125 = NS:LINEOFFS +// 00125 @ Display Line offset (write only) (no of bytes skipped after each line) +// 00125 @ MSG sign extended + case 0x25: + case 0x26: + case 0x27: + Chipset.IORam[d]=c; + bLINEOFFS = TRUE; // addr 0x125-0x127 changed + break; + +// 00128 = NS:LINECOUNT +// 00128 @ Display Line Counter and miscellaneous (28-29) +// 00128 @ [LC3 LC2 LC1 LC0] [DA19 M32 LC5 LC4] +// 00128 @ Line counter 6 bits -> max = 2^6-1 = 63 = disp height +// 00128 @ Normally has 55 -> Menu starts at display row 56 + case 0x28: + // LSB of LINECOUNT changed + if (c != (BYTE) (Chipset.lcounter & 0xf)) + { + Chipset.lcounter = (Chipset.lcounter & ~0xF) | c; + disp |= (DISP_POINTER | DISP_MAIN | DISP_MENUE); + } + break; + + case 0x29: + // MSB of LINECOUNT changed + b = (c & 0x3) << 4; // upper two bits + if (b != (Chipset.lcounter & 0x30)) + { + Chipset.lcounter = (Chipset.lcounter & ~0x30) | b; + disp |= (DISP_POINTER | DISP_MAIN | DISP_MENUE); + } + + if ((c^Chipset.IORam[d])&DA19) // DA19 changed + { + Chipset.IORam[d]^=DA19; // save new DA19 + Map(0x00,0xFF); // new ROM mapping + } + break; + + case 0x2A: break; + case 0x2B: break; + case 0x2C: break; + case 0x2D: break; + +// 0012E = NS:TIMER1CTRL +// 0012E @ TIMER1 Control [SRQ WKE INT XTRA] + case 0x2E: + Chipset.IORam[d]=c; // don't clear XTRA bit + ReadT1(); // dummy read for checking control bits + break; + +// 0012F = NS:TIMER2CTRL +// 0012F @ TIMER2 Control [SRQ WKE INT RUN] + case 0x2F: + Chipset.IORam[d]=c; + ReadT2(); // dummy read for checking control bits + if (c&1) + StartTimers(); + else + StopTimers(); + disp |= DISP_ANNUN; // update annunciators + break; + +// 00130 = NS:MENUADDR +// 00130 @ Display Secondary Start Address (write only) (30-34) +// 00130 @ Menu Display Address, no line offsets + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + Chipset.IORam[d]=c; + bMENUADDR = TRUE; // addr 0x130-0x134 changed + break; + + case 0x35: break; + case 0x36: break; + +// 00137 = HP:TIMER1 +// 00137 @ Decremented 16 times/s + case 0x37: + SetT1(c); // set new value + break; + +// 00138 = HP:TIMER2 +// 00138 @ hardware timer (38-3F), decremented 8192 times/s + // nothing - fall through to default + + default: + Chipset.IORam[d]=c; // write data + + if (d >= TIMER2) // timer2 update + { + Nunpack(Chipset.IORam+TIMER2,ReadT2(),8); + memcpy(Chipset.IORam+d,a,s); + SetT2(Npack(Chipset.IORam+TIMER2,8)); + goto finish; + } + } + a++; d++; + } while (--s); + +finish: + if (bDISPADDR) // 0x120-0x124 changed + { + b = Npack(Chipset.IORam+DISP1CTL,5)&0xFFFFE; + if (b != Chipset.start1) + { + Chipset.start1 = b; + disp |= (DISP_POINTER | DISP_MAIN); + } + } + if (bLINEOFFS) // addr 0x125-0x127 changed + { + signed short lo = (signed short)Npack(Chipset.IORam+LINENIBS,3); + if (lo&0x800) lo-=0x1000; + if (lo != Chipset.loffset) + { + Chipset.loffset = lo; + disp |= (DISP_POINTER | DISP_MAIN); + } + } + if (bMENUADDR) // addr 0x130-0x134 changed + { + b = Npack(Chipset.IORam+DISP2CTL,5)&0xFFFFE; + if (b != Chipset.start2) + { + Chipset.start2 = b; + disp |= (DISP_POINTER | DISP_MENUE); + } + } + + if (tbr_acc) // addr 0x116-0x117 changed + { + IOBit(TCS,TBF,TRUE); // set transmit buffer full bit + CommTransmit(); // transmit char + } + + if (disp & DISP_POINTER) + { + disp &= ~DISP_POINTER; // display pointer updated + UpdateDisplayPointers(); + } + if (disp & DISP_ANNUN) + { + disp &= ~DISP_ANNUN; // annunciators updated + UpdateAnnunciators(); + } + return; +} diff --git a/SOURCE/OPCODES.C b/SOURCE/OPCODES.C new file mode 100644 index 0000000..0a9d33b --- /dev/null +++ b/SOURCE/OPCODES.C @@ -0,0 +1,2482 @@ +/* + * opcodes.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * Copyright (C) 1999 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "Opcodes.h" +#include "apple.h" +#include "io.h" // I/O register definitions +#include "i28f160.h" + +#define w Chipset +#define GOYES3 {if(w.carry) o_goyes3(I);else{w.pc+=2;return;}} +#define GOYES5 {if(w.carry) o_goyes5(I);else{w.pc+=2;return;}} + +#if !defined _BIGENDIAN +#define REG(t,r) (*((t*)&(r))) // little endian machine +#else +#define REG(t,r) (*((t*)((BYTE*)&(r)+sizeof(r)-sizeof(t)))) // big endian machine +#endif + +#pragma intrinsic(memset,memcpy) + +#include "Ops.h" + +// Fields start and length +UINT F_s[16] = {0/*P*/,0,2,0,15,3,0,0,0,0,0,0,0,0,0,0}; +UINT F_l[16] = {1,1/*P+1*/,1,3,1,12,2,16,0,0,0,0,0,0,0,5}; + +VOID o00(LPBYTE I) // RTNSXM +{ + w.cycles+=9; + w.pc = rstkpop(); + w.HST |= XM; + return; +} + +VOID o01(LPBYTE I) // RTN +{ + w.cycles+=9; + w.pc = rstkpop(); + return; +} + +VOID o02(LPBYTE I) // RTNSC +{ + w.cycles+=9; + w.pc = rstkpop(); + w.carry = TRUE; + return; +} + +VOID o03(LPBYTE I) // RTNCC +{ + w.cycles+=9; + w.pc = rstkpop(); + w.carry = FALSE; + return; +} + +VOID o04(LPBYTE I) // SETHEX +{ + w.cycles+=3; + w.pc+=2; + w.mode_dec = FALSE; + return; +} + +VOID o05(LPBYTE I) // SETDEC +{ + w.cycles+=3; + w.pc+=2; + w.mode_dec = TRUE; + return; +} + +VOID o06(LPBYTE I) // RSTK=C +{ + w.cycles+=8; + w.pc+=2; + rstkpush(Npack(w.C,5)); + return; +} + +VOID o07(LPBYTE I) // C=RSTK +{ + w.cycles+=8; + w.pc+=2; + Nunpack(w.C,rstkpop(),5); + return; +} + +VOID o08(LPBYTE I) // CLRST +{ + w.cycles+=5; + w.pc+=2; + memset(w.ST, 0, 3); + return; +} + +VOID o09(LPBYTE I) // C=ST +{ + w.cycles+=5; + w.pc+=2; + memcpy(w.C, w.ST, 3); + return; +} + +VOID o0A(LPBYTE I) // ST=C +{ + w.cycles+=5; + w.pc+=2; + memcpy(w.ST, w.C, 3); + return; +} + +VOID o0B(LPBYTE I) // CSTEX +{ + w.cycles+=5; + w.pc+=2; + Nxchg(w.C, w.ST, 3); + return; +} + +VOID o0C(LPBYTE I) // P=P+1 +{ + w.cycles+=3; + w.pc+=2; + if (w.P<15) + { + w.P++; + w.carry=FALSE; + } + else + { + w.P=0; + w.carry=TRUE; + } + PCHANGED; + return; +} + +VOID o0D(LPBYTE I) // P=P-1 +{ + w.cycles+=3; + w.pc+=2; + if (w.P) + { + w.P--; + w.carry=FALSE; + } + else + { + w.P=0xF; + w.carry=TRUE; + } + PCHANGED; + return; +} + +VOID o0Ef0(LPBYTE I) // A=A&B f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.A,w.B,I[2]); + return; +} +VOID o0Ef1(LPBYTE I) // B=B&C f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.B,w.C,I[2]); + return; +} +VOID o0Ef2(LPBYTE I) // C=C&A f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.C,w.A,I[2]); + return; +} +VOID o0Ef3(LPBYTE I) // D=D&C f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.D,w.C,I[2]); + return; +} +VOID o0Ef4(LPBYTE I) // B=B&A f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.B,w.A,I[2]); + return; +} +VOID o0Ef5(LPBYTE I) // C=C&B f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.C,w.B,I[2]); + return; +} +VOID o0Ef6(LPBYTE I) // A=A&C f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.A,w.C,I[2]); + return; +} +VOID o0Ef7(LPBYTE I) // C=C&D f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFand(w.C,w.D,I[2]); + return; +} + +VOID o0Ef8(LPBYTE I) // A=A!B f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.A,w.B,I[2]); + return; +} +VOID o0Ef9(LPBYTE I) // B=B!C f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.B,w.C,I[2]); + return; +} +VOID o0EfA(LPBYTE I) // C=C!A f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.C,w.A,I[2]); + return; +} +VOID o0EfB(LPBYTE I) // D=D!C f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.D,w.C,I[2]); + return; +} +VOID o0EfC(LPBYTE I) // B=B!A f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.B,w.A,I[2]); + return; +} +VOID o0EfD(LPBYTE I) // C=C!B f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.C,w.B,I[2]); + return; +} +VOID o0EfE(LPBYTE I) // A=A!C f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.A,w.C,I[2]); + return; +} +VOID o0EfF(LPBYTE I) // C=C!D f +{ + w.cycles+=4+F_l[I[2]]; + w.pc+=4; + NFor(w.C,w.D,I[2]); + return; +} + +VOID o0F(LPBYTE I) // RTI +{ + w.cycles+=9; + w.pc = rstkpop(); + w.inte = TRUE; // enable interrupt + + if ((w.intd && w.intk) || w.IR15X) // keyboard interrupt pending + { + w.intd = FALSE; // no keyboard interrupt pending any more + INTERRUPT; // restart interrupt handler + } + + // low interrupt lines + { + BOOL bNINT2 = Chipset.IORam[SRQ1] == 0 && (Chipset.IORam[SRQ2] & LSRQ) == 0; + BOOL bNINT = (Chipset.IORam[SRQ2] & NINT) != 0; + + // card detection off and timer running + if ((Chipset.IORam[CARDCTL] & ECDT) == 0 && (Chipset.IORam[TIMER2_CTRL] & RUN) != 0) + { + // state of CDT2 + bNINT2 = bNINT2 && (Chipset.cards_status & (P2W|P2C)) != P2C; + // state of CDT1 + bNINT = bNINT && (Chipset.cards_status & (P1W|P1C)) != P1C; + } + + if (!bNINT2 || !bNINT) // NINT2 or NINT interrupt line low + INTERRUPT; // restart interrupt handler + } + + // restart interrupt handler when timer interrupt + if (w.IORam[TIMER1_CTRL]&INTR) // INT bit of timer1 is set + ReadT1(); // check for int + + if (w.IORam[TIMER2_CTRL]&INTR) // INT bit of timer2 is set + ReadT2(); // check for int + return; +} + +VOID o100(LPBYTE I) // R0=A W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R0, w.A, 16); + return; +} + +VOID o101(LPBYTE I) // R1=A W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R1, w.A, 16); + return; +} + +VOID o102(LPBYTE I) // R2=A W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R2, w.A, 16); + return; +} + +VOID o103(LPBYTE I) // R3=A W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R3, w.A, 16); + return; +} + +VOID o104(LPBYTE I) // R4=A W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R4, w.A, 16); + return; +} + +VOID o108(LPBYTE I) // R0=C W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R0, w.C, 16); + return; +} + +VOID o109(LPBYTE I) // R1=C W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R1, w.C, 16); + return; +} + +VOID o10A(LPBYTE I) // R2=C W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R2, w.C, 16); + return; +} + +VOID o10B(LPBYTE I) // R3=C W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R3, w.C, 16); + return; +} + +VOID o10C(LPBYTE I) // R4=C W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.R4, w.C, 16); + return; +} + +VOID o110(LPBYTE I) // A=R0 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.A, w.R0, 16); + return; +} + +VOID o111(LPBYTE I) // A=R1 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.A, w.R1, 16); + return; +} + +VOID o112(LPBYTE I) // A=R2 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.A, w.R2, 16); + return; +} + +VOID o113(LPBYTE I) // A=R3 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.A, w.R3, 16); + return; +} + +VOID o114(LPBYTE I) // A=R4 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.A, w.R4, 16); + return; +} + +VOID o118(LPBYTE I) // C=R0 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.C, w.R0, 16); + return; +} + +VOID o119(LPBYTE I) // C=R1 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.C, w.R1, 16); + return; +} + +VOID o11A(LPBYTE I) // C=R2 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.C, w.R2, 16); + return; +} + +VOID o11B(LPBYTE I) // C=R3 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.C, w.R3, 16); + return; +} + +VOID o11C(LPBYTE I) // C=R4 W +{ + w.cycles+=19; + w.pc+=3; + memcpy(w.C, w.R4, 16); + return; +} + +VOID o120(LPBYTE I) // AR0EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.A, w.R0, 16); + return; +} + +VOID o121(LPBYTE I) // AR1EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.A, w.R1, 16); + return; +} + +VOID o122(LPBYTE I) // AR2EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.A, w.R2, 16); + return; +} + +VOID o123(LPBYTE I) // AR3EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.A, w.R3, 16); + return; +} + +VOID o124(LPBYTE I) // AR4EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.A, w.R4, 16); + return; +} + +VOID o128(LPBYTE I) // CR0EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.C, w.R0, 16); + return; +} + +VOID o129(LPBYTE I) // CR1EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.C, w.R1, 16); + return; +} + +VOID o12A(LPBYTE I) // CR2EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.C, w.R2, 16); + return; +} + +VOID o12B(LPBYTE I) // CR3EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.C, w.R3, 16); + return; +} + +VOID o12C(LPBYTE I) // CR4EX W +{ + w.cycles+=19; + w.pc+=3; + Nxchg(w.C, w.R4, 16); + return; +} + +VOID o130(LPBYTE I) // D0=A +{ + w.cycles+=8; + w.pc+=3; + w.d0=Npack(w.A,5); + return; +} + +VOID o131(LPBYTE I) // D1=A +{ + w.cycles+=8; + w.pc+=3; + w.d1=Npack(w.A,5); + return; +} + +VOID o132(LPBYTE I) // AD0EX +{ + DWORD d = w.d0; + w.d0=Npack(w.A,5); + Nunpack(w.A,d,5); + w.cycles+=8; + w.pc+=3; + return; +} + +VOID o133(LPBYTE I) // AD1EX +{ + DWORD d=w.d1; + w.d1=Npack(w.A,5); + Nunpack(w.A,d,5); + w.cycles+=8; + w.pc+=3; + return; +} + +VOID o134(LPBYTE I) // D0=C +{ + w.cycles+=8; + w.pc+=3; + w.d0=Npack(w.C,5); + return; +} + +VOID o135(LPBYTE I) // D1=C +{ + w.cycles+=8; + w.pc+=3; + w.d1=Npack(w.C,5); + return; +} + +VOID o136(LPBYTE I) // CD0EX +{ + DWORD d=w.d0; + w.d0=Npack(w.C,5); + Nunpack(w.C,d,5); + w.cycles+=8; + w.pc+=3; + return; +} + +VOID o137(LPBYTE I) // CD1EX +{ + DWORD d=w.d1; + w.d1=Npack(w.C,5); + Nunpack(w.C,d,5); + w.cycles+=8; + w.pc+=3; + return; +} + +VOID o138(LPBYTE I) // D0=AS +{ + w.cycles+=7; + w.pc+=3; + REG(WORD,w.d0)=(WORD)Npack(w.A,4); + return; +} + +VOID o139(LPBYTE I) // D1=AS +{ + w.cycles+=7; + w.pc+=3; + REG(WORD,w.d1)=(WORD)Npack(w.A,4); + return; +} + +VOID o13A(LPBYTE I) // AD0XS +{ + DWORD d=w.d0; + REG(WORD,w.d0)=(WORD)Npack(w.A,4); + Nunpack(w.A,d,4); + w.cycles+=7; + w.pc+=3; + return; +} + +VOID o13B(LPBYTE I) // AD1XS +{ + DWORD d=w.d1; + REG(WORD,w.d1)=(WORD)Npack(w.A,4); + Nunpack(w.A,d,4); + w.cycles+=7; + w.pc+=3; + return; +} + +VOID o13C(LPBYTE I) // D0=CS +{ + w.cycles+=7; + w.pc+=3; + REG(WORD,w.d0)=(WORD)Npack(w.C,4); + return; +} + +VOID o13D(LPBYTE I) // D1=CS +{ + w.cycles+=7; + w.pc+=3; + REG(WORD,w.d1)=(WORD)Npack(w.C,4); + return; +} + +VOID o13E(LPBYTE I) // CD0XS +{ + DWORD d=w.d0; + REG(WORD,w.d0)=(WORD)Npack(w.C,4); + Nunpack(w.C,d,4); + w.cycles+=7; + w.pc+=3; + return; +} + +VOID o13F(LPBYTE I) // CD1XS +{ + DWORD d=w.d1; + REG(WORD,w.d1)=(WORD)Npack(w.C,4); + Nunpack(w.C,d,4); + w.cycles+=7; + w.pc+=3; + return; +} + +VOID o140(LPBYTE I) { w.cycles+=17; w.pc+=3; Nwrite(w.A, w.d0, 5); return; } // DAT0=A A +VOID o141(LPBYTE I) { w.cycles+=17; w.pc+=3; Nwrite(w.A, w.d1, 5); return; } // DAT1=A A +VOID o144(LPBYTE I) { w.cycles+=17; w.pc+=3; Nwrite(w.C, w.d0, 5); return; } // DAT0=C A +VOID o145(LPBYTE I) { w.cycles+=17; w.pc+=3; Nwrite(w.C, w.d1, 5); return; } // DAT1=C A +VOID o148(LPBYTE I) { w.cycles+=14; w.pc+=3; Nwrite(w.A, w.d0, 2); return; } // DAT0=A B +VOID o149(LPBYTE I) { w.cycles+=14; w.pc+=3; Nwrite(w.A, w.d1, 2); return; } // DAT1=A B +VOID o14C(LPBYTE I) { w.cycles+=14; w.pc+=3; Nwrite(w.C, w.d0, 2); return; } // DAT0=C B +VOID o14D(LPBYTE I) { w.cycles+=14; w.pc+=3; Nwrite(w.C, w.d1, 2); return; } // DAT1=C B + +VOID o142(LPBYTE I) { w.cycles+=18; w.pc+=3; Nread(w.A, w.d0, 5); return; } // A=DAT0 A +VOID o143(LPBYTE I) { w.cycles+=18; w.pc+=3; Nread(w.A, w.d1, 5); return; } // A=DAT1 A +VOID o146(LPBYTE I) { w.cycles+=18; w.pc+=3; Nread(w.C, w.d0, 5); return; } // C=DAT0 A +VOID o147(LPBYTE I) { w.cycles+=18; w.pc+=3; Nread(w.C, w.d1, 5); return; } // C=DAT1 A +VOID o14A(LPBYTE I) { w.cycles+=15; w.pc+=3; Nread(w.A, w.d0, 2); return; } // A=DAT0 B +VOID o14B(LPBYTE I) { w.cycles+=15; w.pc+=3; Nread(w.A, w.d1, 2); return; } // A=DAT1 B +VOID o14E(LPBYTE I) { w.cycles+=15; w.pc+=3; Nread(w.C, w.d0, 2); return; } // C=DAT0 B +VOID o14F(LPBYTE I) { w.cycles+=15; w.pc+=3; Nread(w.C, w.d1, 2); return; } // C=DAT0 B + +VOID o150a(LPBYTE I) { w.cycles+=16+F_l[I[3]]; w.pc+=4; NFwrite(w.A, w.d0, I[3]); return; } // DAT0=A a +VOID o151a(LPBYTE I) { w.cycles+=16+F_l[I[3]]; w.pc+=4; NFwrite(w.A, w.d1, I[3]); return; } // DAT1=A a +VOID o154a(LPBYTE I) { w.cycles+=16+F_l[I[3]]; w.pc+=4; NFwrite(w.C, w.d0, I[3]); return; } // DAT0=C a +VOID o155a(LPBYTE I) { w.cycles+=16+F_l[I[3]]; w.pc+=4; NFwrite(w.C, w.d1, I[3]); return; } // DAT1=C a +VOID o152a(LPBYTE I) { w.cycles+=17+F_l[I[3]]; w.pc+=4; NFread(w.A, w.d0, I[3]); return; } // A=DAT0 a +VOID o153a(LPBYTE I) { w.cycles+=17+F_l[I[3]]; w.pc+=4; NFread(w.A, w.d1, I[3]); return; } // A=DAT1 a +VOID o156a(LPBYTE I) { w.cycles+=17+F_l[I[3]]; w.pc+=4; NFread(w.C, w.d0, I[3]); return; } // C=DAT0 a +VOID o157a(LPBYTE I) { w.cycles+=17+F_l[I[3]]; w.pc+=4; NFread(w.C, w.d1, I[3]); return; } // C=DAT1 a + +VOID o158x(LPBYTE I) { w.cycles+=16+I[3]+1; w.pc+=4; Nwrite(w.A, w.d0, I[3]+1); return; } // DAT0=A x +VOID o159x(LPBYTE I) { w.cycles+=16+I[3]+1; w.pc+=4; Nwrite(w.A, w.d1, I[3]+1); return; } // DAT1=A x +VOID o15Cx(LPBYTE I) { w.cycles+=16+I[3]+1; w.pc+=4; Nwrite(w.C, w.d0, I[3]+1); return; } // DAT0=C x +VOID o15Dx(LPBYTE I) { w.cycles+=16+I[3]+1; w.pc+=4; Nwrite(w.C, w.d1, I[3]+1); return; } // DAT1=C x +VOID o15Ax(LPBYTE I) { w.cycles+=17+I[3]+1; w.pc+=4; Nread(w.A, w.d0, I[3]+1); return; } // A=DAT0 x +VOID o15Bx(LPBYTE I) { w.cycles+=17+I[3]+1; w.pc+=4; Nread(w.A, w.d1, I[3]+1); return; } // A=DAT1 x +VOID o15Ex(LPBYTE I) { w.cycles+=17+I[3]+1; w.pc+=4; Nread(w.C, w.d0, I[3]+1); return; } // C=DAT0 x +VOID o15Fx(LPBYTE I) { w.cycles+=17+I[3]+1; w.pc+=4; Nread(w.C, w.d1, I[3]+1); return; } // C=DAT1 x + +VOID o16x(LPBYTE I) // D0=D0+ (n+1) +{ + w.cycles+=7; + w.pc+=3; + w.d0+=I[2]+1; + if (w.d0>0xfffff) + { + w.d0&=0xfffff; + w.carry=TRUE; + } + else + { + w.carry=FALSE; + } + return; +} + +VOID o17x(LPBYTE I) // D1=D1+ (n+1) +{ + w.cycles+=7; + w.pc+=3; + w.d1+=I[2]+1; + if (w.d1>0xfffff) + { + w.d1&=0xfffff; + w.carry=TRUE; + } + else + { + w.carry=FALSE; + } + return; +} + +VOID o18x(LPBYTE I) // D0=D0- (n+1) +{ + w.cycles+=7; + w.pc+=3; + w.d0-=I[2]+1; + if (w.d0>0xfffff) + { + w.d0&=0xfffff; + w.carry=TRUE; + } + else + { + w.carry=FALSE; + } + return; +} + +VOID o19d2(LPBYTE I) // D0=(2) #dd +{ + w.cycles+=4; + w.pc+=4; + REG(BYTE,w.d0)=(BYTE)Npack(I+2,2); + return; +} + +VOID o1Ad4(LPBYTE I) // D0=(4) #dddd +{ + w.cycles+=6; + w.pc+=6; + REG(WORD,w.d0)=(WORD)Npack(I+2,4); + return; +} + +VOID o1Bd5(LPBYTE I) // D0=(5) #ddddd +{ + w.cycles+=7; + w.pc+=7; + w.d0=Npack(I+2,5); + return; +} + +VOID o1Cx(LPBYTE I) // D1=D1- (n+1) +{ + w.cycles+=7; + w.pc+=3; + w.d1-=I[2]+1; + if (w.d1>0xfffff) + { + w.d1&=0xfffff; + w.carry=TRUE; + } + else + { + w.carry=FALSE; + } + return; +} + +VOID o1Dd2(LPBYTE I) // D1=(2) #dd +{ + w.cycles+=4; + w.pc+=4; + REG(BYTE,w.d1)=(BYTE)Npack(I+2,2); + return; +} + +VOID o1Ed4(LPBYTE I) // D1=(4) #dddd +{ + w.cycles+=6; + w.pc+=6; + REG(WORD,w.d1)=(WORD)Npack(I+2,4); + return; +} + +VOID o1Fd5(LPBYTE I) // D1=(5) #ddddd +{ + w.cycles+=7; + w.pc+=7; + w.d1=Npack(I+2,5); + return; +} + +VOID o2n(LPBYTE I) // P= n +{ + w.cycles+=2; + w.pc+=2; + w.P=I[1]; + PCHANGED; + return; +} + +VOID o3X(LPBYTE I) // LCHEX +{ + UINT n=I[1]+1; + UINT d=16-w.P; + w.cycles+=3+I[1]; + w.pc+=2; + I+=2; // UNSAFE + if (n<=d) + { + memcpy(w.C+w.P,I,n); + } + else + { + memcpy(w.C+w.P,I,d); + memcpy(w.C,I+d,n-d); + } + w.pc+=n; + return; +} + +VOID o4d2(LPBYTE I) // GOC #dd +{ + if (!w.carry) + { + w.cycles+=3; + w.pc+=3; + } + else + { + signed char jmp=I[1]+(I[2]<<4); + if (jmp) + w.pc+=jmp+1; + else + w.pc=rstkpop(); + w.cycles+=10; + w.pc&=0xFFFFF; + } + return; +} + +VOID o5d2(LPBYTE I) // GONC +{ + if (w.carry) + { + w.cycles+=3; + w.pc+=3; + } + else + { + signed char jmp=I[1]+(I[2]<<4); + if (jmp) + w.pc+=jmp+1; + else + w.pc=rstkpop(); + w.cycles+=10; + w.pc&=0xFFFFF; + } + return; +} + +VOID o6d3(LPBYTE I) // GOTO +{ + DWORD d=Npack(I+1,3); + if (d&0x800) + w.pc-=0xFFF-d; + else + w.pc+=d+1; + w.cycles+=11; + w.pc&=0xFFFFF; + return; +} + +VOID o7d3(LPBYTE I) // GOSUB +{ + DWORD d=Npack(I+1,3); + rstkpush(w.pc+4); + if (d&0x800) w.pc-=0xFFC-d; else w.pc+=d+4; + w.cycles+=12; + w.pc&=0xFFFFF; + return; +} + +VOID o800(LPBYTE I) // OUT=CS +{ + w.cycles+=4; + w.pc+=3; + w.out = (w.out&0xff0) | w.C[0]; + ScanKeyboard(FALSE,FALSE); // 1ms keyboard poll + return; +} + +VOID o801(LPBYTE I) // OUT=C +{ + w.cycles+=6; + w.pc+=3; + w.out = (WORD)Npack(w.C, 3); + ScanKeyboard(FALSE,FALSE); // 1ms keyboard poll + return; +} + +VOID o802(LPBYTE I) // A=IN +{ + w.cycles+=7; + // emulate Clarke/Yorke bug + if ((w.pc & 1) == 0 || MapData(w.pc) == M_IO) + w.pc+=3; + ScanKeyboard(TRUE,FALSE); // update Chipset.in register (direct) + IOBit(0x19,8,w.in != 0); // update KDN bit in the SRQ register + Nunpack(w.A, w.in, 4); + return; +} + +VOID o803(LPBYTE I) // C=IN +{ + w.cycles+=7; + // emulate Clarke/Yorke bug + if ((w.pc & 1) == 0 || MapData(w.pc) == M_IO) + w.pc+=3; + ScanKeyboard(TRUE,FALSE); // update Chipset.in register (direct) + IOBit(0x19,8,w.in != 0); // update KDN bit in the SRQ register + Nunpack(w.C, w.in, 4); + return; +} + +VOID o804(LPBYTE I) // UNCNFG +{ + w.cycles+=12; + w.pc+=3; + Uncnfg(); + return; +} + +VOID o805(LPBYTE I) // CONFIG +{ + w.cycles+=11; + w.pc+=3; + Config(); + return; +} + +VOID o806(LPBYTE I) // C=ID +{ + w.cycles+=11; + w.pc+=3; + C_Eq_Id(); + return; +} + +VOID o807(LPBYTE I) // SHUTDN +{ + BOOL bShutdn = TRUE; // shut down + + // only shut down when no timer wake up + if (w.IORam[TIMER1_CTRL]&WKE) // WKE bit of timer1 is set + { + if (ReadT1()&0x08) // and MSB of timer1 is set + { + w.IORam[TIMER1_CTRL] &= ~WKE; // clear WKE + bShutdn = FALSE; // don't shut down + } + } + if (w.IORam[TIMER2_CTRL]&WKE) // WKE bit of timer2 is set + { + if (ReadT2()&0x80000000) // and MSB of timer2 is set + { + w.IORam[TIMER2_CTRL] &= ~WKE; // clear WKE + bShutdn = FALSE; // don't shut down + } + } + ScanKeyboard(TRUE,FALSE); // update Chipset.in register (direct) + if (w.in == 0 && bShutdn) // shut down only when enabled + { + w.Shutdn = TRUE; // set mode before exit emulation loop + bInterrupt = TRUE; + + // emulation of BS reset circuit in deep sleep + // HP39/40G, HP48GX, HP49G, display off, card control off or in slow mode + if ( (cCurrentRomType=='E' || cCurrentRomType=='G' || cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='P' || cCurrentRomType=='Q') // CdB for HP: add apples + && (w.IORam[BITOFFSET]&DON) == 0 + && ((w.IORam[CARDCTL]&(ECDT|RCDT)) != (ECDT|RCDT))) + { + // on HP48GX ROM must be selected (DA19=1) and + // the NOT MA18 address line must be low (test for high) + // else we get power on VCO from the ROM chip + // (MA18 input pin security diode to VCC pin) + if ( cCurrentRomType!='G' + || ((w.IORam[LINECOUNT+1]&DA19) && (w.pc & 0x80000))) + { + w.Bank_FF = 0; // reset bank switcher FF + RomSwitch(w.Bank_FF); // force new mapping + } + } + } + w.cycles+=6; + w.pc+=3; + return; +} + +VOID o8080(LPBYTE I) // INTON +{ + w.cycles+=5; + w.pc+=4; + w.intk = TRUE; + ScanKeyboard(FALSE,FALSE); // 1ms keyboard poll + if (w.intd || w.IR15X) // keyboard interrupt pending + { + w.intd = FALSE; // no keyboard interrupt pending any more + INTERRUPT; // restart interrupt handler + } + return; +} + +VOID o80810(LPBYTE I) // RSI +{ + w.cycles+=6; + w.pc+=5; + ScanKeyboard(TRUE,TRUE); // one input bit high (direct)? + + // enable KDN update + w.dwKdnCycles = (DWORD) (w.cycles & 0xFFFFFFFF) - (DWORD) T2CYCLES * 16; + + if (w.in && w.inte == FALSE) // key interrupt pending + w.intd = TRUE; // keyboard interrupt pending + return; +} + +VOID o8082X(LPBYTE I) // LA +{ + UINT n=I[4]+1; + UINT d=16-w.P; + w.cycles+=6+I[4]; + w.pc+=5+n; + I+=5; // UNSAFE + if (n<=d) + { + memcpy(w.A+w.P,I,n); + } + else + { + memcpy(w.A+w.P,I,d); + memcpy(w.A,I+d,n-d); + } + return; +} + +VOID o8083(LPBYTE I) // BUSCB +{ + w.cycles+=7; + w.pc+=4; + // emulated as NOP + // InfoMessage(_T("BUSCB instruction executed.")); + return; +} + +VOID o8084n(LPBYTE I) // ABIT=0 n +{ + w.cycles+=6; + w.pc+=5; + Nbit0(w.A, I[4]); + return; +} + +VOID o8085n(LPBYTE I) // ABIT=1 n +{ + w.cycles+=6; + w.pc+=5; + Nbit1(w.A, I[4]); + return; +} + +VOID o8086n(LPBYTE I) // ?ABIT=0 n +{ + w.cycles+=9; + w.pc+=5; + Tbit0(w.A, I[4]); + GOYES5; +} + +VOID o8087n(LPBYTE I) // ?ABIT=1 n +{ + w.cycles+=9; + w.pc+=5; + Tbit1(w.A, I[4]); + GOYES5; +} + +VOID o8088n(LPBYTE I) // CBIT=0 n +{ + w.cycles+=6; + w.pc+=5; + Nbit0(w.C, I[4]); + return; +} + +VOID o8089n(LPBYTE I) // CBIT=1 n +{ + w.cycles+=6; + w.pc+=5; + Nbit1(w.C, I[4]); + return; +} + +VOID o808An(LPBYTE I) // ?CBIT=0 n +{ + w.cycles+=9; + w.pc+=5; + Tbit0(w.C, I[4]); + GOYES5; +} + +VOID o808Bn(LPBYTE I) // ?CBIT=1 n +{ + w.cycles+=9; + w.pc+=5; + Tbit1(w.C, I[4]); + GOYES5; +} + +VOID o808C(LPBYTE I) // PC=(A) +{ + BYTE p[5]; + w.cycles+=23; + Nread(p,Npack(w.A,5),5); // read (A) and update CRC + w.pc=Npack(p,5); + return; +} + +VOID o808D(LPBYTE I) // BUSCD +{ + w.cycles+=7; + w.pc+=4; + // emulated as NOP + // InfoMessage(_T("BUSCD instruction executed.")); + return; +} + +VOID o808E(LPBYTE I) // PC=(C) +{ + BYTE p[5]; + w.cycles+=23; + Nread(p,Npack(w.C,5),5); // read (C) and update CRC + w.pc=Npack(p,5); + return; +} + +VOID o808F(LPBYTE I) // INTOFF +{ + w.cycles+=5; + w.pc+=4; + UpdateKdnBit(); // update KDN bit + w.intk = FALSE; + return; +} + +VOID o809(LPBYTE I) // C+P+1 - HEX MODE +{ + w.cycles+=8; + w.pc+=3; + w.C[0]+=w.P; Nincx(w.C,5); + return; +} + +VOID o80A(LPBYTE I) // RESET +{ + w.cycles+=6; + w.pc+=3; + Reset(); + return; +} + +VOID o80B(LPBYTE I) // BUSCC +{ + w.cycles+=6; + w.pc+=3; + // emulated as NOP + // InfoMessage(_T("BUSCC instruction executed.")); + if (cCurrentRomType=='Q' || cCurrentRomType=='2' || cCurrentRomType=='P') + { + o80BExt(I); // Saturnator extentions + } + return; +} + +VOID o80Cn(LPBYTE I) // C=P n +{ + w.cycles+=6; + w.pc+=4; + w.C[I[3]] = w.P; + return; +} + +VOID o80Dn(LPBYTE I) // P=C n +{ + w.cycles+=6; + w.pc+=4; + w.P = w.C[I[3]]; + PCHANGED; + return; +} + +VOID o80E(LPBYTE I) // SREQ? +{ + w.cycles+=7; + w.pc+=3; + w.C[0]=0; // no device + return; +} + +VOID o80Fn(LPBYTE I) // CPEX n +{ + BYTE n = w.P; + w.P = w.C[I[3]]; + w.C[I[3]] = n; + PCHANGED; + w.cycles+=6; + w.pc+=4; + return; +} + +VOID o810(LPBYTE I) // ASLC +{ + w.cycles+=21; + w.pc+=3; + Nslc(w.A, 16); + return; +} + +VOID o811(LPBYTE I) // BSLC +{ + w.cycles+=21; + w.pc+=3; + Nslc(w.B, 16); + return; +} + +VOID o812(LPBYTE I) // CSLC +{ + w.cycles+=21; + w.pc+=3; + Nslc(w.C, 16); + return; +} + +VOID o813(LPBYTE I) // DSLC +{ + w.cycles+=21; + w.pc+=3; + Nslc(w.D, 16); + return; +} + +VOID o814(LPBYTE I) // ASRC +{ + w.cycles+=21; + w.pc+=3; + Nsrc(w.A, 16); + return; +} + +VOID o815(LPBYTE I) // BSRC +{ + w.cycles+=21; + w.pc+=3; + Nsrc(w.B, 16); + return; +} + +VOID o816(LPBYTE I) // CSRC +{ + w.cycles+=21; + w.pc+=3; + Nsrc(w.C, 16); + return; +} + +VOID o817(LPBYTE I) // DSRC +{ + w.cycles+=21; + w.pc+=3; + Nsrc(w.D, 16); + return; +} + +VOID o818f0x(LPBYTE I) // A=A+x+1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.A[F_s[I[3]]]+=I[5]; // add constant value-1 + Ninc16(w.A,nF_l,F_s[I[3]]); // add one and adjust in HEX mode + return; +} + +VOID o818f1x(LPBYTE I) // B=B+x+1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.B[F_s[I[3]]]+=I[5]; // add constant value-1 + Ninc16(w.B,nF_l,F_s[I[3]]); // add one and adjust in HEX mode + return; +} + +VOID o818f2x(LPBYTE I) // C=C+x+1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.C[F_s[I[3]]]+=I[5]; // add constant value-1 + Ninc16(w.C,nF_l,F_s[I[3]]); // add one and adjust in HEX mode + return; +} + +VOID o818f3x(LPBYTE I) // D=D+x+1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.D[F_s[I[3]]]+=I[5]; // add constant value-1 + Ninc16(w.D,nF_l,F_s[I[3]]); // add one and adjust in HEX mode + return; +} + +VOID o818f8x(LPBYTE I) // A=A-x-1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.A[F_s[I[3]]]-=I[5]; // sub constant value+1 + Ndec16(w.A,nF_l,F_s[I[3]]); // dec one and adjust in HEX mode + return; +} + +VOID o818f9x(LPBYTE I) // B=B-x-1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.B[F_s[I[3]]]-=I[5]; // sub constant value+1 + Ndec16(w.B,nF_l,F_s[I[3]]); // dec one and adjust in HEX mode + return; +} + +VOID o818fAx(LPBYTE I) // C=C-x-1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.C[F_s[I[3]]]-=I[5]; // sub constant value+1 + Ndec16(w.C,nF_l,F_s[I[3]]); // dec one and adjust in HEX mode + return; +} + +VOID o818fBx(LPBYTE I) // D=D-x-1 f +{ + // register length with saturn bug emulation + UINT nF_l = (F_l[I[3]] == 1) ? 0x11 : F_l[I[3]]; + + w.cycles+=5+F_l[I[3]]; + w.pc+=6; + w.D[F_s[I[3]]]-=I[5]; // sub constant value+1 + Ndec16(w.D,nF_l,F_s[I[3]]); // dec one and adjust in HEX mode + return; +} + +VOID o819f0(LPBYTE I) // ASRB.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=5; + NFsrb(w.A, I[3]); + return; +} + +VOID o819f1(LPBYTE I) // BSRB.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=5; + NFsrb(w.B, I[3]); + return; +} + +VOID o819f2(LPBYTE I) // CSRB.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=5; + NFsrb(w.C, I[3]); + return; +} + +VOID o819f3(LPBYTE I) // DSRB.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=5; + NFsrb(w.D, I[3]); + return; +} + +VOID o81Af00(LPBYTE I) // R0=A.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R0, w.A, I[3]); + return; +} + +VOID o81Af01(LPBYTE I) // R1=A.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R1, w.A, I[3]); + return; +} + +VOID o81Af02(LPBYTE I) // R2=A.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R2, w.A, I[3]); + return; +} + +VOID o81Af03(LPBYTE I) // R3=A.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R3, w.A, I[3]); + return; +} + +VOID o81Af04(LPBYTE I) // R4=A.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R4, w.A, I[3]); + return; +} + +VOID o81Af08(LPBYTE I) // R0=C.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R0, w.C, I[3]); + return; +} + +VOID o81Af09(LPBYTE I) // R1=C.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R1, w.C, I[3]); + return; +} + +VOID o81Af0A(LPBYTE I) // R2=C.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R2, w.C, I[3]); + return; +} + +VOID o81Af0B(LPBYTE I) // R3=C.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R3, w.C, I[3]); + return; +} + +VOID o81Af0C(LPBYTE I) // R4=C.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.R4, w.C, I[3]); + return; +} + +VOID o81Af10(LPBYTE I) // A=R0.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.A, w.R0, I[3]); + return; +} + +VOID o81Af11(LPBYTE I) // A=R1.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.A, w.R1, I[3]); + return; +} + +VOID o81Af12(LPBYTE I) // A=R2.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.A, w.R2, I[3]); + return; +} + +VOID o81Af13(LPBYTE I) // A=R3.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.A, w.R3, I[3]); + return; +} + +VOID o81Af14(LPBYTE I) // A=R4.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.A, w.R4, I[3]); + return; +} + +VOID o81Af18(LPBYTE I) // C=R0.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.C, w.R0, I[3]); + return; +} + +VOID o81Af19(LPBYTE I) // C=R1.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.C, w.R1, I[3]); + return; +} + +VOID o81Af1A(LPBYTE I) // C=R2.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.C, w.R2, I[3]); + return; +} + +VOID o81Af1B(LPBYTE I) // C=R3.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.C, w.R3, I[3]); + return; +} + +VOID o81Af1C(LPBYTE I) // C=R4.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFcopy(w.C, w.R4, I[3]); + return; +} + +VOID o81Af20(LPBYTE I) // AR0EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.A, w.R0, I[3]); + return; +} + +VOID o81Af21(LPBYTE I) // AR1EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.A, w.R1, I[3]); + return; +} + +VOID o81Af22(LPBYTE I) // AR2EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.A, w.R2, I[3]); + return; +} + +VOID o81Af23(LPBYTE I) // AR3EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.A, w.R3, I[3]); + return; +} + +VOID o81Af24(LPBYTE I) // AR4EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.A, w.R4, I[3]); + return; +} + +VOID o81Af28(LPBYTE I) // CR0EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.C, w.R0, I[3]); + return; +} + +VOID o81Af29(LPBYTE I) // CR1EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.C, w.R1, I[3]); + return; +} + +VOID o81Af2A(LPBYTE I) // CR2EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.C, w.R2, I[3]); + return; +} + +VOID o81Af2B(LPBYTE I) // CR3EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.C, w.R3, I[3]); + return; +} + +VOID o81Af2C(LPBYTE I) // CR4EX.F f +{ + w.cycles+=6+F_l[I[3]]; + w.pc+=6; + NFxchg(w.C, w.R4, I[3]); + return; +} + +VOID o81B2(LPBYTE I) // PC=A +{ + w.cycles+=16; + w.pc = Npack(w.A,5); + return; +} + +VOID o81B3(LPBYTE I) // PC=C +{ + w.cycles+=16; + w.pc = Npack(w.C,5); + return; +} + +VOID o81B4(LPBYTE I) // A=PC +{ + w.cycles+=9; + w.pc+=4; + Nunpack(w.A,w.pc,5); + return; +} + +VOID o81B5(LPBYTE I) // C=PC +{ + w.cycles+=9; + w.pc+=4; + Nunpack(w.C,w.pc,5); + return; +} + +VOID o81B6(LPBYTE I) // APCEX +{ + DWORD d=w.pc+4; + w.cycles+=16; + w.pc=Npack(w.A,5); + Nunpack(w.A,d,5); + return; +} + +VOID o81B7(LPBYTE I) // CPCEX +{ + DWORD d=w.pc+4; + w.cycles+=16; + w.pc=Npack(w.C,5); + Nunpack(w.C,d,5); + return; +} + +VOID o81C(LPBYTE I) // ASRB +{ + w.cycles+=20; + w.pc+=3; + Nsrb(w.A, 16); + return; +} + +VOID o81D(LPBYTE I) // BSRB +{ + w.cycles+=20; + w.pc+=3; + Nsrb(w.B, 16); + return; +} + +VOID o81E(LPBYTE I) // CSRB +{ + w.cycles+=20; + w.pc+=3; + Nsrb(w.C, 16); + return; +} + +VOID o81F(LPBYTE I) // DSRB +{ + w.cycles+=20; + w.pc+=3; + Nsrb(w.D, 16); + return; +} + +VOID o82n(LPBYTE I) // HST=0 m +{ + w.cycles+=3; + w.pc+=3; + w.HST&=~I[2]; + return; +} + +VOID o83n(LPBYTE I) // ?HST=0 m +{ + w.cycles+=6; + w.pc+=3; + if ((w.HST&I[2])==0) + w.carry=TRUE; + else + w.carry=FALSE; + GOYES3; +} + +VOID o84n(LPBYTE I) // ST=0 n +{ + w.cycles+=4; + w.pc+=3; + Nbit0(w.ST, I[2]); + return; +} + +VOID o85n(LPBYTE I) // ST=1 n +{ + w.cycles+=4; + w.pc+=3; + Nbit1(w.ST, I[2]); + return; +} + +VOID o86n(LPBYTE I) // ?ST=0 n +{ + w.cycles+=7; + w.pc+=3; + Tbit0(w.ST, I[2]); + GOYES3; +} + +VOID o87n(LPBYTE I) // ?ST=1 n +{ + w.cycles+=7; + w.pc+=3; + Tbit1(w.ST, I[2]); + GOYES3; +} + +VOID o88n(LPBYTE I) // ?P# n +{ + w.cycles+=6; + w.pc+=3; + if (w.P!=I[2]) + w.carry=TRUE; + else + w.carry=FALSE; + GOYES3; +} + +VOID o89n(LPBYTE I) // ?P= n +{ + w.cycles+=6; + w.pc+=3; + if (w.P==I[2]) + w.carry=TRUE; + else + w.carry=FALSE; + GOYES3; +} + +VOID o8A0(LPBYTE I) // ?A=B A +{ + w.cycles+=11; + w.pc+=3; + Te(w.A, w.B, 5); + GOYES3; +} + +VOID o8A1(LPBYTE I) // ?B=C A +{ + w.cycles+=11; + w.pc+=3; + Te(w.B, w.C, 5); + GOYES3; +} + +VOID o8A2(LPBYTE I) // ?C=A A +{ + w.cycles+=11; + w.pc+=3; + Te(w.C, w.A, 5); + GOYES3; +} + +VOID o8A3(LPBYTE I) // ?D=C A +{ + w.cycles+=11; + w.pc+=3; + Te(w.D, w.C, 5); + GOYES3; +} + +VOID o8A4(LPBYTE I) // ?A#B A +{ + w.cycles+=11; + w.pc+=3; + Tne(w.A, w.B, 5); + GOYES3; +} + +VOID o8A5(LPBYTE I) // ?B#C A +{ + w.cycles+=11; + w.pc+=3; + Tne(w.B, w.C, 5); + GOYES3; +} + +VOID o8A6(LPBYTE I) // ?C#A A +{ + w.cycles+=11; + w.pc+=3; + Tne(w.C, w.A, 5); + GOYES3; +} + +VOID o8A7(LPBYTE I) // ?D#C A +{ + w.cycles+=11; + w.pc+=3; + Tne(w.D, w.C, 5); + GOYES3; +} + +VOID o8A8(LPBYTE I) // ?A=0 A +{ + w.cycles+=11; + w.pc+=3; + Tz(w.A, 5); + GOYES3; +} + +VOID o8A9(LPBYTE I) // ?B=0 A +{ + w.cycles+=11; + w.pc+=3; + Tz(w.B, 5); + GOYES3; +} + +VOID o8AA(LPBYTE I) // ?C=0 A +{ + w.cycles+=11; + w.pc+=3; + Tz(w.C, 5); + GOYES3; +} + +VOID o8AB(LPBYTE I) // ?D=0 A +{ + w.cycles+=11; + w.pc+=3; + Tz(w.D, 5); + GOYES3; +} + +VOID o8AC(LPBYTE I) // ?A#0 A +{ + w.cycles+=11; + w.pc+=3; + Tnz(w.A, 5); + GOYES3; +} + +VOID o8AD(LPBYTE I) // ?B#0 A +{ + w.cycles+=11; + w.pc+=3; + Tnz(w.B, 5); + GOYES3; +} + +VOID o8AE(LPBYTE I) // ?C#0 A +{ + w.cycles+=11; + w.pc+=3; + Tnz(w.C, 5); + GOYES3; +} + +VOID o8AF(LPBYTE I) // ?D#0 A +{ + w.cycles+=11; + w.pc+=3; + Tnz(w.D, 5); + GOYES3; +} + +VOID o8B0(LPBYTE I) // ?A>B A +{ + w.cycles+=11; + w.pc+=3; + Ta(w.A, w.B, 5); + GOYES3; +} + +VOID o8B1(LPBYTE I) // ?B>C A +{ + w.cycles+=11; + w.pc+=3; + Ta(w.B, w.C, 5); + GOYES3; +} + +VOID o8B2(LPBYTE I) // ?C>A A +{ + w.cycles+=11; + w.pc+=3; + Ta(w.C, w.A, 5); + GOYES3; +} + +VOID o8B3(LPBYTE I) // ?D>C A +{ + w.cycles+=11; + w.pc+=3; + Ta(w.D, w.C, 5); + GOYES3; +} + +VOID o8B4(LPBYTE I) // ?A=B A +{ + w.cycles+=11; + w.pc+=3; + Tae(w.A, w.B, 5); + GOYES3; +} + +VOID o8B9(LPBYTE I) // ?B>=C A +{ + w.cycles+=11; + w.pc+=3; + Tae(w.B, w.C, 5); + GOYES3; +} + +VOID o8BA(LPBYTE I) // ?C>=A A +{ + w.cycles+=11; + w.pc+=3; + Tae(w.C, w.A, 5); + GOYES3; +} + +VOID o8BB(LPBYTE I) // ?D>=C A +{ + w.cycles+=11; + w.pc+=3; + Tae(w.D, w.C, 5); + GOYES3; +} + +VOID o8BC(LPBYTE I) // ?A<=B A +{ + w.cycles+=11; + w.pc+=3; + Tbe(w.A, w.B, 5); + GOYES3; +} + +VOID o8BD(LPBYTE I) // ?B<=C A +{ + w.cycles+=11; + w.pc+=3; + Tbe(w.B, w.C, 5); + GOYES3; +} + +VOID o8BE(LPBYTE I) // ?C<=A A +{ + w.cycles+=11; + w.pc+=3; + Tbe(w.C, w.A, 5); + GOYES3; +} + +VOID o8BF(LPBYTE I) // ?D<=C A +{ + w.cycles+=11; + w.pc+=3; + Tbe(w.D, w.C, 5); + GOYES3; +} + +VOID o8Cd4(LPBYTE I) // GOLONG #dddd +{ + DWORD d=Npack(I+2, 4); + if (d&0x8000) w.pc -= 0xfffe - d; else w.pc+= d + 2; + w.cycles+=14; + w.pc&=0xFFFFF; + return; +} + +VOID o8Dd5(LPBYTE I) // GOVLNG #ddddd +{ + w.cycles+=14; + w.pc = Npack(I+2, 5); + return; +} + +VOID o8Ed4(LPBYTE I) // GOSUBL #dddd +{ + DWORD d=Npack(I+2,4); + rstkpush(w.pc+6); + if (d&0x8000) w.pc -= 0xfffa - d; else w.pc += d + 6; + w.cycles+=14; + w.pc&=0xFFFFF; + return; +} + +VOID o8Fd5(LPBYTE I) // GOSBVL #ddddd +{ + w.cycles+=15; + rstkpush(w.pc+7); + w.pc=Npack(I+2, 5); + return; +} + + // ?r=s f +VOID o9a0(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFe(w.A, w.B, I[1]); GOYES3; } +VOID o9a1(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFe(w.B, w.C, I[1]); GOYES3; } +VOID o9a2(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFe(w.C, w.A, I[1]); GOYES3; } +VOID o9a3(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFe(w.D, w.C, I[1]); GOYES3; } + + // ?r#s f +VOID o9a4(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFne(w.A, w.B, I[1]); GOYES3; } +VOID o9a5(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFne(w.B, w.C, I[1]); GOYES3; } +VOID o9a6(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFne(w.C, w.A, I[1]); GOYES3; } +VOID o9a7(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFne(w.D, w.C, I[1]); GOYES3; } + + // ?r=0 f +VOID o9a8(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFz(w.A, I[1]); GOYES3; } +VOID o9a9(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFz(w.B, I[1]); GOYES3; } +VOID o9aA(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFz(w.C, I[1]); GOYES3; } +VOID o9aB(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFz(w.D, I[1]); GOYES3; } + + // ?r#0 f +VOID o9aC(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFnz(w.A, I[1]); GOYES3; } +VOID o9aD(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFnz(w.B, I[1]); GOYES3; } +VOID o9aE(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFnz(w.C, I[1]); GOYES3; } +VOID o9aF(LPBYTE I) { w.cycles+=6+F_l[I[1]]; w.pc+=3; TFnz(w.D, I[1]); GOYES3; } + + // ?s>r f +VOID o9b0(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFa(w.A, w.B, I[1]&7); GOYES3; } +VOID o9b1(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFa(w.B, w.C, I[1]&7); GOYES3; } +VOID o9b2(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFa(w.C, w.A, I[1]&7); GOYES3; } +VOID o9b3(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFa(w.D, w.C, I[1]&7); GOYES3; } + + // ?r=s f +VOID o9b8(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFae(w.A, w.B, I[1]&7); GOYES3; } +VOID o9b9(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFae(w.B, w.C, I[1]&7); GOYES3; } +VOID o9bA(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFae(w.C, w.A, I[1]&7); GOYES3; } +VOID o9bB(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFae(w.D, w.C, I[1]&7); GOYES3; } + + // ?r<=s f +VOID o9bC(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFbe(w.A, w.B, I[1]&7); GOYES3; } +VOID o9bD(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFbe(w.B, w.C, I[1]&7); GOYES3; } +VOID o9bE(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFbe(w.C, w.A, I[1]&7); GOYES3; } +VOID o9bF(LPBYTE I) { w.cycles+=6+F_l[I[1]&7]; w.pc+=3; TFbe(w.D, w.C, I[1]&7); GOYES3; } + + // r=r+s f +VOID oAa0(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.A, w.B, I[1]); return; } +VOID oAa1(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.B, w.C, I[1]); return; } +VOID oAa2(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.C, w.A, I[1]); return; } +VOID oAa3(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.D, w.C, I[1]); return; } + + // r=r+r f +VOID oAa4(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.A, w.A, I[1]); return; } +VOID oAa5(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.B, w.B, I[1]); return; } +VOID oAa6(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.C, w.C, I[1]); return; } +VOID oAa7(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.D, w.D, I[1]); return; } + + // s=s+r f +VOID oAa8(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.B, w.A, I[1]); return; } +VOID oAa9(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.C, w.B, I[1]); return; } +VOID oAaA(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.A, w.C, I[1]); return; } +VOID oAaB(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFadd(w.C, w.D, I[1]); return; } + + // r=r-1 f +VOID oAaC(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFdec(w.A, I[1]); return; } +VOID oAaD(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFdec(w.B, I[1]); return; } +VOID oAaE(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFdec(w.C, I[1]); return; } +VOID oAaF(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFdec(w.D, I[1]); return; } + + // r=0 f +VOID oAb0(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFzero(w.A, I[1]&7); return; } +VOID oAb1(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFzero(w.B, I[1]&7); return; } +VOID oAb2(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFzero(w.C, I[1]&7); return; } +VOID oAb3(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFzero(w.D, I[1]&7); return; } + + // r=s f +VOID oAb4(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.A, w.B, I[1]&7); return; } +VOID oAb5(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.B, w.C, I[1]&7); return; } +VOID oAb6(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.C, w.A, I[1]&7); return; } +VOID oAb7(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.D, w.C, I[1]&7); return; } + + // s=r f +VOID oAb8(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.B, w.A, I[1]&7); return; } +VOID oAb9(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.C, w.B, I[1]&7); return; } +VOID oAbA(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.A, w.C, I[1]&7); return; } +VOID oAbB(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFcopy(w.C, w.D, I[1]&7); return; } + + // rsEX f +VOID oAbC(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFxchg(w.A, w.B, I[1]&7); return; } +VOID oAbD(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFxchg(w.B, w.C, I[1]&7); return; } +VOID oAbE(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFxchg(w.C, w.A, I[1]&7); return; } +VOID oAbF(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFxchg(w.D, w.C, I[1]&7); return; } + + // r=r-s f +VOID oBa0(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.A, w.B, I[1]); return; } +VOID oBa1(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.B, w.C, I[1]); return; } +VOID oBa2(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.C, w.A, I[1]); return; } +VOID oBa3(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.D, w.C, I[1]); return; } + + // r=r+1 f +VOID oBa4(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFinc(w.A, I[1]); return; } +VOID oBa5(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFinc(w.B, I[1]); return; } +VOID oBa6(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFinc(w.C, I[1]); return; } +VOID oBa7(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFinc(w.D, I[1]); return; } + + // s=s-r f +VOID oBa8(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.B, w.A, I[1]); return; } +VOID oBa9(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.C, w.B, I[1]); return; } +VOID oBaA(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.A, w.C, I[1]); return; } +VOID oBaB(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFsub(w.C, w.D, I[1]); return; } + + // r=s-r f +VOID oBaC(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFrsub(w.A, w.B, I[1]); return; } +VOID oBaD(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFrsub(w.B, w.C, I[1]); return; } +VOID oBaE(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFrsub(w.C, w.A, I[1]); return; } +VOID oBaF(LPBYTE I) { w.cycles+=3+F_l[I[1]]; w.pc+=3; NFrsub(w.D, w.C, I[1]); return; } + + // rSL f +VOID oBb0(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsl(w.A, I[1]&7); return; } +VOID oBb1(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsl(w.B, I[1]&7); return; } +VOID oBb2(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsl(w.C, I[1]&7); return; } +VOID oBb3(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsl(w.D, I[1]&7); return; } + + // rSR f +VOID oBb4(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsr(w.A, I[1]&7); return; } +VOID oBb5(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsr(w.B, I[1]&7); return; } +VOID oBb6(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsr(w.C, I[1]&7); return; } +VOID oBb7(LPBYTE I) { w.cycles+=4+F_l[I[1]&7]; w.pc+=3; NFsr(w.D, I[1]&7); return; } + + // r=-r f +VOID oBb8(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFneg(w.A, I[1]&7); return; } +VOID oBb9(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFneg(w.B, I[1]&7); return; } +VOID oBbA(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFneg(w.C, I[1]&7); return; } +VOID oBbB(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFneg(w.D, I[1]&7); return; } + + // r=-r-1 f +VOID oBbC(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFnot(w.A, I[1]&7); return; } +VOID oBbD(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFnot(w.B, I[1]&7); return; } +VOID oBbE(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFnot(w.C, I[1]&7); return; } +VOID oBbF(LPBYTE I) { w.cycles+=3+F_l[I[1]&7]; w.pc+=3; NFnot(w.D, I[1]&7); return; } + + // r=r+s A +VOID oC0(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.A, w.B, 5); return; } +VOID oC1(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.B, w.C, 5); return; } +VOID oC2(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.C, w.A, 5); return; } +VOID oC3(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.D, w.C, 5); return; } + + // r=r+r A +VOID oC4(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.A, w.A, 5); return; } +VOID oC5(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.B, w.B, 5); return; } +VOID oC6(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.C, w.C, 5); return; } +VOID oC7(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.D, w.D, 5); return; } + + // s=s+r A +VOID oC8(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.B, w.A, 5); return; } +VOID oC9(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.C, w.B, 5); return; } +VOID oCA(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.A, w.C, 5); return; } +VOID oCB(LPBYTE I) { w.cycles+=7; w.pc+=2; Nadd(w.C, w.D, 5); return; } + + // r=r-1 A +VOID oCC(LPBYTE I) { w.cycles+=7; w.pc+=2; Ndec(w.A, 5, 0); return; } +VOID oCD(LPBYTE I) { w.cycles+=7; w.pc+=2; Ndec(w.B, 5, 0); return; } +VOID oCE(LPBYTE I) { w.cycles+=7; w.pc+=2; Ndec(w.C, 5, 0); return; } +VOID oCF(LPBYTE I) { w.cycles+=7; w.pc+=2; Ndec(w.D, 5, 0); return; } + + // r=0 A +VOID oD0(LPBYTE I) { w.cycles+=7; w.pc+=2; memset(w.A, 0, 5); return; } +VOID oD1(LPBYTE I) { w.cycles+=7; w.pc+=2; memset(w.B, 0, 5); return; } +VOID oD2(LPBYTE I) { w.cycles+=7; w.pc+=2; memset(w.C, 0, 5); return; } +VOID oD3(LPBYTE I) { w.cycles+=7; w.pc+=2; memset(w.D, 0, 5); return; } + + // r=s A +VOID oD4(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.A, w.B, 5); return; } +VOID oD5(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.B, w.C, 5); return; } +VOID oD6(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.C, w.A, 5); return; } +VOID oD7(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.D, w.C, 5); return; } + + // s=r A +VOID oD8(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.B, w.A, 5); return; } +VOID oD9(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.C, w.B, 5); return; } +VOID oDA(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.A, w.C, 5); return; } +VOID oDB(LPBYTE I) { w.cycles+=7; w.pc+=2; memcpy(w.C, w.D, 5); return; } + + // rsEX +VOID oDC(LPBYTE I) { w.cycles+=7; w.pc+=2; Nxchg(w.A, w.B, 5); return; } +VOID oDD(LPBYTE I) { w.cycles+=7; w.pc+=2; Nxchg(w.B, w.C, 5); return; } +VOID oDE(LPBYTE I) { w.cycles+=7; w.pc+=2; Nxchg(w.C, w.A, 5); return; } +VOID oDF(LPBYTE I) { w.cycles+=7; w.pc+=2; Nxchg(w.D, w.C, 5); return; } + + // r=r-s A +VOID oE0(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.A, w.B, 5); return; } +VOID oE1(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.B, w.C, 5); return; } +VOID oE2(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.C, w.A, 5); return; } +VOID oE3(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.D, w.C, 5); return; } + + // r=r+1 A +VOID oE4(LPBYTE I) { w.cycles+=7; w.pc+=2; Ninc(w.A, 5, 0); return; } +VOID oE5(LPBYTE I) { w.cycles+=7; w.pc+=2; Ninc(w.B, 5, 0); return; } +VOID oE6(LPBYTE I) { w.cycles+=7; w.pc+=2; Ninc(w.C, 5, 0); return; } +VOID oE7(LPBYTE I) { w.cycles+=7; w.pc+=2; Ninc(w.D, 5, 0); return; } + + // s=s-r A +VOID oE8(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.B, w.A, 5); return; } +VOID oE9(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.C, w.B, 5); return; } +VOID oEA(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.A, w.C, 5); return; } +VOID oEB(LPBYTE I) { w.cycles+=7; w.pc+=2; Nsub(w.C, w.D, 5); return; } + + // r=s-r A +VOID oEC(LPBYTE I) { w.cycles+=7; w.pc+=2; Nrsub(w.A, w.B, 5); return; } +VOID oED(LPBYTE I) { w.cycles+=7; w.pc+=2; Nrsub(w.B, w.C, 5); return; } +VOID oEE(LPBYTE I) { w.cycles+=7; w.pc+=2; Nrsub(w.C, w.A, 5); return; } +VOID oEF(LPBYTE I) { w.cycles+=7; w.pc+=2; Nrsub(w.D, w.C, 5); return; } + + // rSL A +VOID oF0(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsl(w.A, 5); return; } +VOID oF1(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsl(w.B, 5); return; } +VOID oF2(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsl(w.C, 5); return; } +VOID oF3(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsl(w.D, 5); return; } + + // rSR A +VOID oF4(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsr(w.A, 5); return; } +VOID oF5(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsr(w.B, 5); return; } +VOID oF6(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsr(w.C, 5); return; } +VOID oF7(LPBYTE I) { w.cycles+=8; w.pc+=2; Nsr(w.D, 5); return; } + + // r=-r A +VOID oF8(LPBYTE I) { w.cycles+=7; w.pc+=2; Nneg(w.A, 5); return; } +VOID oF9(LPBYTE I) { w.cycles+=7; w.pc+=2; Nneg(w.B, 5); return; } +VOID oFA(LPBYTE I) { w.cycles+=7; w.pc+=2; Nneg(w.C, 5); return; } +VOID oFB(LPBYTE I) { w.cycles+=7; w.pc+=2; Nneg(w.D, 5); return; } + + // r=-r-1 A +VOID oFC(LPBYTE I) { w.cycles+=7; w.pc+=2; Nnot(w.A, 5); return; } +VOID oFD(LPBYTE I) { w.cycles+=7; w.pc+=2; Nnot(w.B, 5); return; } +VOID oFE(LPBYTE I) { w.cycles+=7; w.pc+=2; Nnot(w.C, 5); return; } +VOID oFF(LPBYTE I) { w.cycles+=7; w.pc+=2; Nnot(w.D, 5); return; } + +// length is guessed, just skip +VOID o_invalid3(LPBYTE I) +{ + _ASSERT(FALSE); // invalid, length guessed, skip 3 nibbles + w.pc+=3; + return; +} + +VOID o_invalid4(LPBYTE I) +{ + _ASSERT(FALSE); // invalid, length guessed, skip 4 nibbles + w.pc+=4; + return; +} + +VOID o_invalid5(LPBYTE I) +{ + _ASSERT(FALSE); // invalid, length guessed, skip 5 nibbles + w.pc+=5; + return; +} + +VOID o_invalid6(LPBYTE I) +{ + _ASSERT(FALSE); // invalid, length guessed, skip 6 nibbles + w.pc+=6; + return; +} + +VOID o_goyes3(LPBYTE I) +{ + signed char jmp = I[3]+(I[4]<<4); + w.cycles+=7; + if (jmp) + w.pc=(w.pc+jmp)&0xFFFFF; + else + w.pc=rstkpop(); + return; +} + +VOID o_goyes5(LPBYTE I) +{ + signed char jmp = I[5]+(I[6]<<4); + w.cycles+=7; + if (jmp) + w.pc=(w.pc+jmp)&0xFFFFF; + else + w.pc=rstkpop(); + return; +} + +//////// EXTENSIONS //////// +VOID o81B1(LPBYTE I) +{ + if (cCurrentRomType=='Q' || cCurrentRomType=='2' || cCurrentRomType=='P') + { + // The ARM-based calculators use this previously-unused opcode to return to RPL + BYTE p[5]; + w.cycles+=48; + + Nread(w.A, w.d0, 5); + + w.d0+=5; + if (w.d0>0xfffff) + { + w.d0&=0xfffff; + w.carry=TRUE; + } + else + { + w.carry=FALSE; + } + + Nread(p,Npack(w.A,5),5); // read (A) and update CRC + w.pc=Npack(p,5); + } + else + { + // Emu48 borrows this opcode for the beep patch on the non-ARM-based calculators + External(&w); // beep patch + PCHANGED; // update field select table + } + return; +} +//////////////////////////// diff --git a/SOURCE/OPCODES.H b/SOURCE/OPCODES.H new file mode 100644 index 0000000..949419d --- /dev/null +++ b/SOURCE/OPCODES.H @@ -0,0 +1,445 @@ +/* + * opcodes.h + * + * This file is part of Emu48 + * + * Copyright (C) 1999 Christoph Gießelink + * + */ + +#define PCHANGED ((void)(F_s[0]=Chipset.P,F_l[1]=Chipset.P+1)) +#define INTERRUPT ((void)(Chipset.SoftInt=TRUE,bInterrupt=TRUE)) + +extern UINT F_s[16]; +extern UINT F_l[16]; + +extern VOID o00(LPBYTE I); // RTNSXM +extern VOID o01(LPBYTE I); // RTN +extern VOID o02(LPBYTE I); // RTNSC +extern VOID o03(LPBYTE I); // RTNCC +extern VOID o04(LPBYTE I); // SETHEX +extern VOID o05(LPBYTE I); // SETDEC +extern VOID o06(LPBYTE I); // RSTK=C +extern VOID o07(LPBYTE I); // C=RSTK +extern VOID o08(LPBYTE I); // CLRST +extern VOID o09(LPBYTE I); // C=ST +extern VOID o0A(LPBYTE I); // ST=C +extern VOID o0B(LPBYTE I); // CSTEX +extern VOID o0C(LPBYTE I); // P=P+1 +extern VOID o0D(LPBYTE I); // P=P-1 +extern VOID o0Ef0(LPBYTE I); // A=A&B f +extern VOID o0Ef1(LPBYTE I); // B=B&C f +extern VOID o0Ef2(LPBYTE I); // C=C&A f +extern VOID o0Ef3(LPBYTE I); // D=D&C f +extern VOID o0Ef4(LPBYTE I); // B=B&A f +extern VOID o0Ef5(LPBYTE I); // C=C&B f +extern VOID o0Ef6(LPBYTE I); // A=A&C f +extern VOID o0Ef7(LPBYTE I); // C=C&D f +extern VOID o0Ef8(LPBYTE I); // A=A!B f +extern VOID o0Ef9(LPBYTE I); // B=B!C f +extern VOID o0EfA(LPBYTE I); // C=C!A f +extern VOID o0EfB(LPBYTE I); // D=D!C f +extern VOID o0EfC(LPBYTE I); // B=B!A f +extern VOID o0EfD(LPBYTE I); // C=C!B f +extern VOID o0EfE(LPBYTE I); // A=A!C f +extern VOID o0EfF(LPBYTE I); // C=C!D f +extern VOID o0F(LPBYTE I); // RTI +extern VOID o100(LPBYTE I); // R0=A W +extern VOID o101(LPBYTE I); // R1=A W +extern VOID o102(LPBYTE I); // R2=A W +extern VOID o103(LPBYTE I); // R3=A W +extern VOID o104(LPBYTE I); // R4=A W +extern VOID o108(LPBYTE I); // R0=C W +extern VOID o109(LPBYTE I); // R1=C W +extern VOID o10A(LPBYTE I); // R2=C W +extern VOID o10B(LPBYTE I); // R3=C W +extern VOID o10C(LPBYTE I); // R4=C W +extern VOID o110(LPBYTE I); // A=R0 W +extern VOID o111(LPBYTE I); // A=R1 W +extern VOID o112(LPBYTE I); // A=R2 W +extern VOID o113(LPBYTE I); // A=R3 W +extern VOID o114(LPBYTE I); // A=R4 W +extern VOID o118(LPBYTE I); // C=R0 W +extern VOID o119(LPBYTE I); // C=R1 W +extern VOID o11A(LPBYTE I); // C=R2 W +extern VOID o11B(LPBYTE I); // C=R3 W +extern VOID o11C(LPBYTE I); // C=R4 W +extern VOID o120(LPBYTE I); // AR0EX W +extern VOID o121(LPBYTE I); // AR1EX W +extern VOID o122(LPBYTE I); // AR2EX W +extern VOID o123(LPBYTE I); // AR3EX W +extern VOID o124(LPBYTE I); // AR4EX W +extern VOID o128(LPBYTE I); // CR0EX W +extern VOID o129(LPBYTE I); // CR1EX W +extern VOID o12A(LPBYTE I); // CR2EX W +extern VOID o12B(LPBYTE I); // CR3EX W +extern VOID o12C(LPBYTE I); // CR4EX W +extern VOID o130(LPBYTE I); // D0=A +extern VOID o131(LPBYTE I); // D1=A +extern VOID o132(LPBYTE I); // AD0EX +extern VOID o133(LPBYTE I); // AD1EX +extern VOID o134(LPBYTE I); // D0=C +extern VOID o135(LPBYTE I); // D1=C +extern VOID o136(LPBYTE I); // CD0EX +extern VOID o137(LPBYTE I); // CD1EX +extern VOID o138(LPBYTE I); // D0=AS +extern VOID o139(LPBYTE I); // D1=AS +extern VOID o13A(LPBYTE I); // AD0XS +extern VOID o13B(LPBYTE I); // AD1XS +extern VOID o13C(LPBYTE I); // D0=CS +extern VOID o13D(LPBYTE I); // D1=CS +extern VOID o13E(LPBYTE I); // CD0XS +extern VOID o13F(LPBYTE I); // CD1XS +extern VOID o140(LPBYTE I); // DAT0=A A +extern VOID o141(LPBYTE I); // DAT0=A A +extern VOID o144(LPBYTE I); // DAT0=C A +extern VOID o145(LPBYTE I); // DAT1=C A +extern VOID o148(LPBYTE I); // DAT0=A B +extern VOID o149(LPBYTE I); // DAT1=A B +extern VOID o14C(LPBYTE I); // DAT0=C B +extern VOID o14D(LPBYTE I); // DAT1=C B +extern VOID o142(LPBYTE I); // A=DAT0 A +extern VOID o143(LPBYTE I); // A=DAT1 A +extern VOID o146(LPBYTE I); // C=DAT0 A +extern VOID o147(LPBYTE I); // C=DAT1 A +extern VOID o14A(LPBYTE I); // A=DAT0 B +extern VOID o14B(LPBYTE I); // A=DAT1 B +extern VOID o14E(LPBYTE I); // C=DAT0 B +extern VOID o14F(LPBYTE I); // C=DAT0 B +extern VOID o150a(LPBYTE I); // DAT0=A a +extern VOID o151a(LPBYTE I); // DAT1=A a +extern VOID o154a(LPBYTE I); // DAT0=C a +extern VOID o155a(LPBYTE I); // DAT1=C a +extern VOID o152a(LPBYTE I); // A=DAT0 a +extern VOID o153a(LPBYTE I); // A=DAT1 a +extern VOID o156a(LPBYTE I); // C=DAT0 a +extern VOID o157a(LPBYTE I); // C=DAT1 a +extern VOID o158x(LPBYTE I); // DAT0=A x +extern VOID o159x(LPBYTE I); // DAT1=A x +extern VOID o15Cx(LPBYTE I); // DAT0=C x +extern VOID o15Dx(LPBYTE I); // DAT1=C x +extern VOID o15Ax(LPBYTE I); // A=DAT0 x +extern VOID o15Bx(LPBYTE I); // A=DAT1 x +extern VOID o15Ex(LPBYTE I); // C=DAT0 x +extern VOID o15Fx(LPBYTE I); // C=DAT1 x +extern VOID o16x(LPBYTE I); // D0=D0+ (n+1) +extern VOID o17x(LPBYTE I); // D1=D1+ (n+1) +extern VOID o18x(LPBYTE I); // D0=D0- (n+1) +extern VOID o19d2(LPBYTE I); // D0=(2) #dd +extern VOID o1Ad4(LPBYTE I); // D0=(4) #dddd +extern VOID o1Bd5(LPBYTE I); // D0=(5) #ddddd +extern VOID o1Cx(LPBYTE I); // D1=D1- (n+1) +extern VOID o1Dd2(LPBYTE I); // D1=(2) #dd +extern VOID o1Ed4(LPBYTE I); // D1=(4) #dddd +extern VOID o1Fd5(LPBYTE I); // D1=(5) #ddddd +extern VOID o2n(LPBYTE I); // P= n +extern VOID o3X(LPBYTE I); // LCHEX +extern VOID o4d2(LPBYTE I); // GOC #dd +extern VOID o5d2(LPBYTE I); // GONC +extern VOID o6d3(LPBYTE I); // GOTO +extern VOID o7d3(LPBYTE I); // GOSUB +extern VOID o800(LPBYTE I); // OUT=CS +extern VOID o801(LPBYTE I); // OUT=C +extern VOID o802(LPBYTE I); // A=IN +extern VOID o803(LPBYTE I); // C=IN +extern VOID o804(LPBYTE I); // UNCNFG +extern VOID o805(LPBYTE I); // CONFIG +extern VOID o806(LPBYTE I); // C=ID +extern VOID o807(LPBYTE I); // SHUTDN +extern VOID o8080(LPBYTE I); // INTON +extern VOID o80810(LPBYTE I); // RSI +extern VOID o8082X(LPBYTE I); // LA +extern VOID o8083(LPBYTE I); // BUSCB +extern VOID o8084n(LPBYTE I); // ABIT=0 n +extern VOID o8085n(LPBYTE I); // ABIT=1 n +extern VOID o8086n(LPBYTE I); // ?ABIT=0 n +extern VOID o8087n(LPBYTE I); // ?ABIT=1 n +extern VOID o8088n(LPBYTE I); // CBIT=0 n +extern VOID o8089n(LPBYTE I); // CBIT=1 n +extern VOID o808An(LPBYTE I); // ?CBIT=0 n +extern VOID o808Bn(LPBYTE I); // ?CBIT=1 n +extern VOID o808C(LPBYTE I); // PC=(A) +extern VOID o808D(LPBYTE I); // BUSCD +extern VOID o808E(LPBYTE I); // PC=(C) +extern VOID o808F(LPBYTE I); // INTOFF +extern VOID o809(LPBYTE I); // C+P+1 - HEX MODE +extern VOID o80A(LPBYTE I); // RESET +extern VOID o80B(LPBYTE I); // BUSCC +extern VOID o80Cn(LPBYTE I); // C=P n +extern VOID o80Dn(LPBYTE I); // P=C n +extern VOID o80E(LPBYTE I); // SREQ? +extern VOID o80Fn(LPBYTE I); // CPEX n +extern VOID o810(LPBYTE I); // ASLC +extern VOID o811(LPBYTE I); // BSLC +extern VOID o812(LPBYTE I); // CSLC +extern VOID o813(LPBYTE I); // DSLC +extern VOID o814(LPBYTE I); // ASRC +extern VOID o815(LPBYTE I); // BSRC +extern VOID o816(LPBYTE I); // CSRC +extern VOID o817(LPBYTE I); // DSRC +extern VOID o818f0x(LPBYTE I); // A=A+x+1 f +extern VOID o818f1x(LPBYTE I); // B=B+x+1 f +extern VOID o818f2x(LPBYTE I); // C=C+x+1 f +extern VOID o818f3x(LPBYTE I); // D=D+x+1 f +extern VOID o818f8x(LPBYTE I); // A=A-x-1 f +extern VOID o818f9x(LPBYTE I); // B=B-x-1 f +extern VOID o818fAx(LPBYTE I); // C=C-x-1 f +extern VOID o818fBx(LPBYTE I); // D=D-x-1 f +extern VOID o819f0(LPBYTE I); // ASRB.F +extern VOID o819f1(LPBYTE I); // BSRB.F +extern VOID o819f2(LPBYTE I); // CSRB.F +extern VOID o819f3(LPBYTE I); // DSRB.F +extern VOID o81Af00(LPBYTE I); // R0=A.F f +extern VOID o81Af01(LPBYTE I); // R1=A.F f +extern VOID o81Af02(LPBYTE I); // R2=A.F f +extern VOID o81Af03(LPBYTE I); // R3=A.F f +extern VOID o81Af04(LPBYTE I); // R4=A.F f +extern VOID o81Af08(LPBYTE I); // R0=C.F f +extern VOID o81Af09(LPBYTE I); // R1=C.F f +extern VOID o81Af0A(LPBYTE I); // R2=C.F f +extern VOID o81Af0B(LPBYTE I); // R3=C.F f +extern VOID o81Af0C(LPBYTE I); // R4=C.F f +extern VOID o81Af10(LPBYTE I); // A=R0.F f +extern VOID o81Af11(LPBYTE I); // A=R1.F f +extern VOID o81Af12(LPBYTE I); // A=R2.F f +extern VOID o81Af13(LPBYTE I); // A=R3.F f +extern VOID o81Af14(LPBYTE I); // A=R4.F f +extern VOID o81Af18(LPBYTE I); // C=R0.F f +extern VOID o81Af19(LPBYTE I); // C=R1.F f +extern VOID o81Af1A(LPBYTE I); // C=R2.F f +extern VOID o81Af1B(LPBYTE I); // C=R3.F f +extern VOID o81Af1C(LPBYTE I); // C=R4.F f +extern VOID o81Af20(LPBYTE I); // AR0EX.F f +extern VOID o81Af21(LPBYTE I); // AR1EX.F f +extern VOID o81Af22(LPBYTE I); // AR2EX.F f +extern VOID o81Af23(LPBYTE I); // AR3EX.F f +extern VOID o81Af24(LPBYTE I); // AR4EX.F f +extern VOID o81Af28(LPBYTE I); // CR0EX.F f +extern VOID o81Af29(LPBYTE I); // CR1EX.F f +extern VOID o81Af2A(LPBYTE I); // CR2EX.F f +extern VOID o81Af2B(LPBYTE I); // CR3EX.F f +extern VOID o81Af2C(LPBYTE I); // CR4EX.F f +extern VOID o81B2(LPBYTE I); // PC=A +extern VOID o81B3(LPBYTE I); // PC=C +extern VOID o81B4(LPBYTE I); // A=PC +extern VOID o81B5(LPBYTE I); // C=PC +extern VOID o81B6(LPBYTE I); // APCEX +extern VOID o81B7(LPBYTE I); // CPCEX +extern VOID o81C(LPBYTE I); // ASRB +extern VOID o81D(LPBYTE I); // BSRB +extern VOID o81E(LPBYTE I); // CSRB +extern VOID o81F(LPBYTE I); // DSRB +extern VOID o82n(LPBYTE I); // HST=0 m +extern VOID o83n(LPBYTE I); // ?HST=0 m +extern VOID o84n(LPBYTE I); // ST=0 n +extern VOID o85n(LPBYTE I); // ST=1 n +extern VOID o86n(LPBYTE I); // ?ST=0 n +extern VOID o87n(LPBYTE I); // ?ST=1 n +extern VOID o88n(LPBYTE I); // ?P# n +extern VOID o89n(LPBYTE I); // ?P= n +extern VOID o8A0(LPBYTE I); // ?A=B A +extern VOID o8A1(LPBYTE I); // ?B=C A +extern VOID o8A2(LPBYTE I); // ?C=A A +extern VOID o8A3(LPBYTE I); // ?D=C A +extern VOID o8A4(LPBYTE I); // ?A#B A +extern VOID o8A5(LPBYTE I); // ?B#C A +extern VOID o8A6(LPBYTE I); // ?C#A A +extern VOID o8A7(LPBYTE I); // ?D#C A +extern VOID o8A8(LPBYTE I); // ?A=0 A +extern VOID o8A9(LPBYTE I); // ?B=0 A +extern VOID o8AA(LPBYTE I); // ?C=0 A +extern VOID o8AB(LPBYTE I); // ?D=0 A +extern VOID o8AC(LPBYTE I); // ?A#0 A +extern VOID o8AD(LPBYTE I); // ?B#0 A +extern VOID o8AE(LPBYTE I); // ?C#0 A +extern VOID o8AF(LPBYTE I); // ?D#0 A +extern VOID o8B0(LPBYTE I); // ?A>B A +extern VOID o8B1(LPBYTE I); // ?B>C A +extern VOID o8B2(LPBYTE I); // ?C>A A +extern VOID o8B3(LPBYTE I); // ?D>C A +extern VOID o8B4(LPBYTE I); // ?A=B A +extern VOID o8B9(LPBYTE I); // ?B>=C A +extern VOID o8BA(LPBYTE I); // ?C>=A A +extern VOID o8BB(LPBYTE I); // ?D>=C A +extern VOID o8BC(LPBYTE I); // ?A<=B A +extern VOID o8BD(LPBYTE I); // ?B<=C A +extern VOID o8BE(LPBYTE I); // ?C<=A A +extern VOID o8BF(LPBYTE I); // ?D<=C A +extern VOID o8Cd4(LPBYTE I); // GOLONG #dddd +extern VOID o8Dd5(LPBYTE I); // GOVLNG #ddddd +extern VOID o8Ed4(LPBYTE I); // GOSUBL #dddd +extern VOID o8Fd5(LPBYTE I); // GOSBVL #ddddd +extern VOID o9a0(LPBYTE I); // ?A=B f +extern VOID o9a1(LPBYTE I); // ?B=C f +extern VOID o9a2(LPBYTE I); // ?C=A f +extern VOID o9a3(LPBYTE I); // ?D=C f +extern VOID o9a4(LPBYTE I); // ?A#B f +extern VOID o9a5(LPBYTE I); // ?B#C f +extern VOID o9a6(LPBYTE I); // ?C#A f +extern VOID o9a7(LPBYTE I); // ?D#C f +extern VOID o9a8(LPBYTE I); // ?A=0 f +extern VOID o9a9(LPBYTE I); // ?B=0 f +extern VOID o9aA(LPBYTE I); // ?C=0 f +extern VOID o9aB(LPBYTE I); // ?D=0 f +extern VOID o9aC(LPBYTE I); // ?A#0 f +extern VOID o9aD(LPBYTE I); // ?B#0 f +extern VOID o9aE(LPBYTE I); // ?C#0 f +extern VOID o9aF(LPBYTE I); // ?D#0 f +extern VOID o9b0(LPBYTE I); // ?A>B f +extern VOID o9b1(LPBYTE I); // ?B>C f +extern VOID o9b2(LPBYTE I); // ?C>A f +extern VOID o9b3(LPBYTE I); // ?D>C f +extern VOID o9b4(LPBYTE I); // ?A=B f +extern VOID o9b9(LPBYTE I); // ?B>=C f +extern VOID o9bA(LPBYTE I); // ?C>=A f +extern VOID o9bB(LPBYTE I); // ?D>=C f +extern VOID o9bC(LPBYTE I); // ?A<=B f +extern VOID o9bD(LPBYTE I); // ?B<=C f +extern VOID o9bE(LPBYTE I); // ?C<=A f +extern VOID o9bF(LPBYTE I); // ?D<=C f +extern VOID oAa0(LPBYTE I); // A=A+B f +extern VOID oAa1(LPBYTE I); // B=B+C f +extern VOID oAa2(LPBYTE I); // C=C+A f +extern VOID oAa3(LPBYTE I); // D=D+C f +extern VOID oAa4(LPBYTE I); // A=A+A f +extern VOID oAa5(LPBYTE I); // B=B+B f +extern VOID oAa6(LPBYTE I); // C=C+C f +extern VOID oAa7(LPBYTE I); // D=D+D f +extern VOID oAa8(LPBYTE I); // B=B+A f +extern VOID oAa9(LPBYTE I); // C=C+B f +extern VOID oAaA(LPBYTE I); // A=A+C f +extern VOID oAaB(LPBYTE I); // C=C+D f +extern VOID oAaC(LPBYTE I); // A=A-1 f +extern VOID oAaD(LPBYTE I); // B=B-1 f +extern VOID oAaE(LPBYTE I); // C=C-1 f +extern VOID oAaF(LPBYTE I); // D=D-1 f +extern VOID oAb0(LPBYTE I); // A=0 f +extern VOID oAb1(LPBYTE I); // B=0 f +extern VOID oAb2(LPBYTE I); // C=0 f +extern VOID oAb3(LPBYTE I); // D=0 f +extern VOID oAb4(LPBYTE I); // A=B f +extern VOID oAb5(LPBYTE I); // B=C f +extern VOID oAb6(LPBYTE I); // C=A f +extern VOID oAb7(LPBYTE I); // D=C f +extern VOID oAb8(LPBYTE I); // B=A f +extern VOID oAb9(LPBYTE I); // C=B f +extern VOID oAbA(LPBYTE I); // A=C f +extern VOID oAbB(LPBYTE I); // C=D f +extern VOID oAbC(LPBYTE I); // ABEX f +extern VOID oAbD(LPBYTE I); // BCEX f +extern VOID oAbE(LPBYTE I); // CAEX f +extern VOID oAbF(LPBYTE I); // DCEX f +extern VOID oBa0(LPBYTE I); // A=A-B f +extern VOID oBa1(LPBYTE I); // B=B-C f +extern VOID oBa2(LPBYTE I); // C=C-A f +extern VOID oBa3(LPBYTE I); // D=D-C f +extern VOID oBa4(LPBYTE I); // A=A+1 f +extern VOID oBa5(LPBYTE I); // B=B+1 f +extern VOID oBa6(LPBYTE I); // C=C+1 f +extern VOID oBa7(LPBYTE I); // D=D+1 f +extern VOID oBa8(LPBYTE I); // B=B-A f +extern VOID oBa9(LPBYTE I); // C=C-B f +extern VOID oBaA(LPBYTE I); // A=A-C f +extern VOID oBaB(LPBYTE I); // C=C-D f +extern VOID oBaC(LPBYTE I); // A=B-A f +extern VOID oBaD(LPBYTE I); // B=C-B f +extern VOID oBaE(LPBYTE I); // C=A-C f +extern VOID oBaF(LPBYTE I); // D=C-D f +extern VOID oBb0(LPBYTE I); // ASL f +extern VOID oBb1(LPBYTE I); // BSL f +extern VOID oBb2(LPBYTE I); // CSL f +extern VOID oBb3(LPBYTE I); // DSL f +extern VOID oBb4(LPBYTE I); // ASR f +extern VOID oBb5(LPBYTE I); // BSR f +extern VOID oBb6(LPBYTE I); // CSR f +extern VOID oBb7(LPBYTE I); // DSR f +extern VOID oBb8(LPBYTE I); // A=-A f +extern VOID oBb9(LPBYTE I); // B=-B f +extern VOID oBbA(LPBYTE I); // C=-C f +extern VOID oBbB(LPBYTE I); // D=-D f +extern VOID oBbC(LPBYTE I); // A=-A-1 f +extern VOID oBbD(LPBYTE I); // B=-B-1 f +extern VOID oBbE(LPBYTE I); // C=-C-1 f +extern VOID oBbF(LPBYTE I); // D=-D-1 f +extern VOID oC0(LPBYTE I); // A=A+B A +extern VOID oC1(LPBYTE I); // B=B+C A +extern VOID oC2(LPBYTE I); // C=C+A A +extern VOID oC3(LPBYTE I); // D=D+C A +extern VOID oC4(LPBYTE I); // A=A+A A +extern VOID oC5(LPBYTE I); // B=B+B A +extern VOID oC6(LPBYTE I); // C=C+C A +extern VOID oC7(LPBYTE I); // D=D+D A +extern VOID oC8(LPBYTE I); // B=B+A A +extern VOID oC9(LPBYTE I); // C=C+B A +extern VOID oCA(LPBYTE I); // A=A+C A +extern VOID oCB(LPBYTE I); // C=C+D A +extern VOID oCC(LPBYTE I); // A=A-1 A +extern VOID oCD(LPBYTE I); // B=B-1 A +extern VOID oCE(LPBYTE I); // C=C-1 A +extern VOID oCF(LPBYTE I); // D=D-1 A +extern VOID oD0(LPBYTE I); // A=0 A +extern VOID oD1(LPBYTE I); // B=0 A +extern VOID oD2(LPBYTE I); // C=0 A +extern VOID oD3(LPBYTE I); // D=0 A +extern VOID oD4(LPBYTE I); // A=B A +extern VOID oD5(LPBYTE I); // B=C A +extern VOID oD6(LPBYTE I); // C=A A +extern VOID oD7(LPBYTE I); // D=C A +extern VOID oD8(LPBYTE I); // B=A A +extern VOID oD9(LPBYTE I); // C=B A +extern VOID oDA(LPBYTE I); // A=C A +extern VOID oDB(LPBYTE I); // C=D A +extern VOID oDC(LPBYTE I); // ABEX +extern VOID oDD(LPBYTE I); // BCEX +extern VOID oDE(LPBYTE I); // CAEX +extern VOID oDF(LPBYTE I); // DCEX +extern VOID oE0(LPBYTE I); // A=A-B A +extern VOID oE1(LPBYTE I); // B=B-C A +extern VOID oE2(LPBYTE I); // C=C-A A +extern VOID oE3(LPBYTE I); // D=D-C A +extern VOID oE4(LPBYTE I); // A=A+1 A +extern VOID oE5(LPBYTE I); // B=B+1 A +extern VOID oE6(LPBYTE I); // C=C+1 A +extern VOID oE7(LPBYTE I); // D=D+1 A +extern VOID oE8(LPBYTE I); // B=B-A A +extern VOID oE9(LPBYTE I); // C=C-B A +extern VOID oEA(LPBYTE I); // A=A-C A +extern VOID oEB(LPBYTE I); // C=C-D A +extern VOID oEC(LPBYTE I); // A=B-A A +extern VOID oED(LPBYTE I); // B=C-B A +extern VOID oEE(LPBYTE I); // C=A-C A +extern VOID oEF(LPBYTE I); // D=C-D A +extern VOID oF0(LPBYTE I); // ASL A +extern VOID oF1(LPBYTE I); // BSL A +extern VOID oF2(LPBYTE I); // CSL A +extern VOID oF3(LPBYTE I); // DSL A +extern VOID oF4(LPBYTE I); // ASR A +extern VOID oF5(LPBYTE I); // BSR A +extern VOID oF6(LPBYTE I); // CSR A +extern VOID oF7(LPBYTE I); // DSR A +extern VOID oF8(LPBYTE I); // A=-A A +extern VOID oF9(LPBYTE I); // B=-B A +extern VOID oFA(LPBYTE I); // C=-C A +extern VOID oFB(LPBYTE I); // D=-D A +extern VOID oFC(LPBYTE I); // A=-A-1 A +extern VOID oFD(LPBYTE I); // B=-B-1 A +extern VOID oFE(LPBYTE I); // C=-C-1 A +extern VOID oFF(LPBYTE I); // D=-D-1 A + +extern VOID o_invalid3(LPBYTE I); +extern VOID o_invalid4(LPBYTE I); +extern VOID o_invalid5(LPBYTE I); +extern VOID o_invalid6(LPBYTE I); + +extern VOID o_goyes3(LPBYTE I); +extern VOID o_goyes5(LPBYTE I); + +extern VOID o81B1(LPBYTE I); // beep patch diff --git a/SOURCE/OPS.H b/SOURCE/OPS.H new file mode 100644 index 0000000..b6e2b6b --- /dev/null +++ b/SOURCE/OPS.H @@ -0,0 +1,488 @@ +/* + * ops.h + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ + +#define NFunpack(a, b, f) Nunpack((a)+F_s[f], b, F_l[f]) +#define NFread(a, b, f) Nread((a)+F_s[f], b, F_l[f]) +#define NFwrite(a, b, f) Nwrite((a)+F_s[f], b, F_l[f]) +#define NFcopy(a, b, f) memcpy((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFxchg(a, b, f) Nxchg((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFadd(a, b, f) Nadd((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFsub(a, b, f) Nsub((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFrsub(a, b, f) Nrsub((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFand(a, b, f) Nand((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFor(a, b, f) Nor((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define NFzero(a,f) memset((a)+F_s[f], 0, F_l[f]) +#define NFpack(a, f) Npack((a)+F_s[f], F_l[f]) +#define NFinc(a, f) Ninc(a, F_l[f], F_s[f]) +#define NFdec(a, f) Ndec(a, F_l[f], F_s[f]) +#define NFnot(a, f) Nnot((a)+F_s[f], F_l[f]) +#define NFneg(a, f) Nneg((a)+F_s[f], F_l[f]) +#define NFsl(a, f) Nsl((a)+F_s[f], F_l[f]) +#define NFsr(a, f) Nsr((a)+F_s[f], F_l[f]) +#define NFsrb(a, f) Nsrb((a)+F_s[f], F_l[f]) +#define TFe(a, b, f) Te((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define TFa(a, b, f) Ta((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define TFb(a, b, f) Tb((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define TFz(a, f) Tz((a)+F_s[f], F_l[f]) +#define TFne(a, b, f) Tne((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define TFae(a, b, f) Tae((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define TFbe(a, b, f) Tbe((a)+F_s[f], (b)+F_s[f], F_l[f]) +#define TFnz(a, f) Tnz((a)+F_s[f], F_l[f]) + +static __inline LPBYTE FASTPTR(DWORD d) +{ + static BYTE pbyNULL[16]; + LPBYTE lpbyPage; + + d &= 0xFFFFF; // handle address overflows + + if ((Chipset.IOCfig)&&((d&0xFFFC0)==Chipset.IOBase)) + return Chipset.IORam+d-Chipset.IOBase; + + if((lpbyPage = RMap[d>>12]) != NULL) // page valid + { + lpbyPage += d & 0xFFF; // full address + } + else + { + lpbyPage = pbyNULL; // memory allocation + Npeek(lpbyPage, d, 13); // fill with data (LA(8) = longest opcode) + } + return lpbyPage; +} + +static __inline void rstkpush(DWORD d) +{ + Chipset.rstk[Chipset.rstkp] = d; + Chipset.rstkp=(Chipset.rstkp+1)&7; +} + +static __inline DWORD rstkpop() +{ + DWORD r; + + Chipset.rstkp=(Chipset.rstkp-1)&7; + r = Chipset.rstk[Chipset.rstkp]; + Chipset.rstk[Chipset.rstkp] = 0; + return r; +} + +static __inline DWORD Npack(BYTE *a, UINT s) +{ + DWORD r = 0; + + while (s--) r = (r<<4)|a[s]; + return r; +} + +static __inline VOID Nunpack(BYTE *a, DWORD b, UINT s) +{ + UINT i; + for (i=0; i>=4; } +} + +static __inline QWORD Npack64(BYTE *a, UINT s) +{ + QWORD r = 0; + + while (s--) r = (r<<4)|a[s]; + return r; +} + +static __inline VOID Nunpack64(BYTE *a, QWORD b, UINT s) +{ + UINT i; + for (i=0; i>=4; } +} + +static __inline void Nxchg(BYTE *a, BYTE *b, UINT s) +{ + BYTE X[16]; + + memcpy(X, b, s); + memcpy(b, a, s); + memcpy(a, X, s); +} + +static __inline void Ninc(BYTE *a, UINT s, UINT d) +{ + UINT i; + + if (Chipset.mode_dec) + { + BYTE c = 1; + for (i=d; iA)); + + // illegal number in dec mode + if (a[i] >= 10) a[i] &= 0x7; + + a[i] += c; + c = (a[i] >= 10); + if (c) a[i] -= 10; + } + Chipset.carry = (c==1); + } + else + { + for (i=d; iA)); + + a[i]++; + if (a[i] < 16) + { + Chipset.carry = FALSE; + return; + } + a[i] -= 16; + } + Chipset.carry = TRUE; + } +} + +static __inline void Ninc16(BYTE *a, UINT s, UINT d) +{ + UINT i; + + for (i=d; iA)); + + a[i]--; + if ((a[i] & 0xF0) == 0) // check overflow + { + Chipset.carry = FALSE; + return; + } + a[i] += cBase; + } + Chipset.carry = TRUE; +} + +static __inline void Ndec16(BYTE *a, UINT s, UINT d) +{ + UINT i; + + for (i=d; i= cBase) a[i] &= 0x7; + + a[i] += b[i] + c; + if (a[i] >= cBase) + { + a[i] -= cBase; + c = 1; + } + else + c = 0; + } + Chipset.carry = (c==1); +} + +static __inline void Nsub(BYTE *a, BYTE *b, UINT s) +{ + UINT i; + BYTE c = 0; + BYTE cBase = Chipset.mode_dec ? 10 : 16; + + for (i=0; i 0); // check for non-zero digit + for (--a[i]; i>= 1; + *a |= ((a[1] & 1) << 3); + a++; + } + *a >>= 1; +} + +static __inline void Nbit0(BYTE *a, UINT b) +{ + a[b>>2] &= ~(1<<(b&3)); +} + +static __inline void Nbit1(BYTE *a, UINT b) +{ + a[b>>2] |= 1<<(b&3); +} + +static __inline void Tbit0(BYTE *a, UINT b) +{ + if (a[b>>2] & (1<<(b&3))) + Chipset.carry = FALSE; + else + Chipset.carry = TRUE; +} + +static __inline void Tbit1(BYTE *a, UINT b) +{ + if (a[b>>2] & (1<<(b&3))) + Chipset.carry = TRUE; + else + Chipset.carry = FALSE; +} + +static __inline void Te(BYTE *a, BYTE *b, UINT s) +{ + while (s--) + { + if (a[s]!=b[s]) + { + Chipset.carry = FALSE; + return; + } + } + Chipset.carry = TRUE; +} + +static __inline void Tne(BYTE *a, BYTE *b, UINT s) +{ + while (s--) + { + if (a[s]!=b[s]) + { + Chipset.carry = TRUE; + return; + } + } + Chipset.carry = FALSE; +} + +static __inline void Tz(BYTE *a, UINT s) +{ + while (s--) + { + if (a[s]!=0) + { + Chipset.carry = FALSE; + return; + } + } + Chipset.carry = TRUE; +} + +static __inline void Tnz(BYTE *a, UINT s) +{ + while (s--) + { + if (a[s]!=0) + { + Chipset.carry = TRUE; + return; + } + } + Chipset.carry = FALSE; +} + +static __inline void Ta(BYTE *a, BYTE *b, UINT s) +{ + while (--s) if (a[s]!=b[s]) break; + if (a[s]>b[s]) + Chipset.carry = TRUE; + else + Chipset.carry = FALSE; +} + +static __inline void Tb(BYTE *a, BYTE *b, UINT s) +{ + while (--s) if (a[s]!=b[s]) break; + if (a[s]=b[s]) + Chipset.carry = TRUE; + else + Chipset.carry = FALSE; +} + +static __inline void Tbe(BYTE *a, BYTE *b, UINT s) +{ + while (--s) if (a[s]!=b[s]) break; + if (a[s]<=b[s]) + Chipset.carry = TRUE; + else + Chipset.carry = FALSE; +} diff --git a/SOURCE/PCH.C b/SOURCE/PCH.C new file mode 100644 index 0000000..c20bfab --- /dev/null +++ b/SOURCE/PCH.C @@ -0,0 +1,5 @@ +// +// PCH.C +// + +#include "pch.h" diff --git a/SOURCE/PCH.H b/SOURCE/PCH.H new file mode 100644 index 0000000..94ac156 --- /dev/null +++ b/SOURCE/PCH.H @@ -0,0 +1,33 @@ +// +// PCH.H +// + +#define _WIN32_IE 0x0200 +#define _CRT_SECURE_NO_DEPRECATE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if !defined VERIFY +#if defined _DEBUG +#define VERIFY(f) _ASSERT(f) +#else // _DEBUG +#define VERIFY(f) ((VOID)(f)) +#endif // _DEBUG +#endif // _VERIFY + +#if !defined IDC_HAND // Win2k specific definition +#define IDC_HAND MAKEINTRESOURCE(32649) +#endif + +// missing type definition in the MSVC6.0 SDK +typedef SIZE_T DWORD_PTR, *PDWORD_PTR; diff --git a/SOURCE/RESOURCE.H b/SOURCE/RESOURCE.H new file mode 100644 index 0000000..8eef57d --- /dev/null +++ b/SOURCE/RESOURCE.H @@ -0,0 +1,225 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by Emu48.rc +// +#define IDI_EMU48 100 +#define IDR_MENU 101 +#define IDR_DEBUG 102 +#define IDR_DEBUG_TOOLBAR 103 +#define IDR_DEBUG_CODE 104 +#define IDR_DEBUG_MEM 105 +#define IDR_DEBUG_STACK 106 +#define IDB_CHECKBOX 107 +#define IDD_ABOUT 108 +#define IDD_SETTINGS 109 +#define IDD_CHOOSEKML 110 +#define IDD_KMLLOG 111 +#define IDD_DISASM 112 +#define IDD_DEBUG 113 +#define IDD_NEWVALUE 114 +#define IDD_ENTERADR 115 +#define IDD_BREAKEDIT 116 +#define IDD_ENTERBREAK 117 +#define IDD_INSTRUCTIONS 118 +#define IDD_WRITEONLYREG 119 +#define IDD_FIND 120 +#define IDD_PROFILE 121 +#define IDD_MACROSET 122 +#define IDC_REALSPEED 1000 +#define IDC_GRAYSCALE 1001 +#define IDC_AUTOSAVE 1002 +#define IDC_AUTOSAVEONEXIT 1003 +#define IDC_OBJECTLOADWARNING 1004 +#define IDC_ALWAYSDISPLOG 1005 +#define IDC_PORT1EN 1006 +#define IDC_PORT1WR 1007 +#define IDC_PORT2ISSHARED 1008 +#define IDC_PORT2WR 1009 +#define IDC_PORT2 1010 +#define IDC_PORT2LOAD 1011 +#define IDC_WIRE 1012 +#define IDC_IR 1013 +#define IDC_EMUDIR 1014 +#define IDC_EMUDIRSEL 1015 +#define IDC_UPDATE 1016 +#define IDC_KMLSCRIPT 1017 +#define IDC_AUTHOR 1018 +#define IDC_TITLE 1019 +#define IDC_KMLLOG 1020 +#define IDC_VERSION 1021 +#define IDC_LICENSE 1022 +#define IDC_DISASM_WIN 1023 +#define IDC_DISASM_MAP 1024 +#define IDC_DISASM_ROM 1025 +#define IDC_DISASM_RAM 1026 +#define IDC_DISASM_PORT1 1027 +#define IDC_DISASM_PORT2 1028 +#define IDC_DISASM_MODULE 1029 +#define IDC_DISASM_HP 1030 +#define IDC_DISASM_CLASS 1031 +#define IDC_ADDRESS 1032 +#define IDC_DISASM_ADR 1033 +#define IDC_DISASM_NEXT 1034 +#define IDC_DISASM_COPY 1035 +#define IDC_DEBUG_CODE 1036 +#define IDC_STATIC_CODE 1037 +#define IDC_STATIC_REGISTERS 1038 +#define IDC_STATIC_MEMORY 1039 +#define IDC_STATIC_STACK 1040 +#define IDC_REG_A 1041 +#define IDC_REG_B 1042 +#define IDC_REG_C 1043 +#define IDC_REG_D 1044 +#define IDC_REG_R0 1045 +#define IDC_REG_R1 1046 +#define IDC_REG_R2 1047 +#define IDC_REG_R3 1048 +#define IDC_REG_R4 1049 +#define IDC_REG_D0 1050 +#define IDC_REG_D1 1051 +#define IDC_REG_P 1052 +#define IDC_REG_PC 1053 +#define IDC_REG_OUT 1054 +#define IDC_REG_IN 1055 +#define IDC_REG_ST 1056 +#define IDC_REG_CY 1057 +#define IDC_REG_MODE 1058 +#define IDC_REG_MP 1059 +#define IDC_REG_SR 1060 +#define IDC_REG_SB 1061 +#define IDC_REG_XM 1062 +#define IDC_MISC_INT 1063 +#define IDC_MISC_KEY 1064 +#define IDC_MISC_BS 1065 +#define IDC_NEWVALUE 1066 +#define IDC_ENTERADR 1067 +#define IDC_DEBUG_MEM 1068 +#define IDC_DEBUG_MEM_ADDR 1069 +#define IDC_DEBUG_MEM_COL0 1070 +#define IDC_DEBUG_MEM_COL1 1071 +#define IDC_DEBUG_MEM_COL2 1072 +#define IDC_DEBUG_MEM_COL3 1073 +#define IDC_DEBUG_MEM_COL4 1074 +#define IDC_DEBUG_MEM_COL5 1075 +#define IDC_DEBUG_MEM_COL6 1076 +#define IDC_DEBUG_MEM_COL7 1077 +#define IDC_DEBUG_MEM_TEXT 1078 +#define IDC_DEBUG_STACK 1079 +#define IDC_STATIC_BREAKPOINT 1080 +#define IDC_BREAKEDIT_ADD 1081 +#define IDC_BREAKEDIT_DELETE 1082 +#define IDC_BREAKEDIT_WND 1083 +#define IDC_STATIC_MMU 1084 +#define IDC_MMU_IO_A 1085 +#define IDC_MMU_NCE2_A 1086 +#define IDC_MMU_CE1_A 1087 +#define IDC_MMU_CE2_A 1088 +#define IDC_MMU_NCE3_A 1089 +#define IDC_MMU_IO_S 1090 +#define IDC_MMU_CE1_S 1091 +#define IDC_MMU_CE2_S 1092 +#define IDC_MMU_NCE2_S 1093 +#define IDC_MMU_NCE3_S 1094 +#define IDC_STATIC_MISC 1095 +#define IDC_MISC_BS_TXT 1096 +#define IDC_INSTR_TEXT 1097 +#define IDC_INSTR_CODE 1098 +#define IDC_INSTR_COPY 1099 +#define IDC_INSTR_CLEAR 1100 +#define IDC_PROFILE_LASTCYCLES 1101 +#define IDC_PROFILE_LASTTIME 1102 +#define IDC_BPCODE 1103 +#define IDC_BPRPL 1104 +#define IDC_BPACCESS 1105 +#define IDC_BPREAD 1106 +#define IDC_BPWRITE 1107 +#define IDC_FIND_DATA 1108 +#define IDC_FIND_ASCII 1109 +#define IDC_FIND_CASE 1110 +#define IDC_ADDR20_24 1111 +#define IDC_ADDR25_27 1112 +#define IDC_ADDR28_29 1113 +#define IDC_ADDR30_34 1114 +#define IDC_MACRO_SLOW 1115 +#define IDC_MACRO_FAST 1116 +#define IDC_MACRO_SLIDER 1117 +#define IDC_MACRO_REAL 1118 +#define IDC_MACRO_MANUAL 1119 +#define IDC_SOUND_SLIDER 1120 +#define IDC_SOUND_SPEAKER 1121 +#define IDC_SOUND_WAVE 1122 +#define ID_FILE_NEW 40001 +#define ID_FILE_OPEN 40002 +#define ID_FILE_SAVE 40003 +#define ID_FILE_SAVEAS 40004 +#define ID_FILE_EXIT 40005 +#define ID_VIEW_COPY 40006 +#define ID_VIEW_SETTINGS 40007 +#define ID_VIEW_RESET 40008 +#define ID_OBJECT_LOAD 40009 +#define ID_OBJECT_SAVE 40010 +#define ID_ABOUT 40011 +#define ID_FILE_CLOSE 40013 +#define ID_BACKUP_SAVE 40014 +#define ID_BACKUP_RESTORE 40015 +#define ID_BACKUP_DELETE 40016 +#define ID_VIEW_SCRIPT 40017 +#define ID_STACK_COPY 40019 +#define ID_STACK_PASTE 40020 +#define ID_TOOL_DISASM 40021 +#define ID_TOOL_DEBUG 40022 +#define ID_TOOL_MACRO_RECORD 40023 +#define ID_TOOL_MACRO_PLAY 40024 +#define ID_TOOL_MACRO_STOP 40025 +#define ID_TOOL_MACRO_SETTINGS 40026 +#define ID_DEBUG_RUN 40027 +#define ID_DEBUG_RUNCURSOR 40028 +#define ID_DEBUG_STEP 40029 +#define ID_DEBUG_STEPOVER 40030 +#define ID_DEBUG_BREAK 40031 +#define ID_DEBUG_STEPOUT 40032 +#define ID_DEBUG_CANCEL 40033 +#define ID_BREAKPOINTS_SETBREAK 40034 +#define ID_BREAKPOINTS_CODEEDIT 40035 +#define ID_BREAKPOINTS_CLEARALL 40036 +#define ID_BREAKPOINTS_NOP3 40037 +#define ID_BREAKPOINTS_DOCODE 40038 +#define ID_BREAKPOINTS_RPL 40039 +#define ID_DEBUG_CODE_GOADR 40040 +#define ID_DEBUG_CODE_GOPC 40041 +#define ID_DEBUG_CODE_SETPCTOSELECT 40042 +#define ID_DEBUG_MEM_GOADR 40043 +#define ID_DEBUG_MEM_GOPC 40044 +#define ID_DEBUG_MEM_GOD0 40045 +#define ID_DEBUG_MEM_GOD1 40046 +#define ID_DEBUG_MEM_GOSTACK 40047 +#define ID_DEBUG_MEM_FNONE 40048 +#define ID_DEBUG_MEM_FADDR 40049 +#define ID_DEBUG_MEM_FPC 40050 +#define ID_DEBUG_MEM_FD0 40051 +#define ID_DEBUG_MEM_FD1 40052 +#define ID_DEBUG_MEM_FIND 40053 +#define ID_DEBUG_MEM_MAP 40054 +#define ID_DEBUG_MEM_NCE1 40055 +#define ID_DEBUG_MEM_NCE2 40056 +#define ID_DEBUG_MEM_CE1 40057 +#define ID_DEBUG_MEM_CE2 40058 +#define ID_DEBUG_MEM_NCE3 40059 +#define ID_DEBUG_STACK_PUSH 40060 +#define ID_DEBUG_STACK_POP 40061 +#define ID_DEBUG_STACK_MODIFY 40062 +#define ID_INTR_STEPOVERINT 40063 +#define ID_INFO_LASTINSTRUCTIONS 40064 +#define ID_INFO_PROFILE 40065 +#define ID_INFO_WRITEONLYREG 40066 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 123 +#define _APS_NEXT_COMMAND_VALUE 40067 +#define _APS_NEXT_CONTROL_VALUE 1123 +#define _APS_NEXT_SYMED_VALUE 108 +#endif +#endif diff --git a/SOURCE/RPL.C b/SOURCE/RPL.C new file mode 100644 index 0000000..9caacc6 --- /dev/null +++ b/SOURCE/RPL.C @@ -0,0 +1,419 @@ +/* + * rpl.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "Emu48.h" +#include "ops.h" +#include "io.h" + +//| 38G | 39G | 40G | 48SX | 48GX | 49G | Name +//#F0688 #806E9 #806E9 #7056A #806E9 #806E9 =TEMPOB +//#F068D #806EE #806EE #7056F #806EE #806EE =TEMPTOP +//#F0692 #806F3 #806F3 #70574 #806F3 #806F3 =RSKTOP (B) +//#F0697 #806F8 #806F8 #70579 #806F8 #806F8 =DSKTOP (D1) +//#F069C #806FD #806FD #7057E #806FD #806FD =EDITLINE +//#F0DEA #80E9B #80E9B #7066E #807ED #80E9B =AVMEM (D) +//#F0705 #8076B #8076B #705B0 #8072F #8076B =INTRPPTR (D0) +//#F0E42 #80F02 #80F02 #706C5 #80843 #80F02 =SystemFlags + +#define TEMPOB ((cCurrentRomType=='S')?0x7056A:0x806E9) +#define TEMPTOP ((cCurrentRomType=='S')?0x7056F:0x806EE) +#define RSKTOP ((cCurrentRomType=='S')?0x70574:0x806F3) +#define DSKTOP ((cCurrentRomType=='S')?0x70579:0x806F8) +#define EDITLINE ((cCurrentRomType=='S')?0x7057E:0x806FD) +// CdB for HP: add apples +#define AVMEM ((cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q')?((cCurrentRomType=='S')?0x7066E:0x807ED):0x80E9B) +#define INTRPPTR ((cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q')?((cCurrentRomType=='S')?0x705B0:0x8072F):0x8076B) +#define SYSTEMFLAGS ((cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q')?((cCurrentRomType=='S')?0x706C5:0x80843):0x80F02) + +#define DOINT 0x02614 // Precision Integer (HP49G) +#define DOLNGREAL 0x0263A // Precision Real (HP49G) +#define DOLNGCMP 0x02660 // Precision Complex (HP49G) +#define DOMATRIX 0x02686 // Symbolic matrix (HP49G) +#define DOFLASHP 0x026AC // Flash PTR (HP49G) +#define DOAPLET 0x026D5 // Aplet (HP49G) +#define DOMINIFONT 0x026FE // Mini Font (HP49G) +#define DOBINT 0x02911 // System Binary +#define DOREAL 0x02933 // Real +#define DOEREAL 0x02955 // Long Real +#define DOCMP 0x02977 // Complex +#define DOECMP 0x0299D // Long Complex +#define DOCHAR 0x029BF // Character +#define DOARRY 0x029E8 // Array +#define DOLNKARRY 0x02A0A // Linked Array +#define DOCSTR 0x02A2C // String +#define DOHSTR 0x02A4E // Binary Integer +#define DOLIST 0x02A74 // List +#define DORRP 0x02A96 // Directory +#define DOSYMB 0x02AB8 // Algebraic +#define DOTAG 0x02AFC // Tagged +#define DOEXT1 0x02BAA // Extended Pointer +#define DOEXT 0x02ADA // Unit +#define DOGROB 0x02B1E // Graphic +#define DOLIB 0x02B40 // Library +#define DOBAK 0x02B62 // Backup +#define DOEXT0 0x02B88 // Library Data +#define DOEXT2 0x02BCC // Reserved 1, Font (HP49G) +#define DOEXT3 0x02BEE // Reserved 2 +#define DOEXT4 0x02C10 // Reserved 3 +#define DOCOL 0x02D9D // Program +#define DOCODE 0x02DCC // Code +#define DOIDNT 0x02E48 // Global Name +#define DOLAM 0x02E6D // Local Name +#define DOROMP 0x02E92 // XLIB Name +#define SEMI 0x0312B // ; + +#define GARBAGECOL 0x0613E // =GARBAGECOL entry for HP48S/G/GII and HP49G(+) + +// check for Metakernel version +#define METAKERNEL Metakernel() + +// search for "MDGKER:MK2.30" or "MDGKER:PREVIE" in port1 of a HP48GX +static BOOL Metakernel(VOID) +{ + BOOL bMkDetect = FALSE; + + // card in slot1 of a HP48GX enabled + if (cCurrentRomType=='G' && Chipset.Port1 && Chipset.cards_status & PORT1_PRESENT) + { + // check for Metakernel string "MDGKER:" + if (!strncmp(&Chipset.Port1[12],"\xD\x4\x4\x4\x7\x4\xB\x4\x5\x4\x2\x5\xA\x3",14)) + { + bMkDetect = TRUE; // Metakernel detected + // check for "MK" + if (!strncmp(&Chipset.Port1[26],"\xD\x4\xB\x4",4)) + { + // get version number + WORD wVersion = ((Chipset.Port1[30] * 10) + Chipset.Port1[34]) * 10 + + Chipset.Port1[36]; + + // version newer then V2.30, then compatible with HP OS + bMkDetect = (wVersion <= 230); + } + } + } + return bMkDetect; +} + +static DWORD RPL_GarbageCol(VOID) // RPL variables must be in system RAM +{ + CHIPSET OrgChipset; + DWORD dwAVMEM; + + // only for HP48SX, HP48GX, HP49G, HP48GII and HP49G+ + _ASSERT( cCurrentRomType == 'S' || cCurrentRomType == 'G' || cCurrentRomType == 'X' + || cCurrentRomType == '2' || cCurrentRomType == 'Q'); + + OrgChipset = Chipset; // save original chipset + + // entry for =GARBAGECOL + Chipset.P = 0; // P=0 + Chipset.mode_dec = FALSE; // hex mode + Chipset.pc = GARBAGECOL; // =GARBAGECOL entry + rstkpush(0xFFFFF); // return address for stopping + + while (Chipset.pc != 0xFFFFF) // wait for stop address + { + EvalOpcode(FASTPTR(Chipset.pc)); // execute opcode + } + + dwAVMEM = Npack(Chipset.C,5); // available AVMEM + Chipset = OrgChipset; // restore original chipset + return dwAVMEM; +} + +BOOL RPL_GetSystemFlag(INT nFlag) +{ + DWORD dwAddr; + BYTE byMask,byFlag; + + _ASSERT(nFlag > 0); // first flag is 1 + + // calculate memory address and bit mask + dwAddr = SYSTEMFLAGS + (nFlag - 1) / 4; + byMask = 1 << ((nFlag - 1) & 0x3); + + Npeek(&byFlag,dwAddr,sizeof(byFlag)); + return (byFlag & byMask) != 0; +} + +DWORD RPL_SkipOb(DWORD d) +{ + BYTE X[8]; + DWORD n, l; + + Npeek(X,d,5); + n = Npack(X, 5); // read prolog + switch (n) + { + case DOFLASHP: l = (cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') ? 5 : 12; break; // Flash PTR (HP49G) // CdB for HP: add apples + case DOBINT: l = 10; break; // System Binary + case DOREAL: l = 21; break; // Real + case DOEREAL: l = 26; break; // Long Real + case DOCMP: l = 37; break; // Complex + case DOECMP: l = 47; break; // Long Complex + case DOCHAR: l = 7; break; // Character + case DOEXT1: l = 15; break; // Extended Pointer + case DOROMP: l = 11; break; // XLIB Name + case DOMATRIX: // Symbolic matrix (HP49G) + if (cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') // CdB for HP: add apples + { + l = 5; + break; + } + case DOLIST: // List + case DOSYMB: // Algebraic + case DOEXT: // Unit + case DOCOL: // Program + n=d+5; + do + { + d=n; n=RPL_SkipOb(d); + } while (d!=n); + return n+5; + case SEMI: return d; // SEMI + case DOIDNT: // Global Name + case DOLAM: // Local Name + case DOTAG: // Tagged + Npeek(X,d+5,2); n = 7 + Npack(X,2)*2; + return RPL_SkipOb(d+n); + case DORRP: // Directory + d+=8; + n = Read5(d); + if (n==0) + { + return d+5; + } + else + { + d+=n; + Npeek(X,d,2); + n = Npack(X,2)*2 + 4; + return RPL_SkipOb(d+n); + } + case DOINT: // Precision Integer (HP49G) + case DOAPLET: // Aplet (HP49G) + case DOMINIFONT: // Mini Font (HP49G) + if (cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') // CdB for HP: add apples + { + l = 5; + break; + } + case DOARRY: // Array + case DOLNKARRY: // Linked Array + case DOCSTR: // String + case DOHSTR: // Binary Integer + case DOGROB: // Graphic + case DOLIB: // Library + case DOBAK: // Backup + case DOEXT0: // Library Data + case DOEXT2: // Reserved 1, Font (HP49G) + case DOEXT3: // Reserved 2 + case DOEXT4: // Reserved 3 + case DOCODE: // Code + l = 5+Read5(d+5); + break; + case DOLNGREAL: // Precision Real (HP49G) + l = 5; + if (cCurrentRomType=='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') // CdB for HP: add apples + { + l += Read5(d+l); + l += Read5(d+l); + } + break; + case DOLNGCMP: // Precision Complex (HP49G) + l = 5; + if (cCurrentRomType=='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') // CdB for HP: add apples + { + l += Read5(d+l); + l += Read5(d+l); + l += Read5(d+l); + l += Read5(d+l); + } + break; + default: return d+5; + } + return d+l; +} + +DWORD RPL_ObjectSize(BYTE *o) +{ + DWORD n, l = 0; + + n = Npack(o, 5); // read prolog + switch (n) + { + case DOFLASHP: l = (cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') ? 5 : 12; break; // Flash PTR (HP49G) // CdB for HP: add apples + case DOBINT: l = 10; break; // System Binary + case DOREAL: l = 21; break; // Real + case DOEREAL: l = 26; break; // Long Real + case DOCMP: l = 37; break; // Complex + case DOECMP: l = 47; break; // Long Complex + case DOCHAR: l = 7; break; // Character + case DOEXT1: l = 15; break; // Extended Pointer + case DOROMP: l = 11; break; // XLIB Name + case DOMATRIX: // Symbolic matrix (HP49G) + if (cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') // CdB for HP: add apples + { + l = 5; + break; + } + case DOLIST: // List + case DOSYMB: // Algebraic + case DOEXT: // Unit + case DOCOL: // Program + n=5; + do { l+=n; o+=n; n=RPL_ObjectSize(o); } while (n); + l += 5; + break; + case SEMI: l = 0; break; // SEMI + case DOIDNT: // Global Name + case DOLAM: // Local Name + case DOTAG: // Tagged + n = 7 + Npack(o+5,2)*2; + l = n + RPL_ObjectSize(o+n); + break; + case DORRP: // Directory + n = Npack(o+8,5); + if (n==0) // empty dir + { + l=13; + } + else + { + l = 8+n; + n = Npack(o+l,2)*2 + 4; + l += n; + l += RPL_ObjectSize(o+l); + } + break; + case DOINT: // Precision Integer (HP49G) + case DOAPLET: // Aplet (HP49G) + case DOMINIFONT: // Mini Font (HP49G) + if (cCurrentRomType!='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') // CdB for HP: add apples + { + l = 5; + break; + } + case DOARRY: // Array + case DOLNKARRY: // Linked Array + case DOCSTR: // String + case DOHSTR: // Binary Integer + case DOGROB: // Graphic + case DOLIB: // Library + case DOBAK: // Backup + case DOEXT0: // Library Data + case DOEXT2: // Reserved 1, Font (HP49G) + case DOEXT3: // Reserved 2 + case DOEXT4: // Reserved 3 + case DOCODE: // Code + l = 5 + Npack(o+5,5); + break; + case DOLNGREAL: // Precision Real (HP49G) + l = 5; + if (cCurrentRomType=='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') // CdB for HP: add apples + { + l += Npack(o+l,5); + l += Npack(o+l,5); + } + break; + case DOLNGCMP: // Precision Complex (HP49G) + l = 5; + if (cCurrentRomType=='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') // CdB for HP: add apples + { + l += Npack(o+l,5); + l += Npack(o+l,5); + l += Npack(o+l,5); + l += Npack(o+l,5); + } + break; + default: l=5; + } + return l; +} + +DWORD RPL_CreateTemp(DWORD l) +{ + DWORD a, b, c; + BYTE *p; + + l += 6; // memory for link field (5) + marker (1) and end + b = Read5(RSKTOP); // tail address of rtn stack + c = Read5(DSKTOP); // top of data stack + if ((b+l)>c) // there's not enough memory to move DSKTOP + { + RPL_GarbageCol(); // do a garbage collection + b = Read5(RSKTOP); // reload tail address of rtn stack + c = Read5(DSKTOP); // reload top of data stack + } + if ((b+l)>c) return 0; // check if now there's enough memory to move DSKTOP + a = Read5(TEMPTOP); // tail address of top object + Write5(TEMPTOP, a+l); // adjust new end of top object + Write5(RSKTOP, b+l); // adjust new end of rtn stack + Write5(AVMEM, (c-b-l)/5); // calculate free memory (*5 nibbles) + p = HeapAlloc(hHeap,0,b-a); // move down rtn stack + Npeek(p,a,b-a); + Nwrite(p,a+l,b-a); + HeapFree(hHeap,0,p); + Write5(a+l-5,l); // set object length field + return (a+1); // return base address of new object +} + +UINT RPL_Depth(VOID) +{ + return (Read5(EDITLINE) - Read5(DSKTOP)) / 5 - 1; +} + +DWORD RPL_Pick(UINT l) +{ + DWORD stkp; + + _ASSERT(l > 0); // first stack element is one + if (l == 0) return 0; + if (METAKERNEL) ++l; // Metakernel support + if (RPL_Depth() < l) return 0; // not enough elements on stack + stkp = Read5(DSKTOP) + (l-1)*5; + return Read5(stkp); // return object address +} + +VOID RPL_Replace(DWORD n) +{ + DWORD stkp; + + stkp = Read5(DSKTOP); + if (METAKERNEL) stkp+=5; // Metakernel support + Write5(stkp,n); + return; +} + +VOID RPL_Push(UINT l,DWORD n) +{ + UINT i; + DWORD stkp, avmem; + + if (l > RPL_Depth() + 1) return; // invalid stack level + + avmem = Read5(AVMEM); // amount of free memory + if (avmem == 0) return; // no memory free + avmem--; // fetch memory + Write5(AVMEM,avmem); // save new amount of free memory + + if (METAKERNEL) ++l; // Metakernel, save MK object on stack level 1 + + stkp = Read5(DSKTOP) - 5; // get pointer of new stack level 1 + Write5(DSKTOP,stkp); // save it + + for (i = 1; i < l; ++i) // move down stack level entries before insert pos + { + Write5(stkp,Read5(stkp+5)); // move down stack level entry + stkp += 5; // next stack entry + } + + Write5(stkp,n); // save pointer of new object on given stack level + return; +} diff --git a/SOURCE/SERIAL.C b/SOURCE/SERIAL.C new file mode 100644 index 0000000..2b39208 --- /dev/null +++ b/SOURCE/SERIAL.C @@ -0,0 +1,383 @@ +/* + * Serial.c + * + * This file is part of Emu48 + * + * Copyright (C) 1998 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "io.h" + +#define INTERRUPT ((void)(Chipset.SoftInt=TRUE,bInterrupt=TRUE)) + +// state of USRQ +#define NINT2ERBZ ((Chipset.IORam[IOC] & (SON | ERBZ)) == (SON | ERBZ) && (Chipset.IORam[RCS] & RBZ) != 0) +#define NINT2ERBF ((Chipset.IORam[IOC] & (SON | ERBF)) == (SON | ERBF) && (Chipset.IORam[RCS] & RBF) != 0) +#define NINT2ETBE ((Chipset.IORam[IOC] & (SON | ETBE)) == (SON | ETBE) && (Chipset.IORam[TCS] & TBF) == 0) + +#define NINT2USRQ (NINT2ERBZ || NINT2ERBF || NINT2ETBE) + +static HANDLE hComm = NULL; + +static HANDLE hCThreadTxd; +static HANDLE hCThreadEv; + +static HANDLE hEventTxd; +static BOOL bWriting; +static BYTE tbr; + +static BOOL bReading; +static BYTE cBuffer[32]; +static WORD nRp; +static DWORD dwBytesRead; + +static DWORD WINAPI TransmitThread(LPVOID pParam) +{ + OVERLAPPED osWr = { 0 }; + + osWr.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); + + while (bWriting) + { + WaitForSingleObject(hEventTxd,INFINITE); + if (bWriting) + { + DWORD dwWritten; + if (!WriteFile(hComm,(LPCVOID) &tbr,1,&dwWritten,&osWr)) + if (GetLastError() == ERROR_IO_PENDING) + GetOverlappedResult(hComm,&osWr,&dwWritten,TRUE); + } + } + + CloseHandle(osWr.hEvent); // close write event handle + return 0; + UNREFERENCED_PARAMETER(pParam); +} + +static DWORD WINAPI EventThread(LPVOID pParam) +{ + DWORD dwEvent; + + bReading = TRUE; // flag for SerialThread started + while (bReading) + { + _ASSERT(hComm != NULL); + WaitCommEvent(hComm,&dwEvent,NULL); // wait for serial event + if (dwEvent & EV_RXCHAR) // signal char received + { + CommReceive(); // get data + // interrupt request and emulation thread down + if (Chipset.SoftInt && Chipset.Shutdn) + { + Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode + SetEvent(hEventShutdn); // wake up emulation thread + } + } + if (dwEvent & EV_TXEMPTY) // signal transmit buffer empty + { + IOBit(TCS,TBZ,FALSE); // clear transmitter busy bit + CommTransmit(); // check for new char to transmit + } + if (dwEvent & EV_ERR) // signal error received + { + DWORD dwError; + + ClearCommError(hComm,&dwError,NULL); + if (dwError & (CE_FRAME | CE_OVERRUN | CE_BREAK)) + IOBit(RCS,RER,TRUE); // receiver error + } + } + return 0; + UNREFERENCED_PARAMETER(pParam); +} + +BOOL CommOpen(LPTSTR strWirePort,LPTSTR strIrPort) +{ + COMMTIMEOUTS CommTimeouts = { MAXDWORD, 0L, 0L, 0L, 0L }; + + LPCTSTR strPort = (Chipset.IORam[IR_CTRL] & EIRU) ? strIrPort : strWirePort; + + _ASSERT(Chipset.IORam[IOC] & SON); // UART on + CommClose(); // close port if already open + + if (lstrcmp(strPort, _T(NO_SERIAL))) // port defined + { + TCHAR szDevice[16] = _T("\\\\.\\"); + + // check if device buffer is big enough + _ASSERT(lstrlen(szDevice) + lstrlen(strPort) < ARRAYSIZEOF(szDevice)); + if (lstrlen(szDevice) + lstrlen(strPort) >= ARRAYSIZEOF(szDevice)) + return hComm != NULL; + + _tcscat(szDevice,strPort); // device name + hComm = CreateFile(szDevice, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, + NULL); + + if(hComm != INVALID_HANDLE_VALUE) + { + DWORD dwThreadId; + + nRp = 0; // reset receiver state + dwBytesRead = 0L; + + SetCommTimeouts(hComm,&CommTimeouts); + CommSetBaud(); + + CommTxBRK(); // update BRK condition + + // event to transmit character + hEventTxd = CreateEvent(NULL,FALSE,FALSE,NULL); + + // create char transmit handler + bWriting = TRUE; + hCThreadTxd = CreateThread(NULL,0,&TransmitThread,NULL,CREATE_SUSPENDED,&dwThreadId); + _ASSERT(hCThreadTxd); + SetThreadPriority(hCThreadTxd,THREAD_PRIORITY_ABOVE_NORMAL); + ResumeThread(hCThreadTxd); // start thread + + // create Comm event handler + bReading = FALSE; + SetCommMask(hComm,EV_RXCHAR | EV_TXEMPTY | EV_ERR); // event on RX, TX, error + hCThreadEv = CreateThread(NULL,0,&EventThread,NULL,CREATE_SUSPENDED,&dwThreadId); + _ASSERT(hCThreadEv); + SetThreadPriority(hCThreadEv,THREAD_PRIORITY_ABOVE_NORMAL); + ResumeThread(hCThreadEv); // start thread + while (!bReading) Sleep(0); // wait for SerialThread started + } + else + hComm = NULL; + } + + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("COM port %s.\n"),hComm ? _T("opened"): _T("open error")); + OutputDebugString(buffer); + } + #endif + return hComm != NULL; +} + +VOID CommClose(VOID) +{ + if (hComm != NULL) // port open + { + // workaround to fix problems with some Kermit server programs + // reason: on one hand we have the character transmitting time base on the + // selected baudrate, on the other hand the time between sending the last + // character and closing the port. The last time is much longer on the real + // calculator than on the emulator running at full speed, therefore the + // slow down time on the emulator + Sleep(25); // slow down time + + bReading = FALSE; // kill event thread + SetCommMask(hComm,0L); // clear all events and force WaitCommEvent to return + WaitForSingleObject(hCThreadEv,INFINITE); + CloseHandle(hCThreadEv); + + bWriting = FALSE; // kill write thread + SetEvent(hEventTxd); // continue write thread + WaitForSingleObject(hCThreadTxd,INFINITE); + CloseHandle(hCThreadTxd); + + CloseHandle(hEventTxd); // close Txd event + CloseHandle(hComm); // close port + hComm = NULL; + #if defined DEBUG_SERIAL + OutputDebugString(_T("COM port closed.\n")); + #endif + } + return; +} + +VOID CommSetBaud(VOID) +{ + if (hComm != NULL) + { + const DWORD dwBaudrates[] = { 1200, 1920, 2400, 3840, 4800, 7680, 9600, 15360 }; +// const DWORD dwAppleBaudrates[] = { 1200, 1920, 2400, 3840, 4800, 7680, 9600, 14400, 15360, 19200, 38400, 57600, 115200 }; + + DCB dcb; + + ZeroMemory(&dcb,sizeof(dcb)); + dcb.DCBlength = sizeof(dcb); + dcb.BaudRate = dwBaudrates[Chipset.IORam[BAUD] & 0x7]; + dcb.fBinary = TRUE; + dcb.fParity = TRUE; + dcb.fOutxCtsFlow = FALSE; + dcb.fOutxDsrFlow = FALSE; + dcb.fDtrControl = DTR_CONTROL_DISABLE; + dcb.fDsrSensitivity = FALSE; + dcb.fOutX = FALSE; + dcb.fErrorChar = FALSE; + dcb.fNull = FALSE; + dcb.fRtsControl = RTS_CONTROL_DISABLE; + dcb.fAbortOnError = FALSE; // may changed in further implementations + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; // no parity check, emulated by software + dcb.StopBits = TWOSTOPBITS; + + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + wsprintf(buffer,_T("CommsetBaud: %ld\n"),dcb.BaudRate); + OutputDebugString(buffer); + } + #endif + + SetCommState(hComm,&dcb); + } + return; +} + +BOOL UpdateUSRQ(VOID) // USRQ handling +{ + BOOL bUSRQ = NINT2USRQ; + IOBit(SRQ1,USRQ,bUSRQ); // update USRQ bit + return bUSRQ; +} + +VOID CommTxBRK(VOID) +{ + if (Chipset.IORam[TCS] & BRK) // BRK condition + { + if (hComm != NULL) // com port open + { + // abort data transfer + PurgeComm(hComm,PURGE_TXABORT | PURGE_TXCLEAR); + SetCommBreak(hComm); // set into BRK state + } + + // TBF and TBZ bits of TCS are undefined + + if (Chipset.IORam[TCS] & LPB) // is loopback bit set + { + dwBytesRead = nRp = 0; // clear receive buffer + cBuffer[dwBytesRead++] = 0; // save character in receive buffer + + CommReceive(); // receive available byte + IOBit(RCS,RER,TRUE); // receiver error (no stop bit) + } + } + else + { + if (hComm != NULL) // com port open + { + ClearCommBreak(hComm); // clear BRK state + } + } + return; +} + +VOID CommTransmit(VOID) +{ + BOOL bTxChar = FALSE; + + EnterCriticalSection(&csTxdLock); + if ( (Chipset.IORam[TCS] & TBZ) == 0 // transmitter not busy + && (Chipset.IORam[TCS] & TBF) != 0) // transmit buffer full + { + tbr = (Chipset.IORam[TBR_MSB] << 4) | Chipset.IORam[TBR_LSB]; + + IOBit(TCS,TBF,FALSE); // clear transmit buffer full bit + IOBit(TCS,TBZ,TRUE); // set transmitter busy bit + + bTxChar = TRUE; + } + LeaveCriticalSection(&csTxdLock); + + if (bTxChar) // character to transmit + { + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + if (isprint(tbr)) + wsprintf(buffer,_T("-> '%c'\n"),tbr); + else + wsprintf(buffer,_T("-> %02X\n"),tbr); + OutputDebugString(buffer); + } + #endif + + if (Chipset.IORam[TCS] & LPB) // is loopback bit set + { + if (dwBytesRead == 0) nRp = 0; // no character received, reset read pointer + cBuffer[nRp+dwBytesRead] = tbr; // save character in receive buffer + ++dwBytesRead; + + CommReceive(); // receive available byte + } + + if (hComm != NULL) // com port open + { + SetEvent(hEventTxd); // write TBR byte + } + else + { + IOBit(TCS,TBZ,FALSE); // clear transmitter busy bit + } + } + if (UpdateUSRQ()) // update USRQ bit + INTERRUPT; + return; +} + +VOID CommReceive(VOID) +{ + OVERLAPPED os = { 0 }; + + if (!(Chipset.IORam[IOC] & SON)) // UART off + { + dwBytesRead = 0L; // no bytes received + return; + } + + EnterCriticalSection(&csRecvLock); + do + { + if (Chipset.IORam[RCS] & RBF) // receive buffer full + break; + + // reject reading if com port is closed and not whole operation + if (hComm && dwBytesRead == 0L) // com port open and buffer empty + { + if(ReadFile(hComm,&cBuffer,sizeof(cBuffer),&dwBytesRead,&os) == FALSE) + dwBytesRead = 0L; + else // bytes received + nRp = 0; // reset read pointer + } + + if(dwBytesRead == 0L) // receive buffer empty + break; + + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + if (isprint(cBuffer[nRp])) + wsprintf(buffer,_T("<- '%c'\n"),cBuffer[nRp]); + else + wsprintf(buffer,_T("<- %02X\n"),cBuffer[nRp]); + OutputDebugString(buffer); + } + #endif + + Chipset.IORam[RBR_MSB] = (cBuffer[nRp] >> 4); + Chipset.IORam[RBR_LSB] = (cBuffer[nRp] & 0x0f); + ++nRp; + --dwBytesRead; + + Chipset.IORam[RCS] |= RBF; // receive buffer full + if(UpdateUSRQ()) // update USRQ bit + INTERRUPT; + } + while(0); + LeaveCriticalSection(&csRecvLock); + return; +} diff --git a/SOURCE/SETTINGS.C b/SOURCE/SETTINGS.C new file mode 100644 index 0000000..5f01106 --- /dev/null +++ b/SOURCE/SETTINGS.C @@ -0,0 +1,222 @@ +/* + * settings.c + * + * This file is part of Emu48 + * + * Copyright (C) 2000 Christoph Gießelink + * + */ +#include "pch.h" +#include "Emu48.h" +#include "i28f160.h" + +// #define REGISTRY // use registry instead of *.ini file + +//################ +//# +//# Low level subroutines +//# +//################ + +#if !defined REGISTRY + +// INI-file handling + +#if !defined EMU48_INI + #define EMU48_INI "Emu48.ini" +#endif + +#define ReadString(sec,key,dv,v,sv) GetPrivateProfileString(sec,key,dv,v,sv,_T(EMU48_INI)) +#define ReadInt(sec,key,dv) GetPrivateProfileInt(sec,key,dv,_T(EMU48_INI)); +#define WriteString(sec,key,v) WritePrivateProfileString(sec,key,v,_T(EMU48_INI)) +#define WriteInt(sec,key,v) WritePrivateProfileInt(sec,key,v,_T(EMU48_INI)) + +static BOOL WritePrivateProfileInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, int nValue, LPCTSTR lpszFilename) +{ + TCHAR s[16]; + wsprintf(s,_T("%i"),nValue); + return WritePrivateProfileString(lpszSection, lpszEntry, s, lpszFilename); +} + +#else + +// registry handling + +#if !defined REGISTRYKEY + #define REGISTRYKEY "Software\\Emu48" +#endif + +#define ReadString(sec,key,dv,v,sv) GetRegistryString(sec,key,dv,v,sv) +#define ReadInt(sec,key,dv) GetRegistryInt(sec,key,dv) +#define WriteString(sec,key,v) WriteReg(sec,key,REG_SZ,(BYTE *) v,(lstrlen(v)+1) * sizeof(*v)) +#define WriteInt(sec,key,v) WriteReg(sec,key,REG_DWORD,(BYTE *) &v,sizeof(int)) + +static VOID ReadReg(LPCTSTR lpSubKey, LPCTSTR lpValueName, LPBYTE lpData, DWORD *pdwSize) +{ + TCHAR lpKey[256] = _T(REGISTRYKEY) _T("\\"); + + DWORD retCode,dwType; + HKEY hKey; + + lstrcat(lpKey, lpSubKey); // full registry key + + retCode = RegOpenKeyEx(HKEY_CURRENT_USER, + lpKey, + 0, + KEY_QUERY_VALUE, + &hKey); + if (retCode == ERROR_SUCCESS) + { + retCode = RegQueryValueEx(hKey,lpValueName,NULL,&dwType,lpData,pdwSize); + RegCloseKey(hKey); + } + + if (retCode != ERROR_SUCCESS) // registry entry not found + *pdwSize = 0; // return zero size + return; +} + +static VOID WriteReg(LPCTSTR lpSubKey, LPCTSTR lpValueName, DWORD dwType, CONST BYTE *lpData, DWORD cbData) +{ + TCHAR lpKey[256] = _T(REGISTRYKEY) _T("\\"); + + DWORD retCode; + HKEY hKey; + DWORD dwDisposition; + + lstrcat(lpKey, lpSubKey); // full registry key + + retCode = RegCreateKeyEx(HKEY_CURRENT_USER, + lpKey, + 0,_T(""), + REG_OPTION_NON_VOLATILE, + KEY_WRITE, + NULL, + &hKey, + &dwDisposition); + _ASSERT(retCode == ERROR_SUCCESS); + + RegSetValueEx(hKey,lpValueName,0,dwType,lpData,cbData); + RegCloseKey(hKey); + return; +} + +static DWORD GetRegistryString(LPCTSTR lpszSection, LPCTSTR lpszEntry, LPCTSTR lpDefault, LPTSTR lpData, DWORD dwSize) +{ + dwSize *= sizeof(*lpData); // buffer size in bytes + ReadReg(lpszSection,lpszEntry,(LPBYTE) lpData,&dwSize); + if (dwSize == 0) + { + lstrcpy(lpData,lpDefault); + dwSize = lstrlen(lpData); + } + else + { + dwSize = (dwSize / sizeof(*lpData)) - 1; + } + return dwSize; +} + +static INT GetRegistryInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, INT nDefault) +{ + UINT nValue; + DWORD dwSize = sizeof(nValue); + + ReadReg(lpszSection,lpszEntry,(LPBYTE) &nValue,&dwSize); + return dwSize ? nValue : nDefault; +} + +#endif + + +//################ +//# +//# Public functions +//# +//################ + +VOID ReadSettings(VOID) +{ + // Files + ReadString(_T("Files"),_T("Emu48Directory"),szCurrentDirectory,szEmuDirectory,ARRAYSIZEOF(szEmuDirectory)); + bAutoSave = ReadInt(_T("Files"),_T("AutoSave"),bAutoSave); + bAutoSaveOnExit = ReadInt(_T("Files"),_T("AutoSaveOnExit"),bAutoSaveOnExit); + bSaveDefConfirm = ReadInt(_T("Files"),_T("SaveDefaultConfirm"),bSaveDefConfirm); + bLoadObjectWarning = ReadInt(_T("Files"),_T("LoadObjectWarning"),bLoadObjectWarning); + // Port2 + bPort2IsShared = ReadInt(_T("Port2"),_T("IsShared"),bPort2IsShared); + ReadString(_T("Port2"),_T("Filename"),_T("SHARED.BIN"),szPort2Filename,ARRAYSIZEOF(szPort2Filename)); + // KML + bAlwaysDisplayLog = ReadInt(_T("KML"),_T("AlwaysDisplayLog"),bAlwaysDisplayLog); + bClassicCursor = ReadInt(_T("KML"),_T("ClassicCursor"),bClassicCursor); + // Disassembler + disassembler_mode = ReadInt(_T("Disassembler"),_T("Mnemonics"),disassembler_mode); + // Emulator + bRealSpeed = ReadInt(_T("Emulator"),_T("RealSpeed"),bRealSpeed); + dwSXCycles = ReadInt(_T("Emulator"),_T("SXCycles"),dwSXCycles); + dwGXCycles = ReadInt(_T("Emulator"),_T("GXCycles"),dwGXCycles); + dwGPCycles = ReadInt(_T("Emulator"),_T("GPCycles"),dwGPCycles); // CdB for HP: add apples + dwG2Cycles = ReadInt(_T("Emulator"),_T("G2Cycles"),dwG2Cycles); // CdB for HP: add apples + bGrayscale = ReadInt(_T("Emulator"),_T("Grayscale"),bGrayscale); + bWaveBeep = ReadInt(_T("Emulator"),_T("WaveBeep"),bWaveBeep); + dwWaveVol = ReadInt(_T("Emulator"),_T("WaveVolume"),dwWaveVol); + SetSpeed(bRealSpeed); // set speed + // Macro + bMacroRealSpeed = ReadInt(_T("Macro"),_T("RealSpeed"),bMacroRealSpeed); + nMacroTimeout = ReadInt(_T("Macro"),_T("ReplayTimeout"),nMacroTimeout); + // Serial + ReadString(_T("Serial"),_T("Wire"),_T(NO_SERIAL),szSerialWire,ARRAYSIZEOF(szSerialWire)); + ReadString(_T("Serial"),_T("Ir"),_T(NO_SERIAL),szSerialIr,ARRAYSIZEOF(szSerialIr)); + // ROM + bRomWriteable = ReadInt(_T("ROM"),_T("Writeable"),bRomWriteable); + bWP = ReadInt(_T("ROM"),_T("WP#"),bWP); + return; +} + +VOID WriteSettings(VOID) +{ + // Files + WriteString(_T("Files"),_T("Emu48Directory"),szEmuDirectory); + WriteInt(_T("Files"),_T("AutoSave"),bAutoSave); + WriteInt(_T("Files"),_T("AutoSaveOnExit"),bAutoSaveOnExit); + WriteInt(_T("Files"),_T("AutoSaveOnExit"),bAutoSaveOnExit); + WriteInt(_T("Files"),_T("LoadObjectWarning"),bLoadObjectWarning); + // Port2 + WriteInt(_T("Port2"),_T("IsShared"),bPort2IsShared); + WriteString(_T("Port2"),_T("Filename"),szPort2Filename); + // KML + WriteInt(_T("KML"),_T("AlwaysDisplayLog"),bAlwaysDisplayLog); + WriteInt(_T("KML"),_T("ClassicCursor"),bClassicCursor); + // Disassembler + WriteInt(_T("Disassembler"),_T("Mnemonics"),disassembler_mode); + // Emulator + WriteInt(_T("Emulator"),_T("RealSpeed"),bRealSpeed); + WriteInt(_T("Emulator"),_T("SXCycles"),dwSXCycles); + WriteInt(_T("Emulator"),_T("GXCycles"),dwGXCycles); + WriteInt(_T("Emulator"),_T("GPCycles"),dwGPCycles); // CdB for HP: add apples + WriteInt(_T("Emulator"),_T("G2Cycles"),dwG2Cycles); // CdB for HP: add apples + WriteInt(_T("Emulator"),_T("Grayscale"),bGrayscale); + WriteInt(_T("Emulator"),_T("WaveBeep"),bWaveBeep); + WriteInt(_T("Emulator"),_T("WaveVolume"),dwWaveVol); + // Macro + WriteInt(_T("Macro"),_T("RealSpeed"),bMacroRealSpeed); + WriteInt(_T("Macro"),_T("ReplayTimeout"),nMacroTimeout); + // Serial + WriteString(_T("Serial"),_T("Wire"),szSerialWire); + WriteString(_T("Serial"),_T("Ir"),szSerialIr); + // ROM + WriteInt(_T("ROM"),_T("Writeable"),bRomWriteable); + return; +} + +VOID ReadLastDocument(LPTSTR szFilename, DWORD nSize) +{ + ReadString(_T("Files"),_T("LastDocument"),_T(""),szFilename,nSize); + return; +} + +VOID WriteLastDocument(LPCTSTR szFilename) +{ + WriteString(_T("Files"),_T("LastDocument"),szFilename); + return; +} diff --git a/SOURCE/STACK.C b/SOURCE/STACK.C new file mode 100644 index 0000000..d3adb01 --- /dev/null +++ b/SOURCE/STACK.C @@ -0,0 +1,674 @@ +/* + * stack.c + * + * This file is part of Emu48 + * + * Copyright (C) 2005 Christoph Gießelink + * + */ +#include "pch.h" +#include "resource.h" +#include "Emu48.h" +#include "io.h" + +#define fnRadix 51 // fraction mark +#define fnApprox 105 // exact / approx. mode (HP49G) + +#define DOINT 0x02614 // Precision Integer (HP49G) +#define DOREAL 0x02933 // Real +#define DOCSTR 0x02A2C // String + +//################ +//# +//# Low level subroutines +//# +//################ + +static INT RPL_GetZInt(BYTE CONST *pbyNum,INT nIntLen,LPTSTR cp,INT nSize) +{ + INT i = 0; // character counter + + _ASSERT(nSize > 0); // target buffer size + + if (nIntLen > 1) // has sign nibble + { + --nIntLen; // remove sign from digit length + + // check for valid sign + _ASSERT(pbyNum[nIntLen] == 0 || pbyNum[nIntLen] == 9); + if (pbyNum[nIntLen] == 9) // negative number + { + *cp++ = _T('-'); // add sign + --nSize; // dec dest buffer size + ++i; // wrote one character + } + } + + if (nIntLen >= nSize) return 0; // dest buffer overflow + i += nIntLen; // adjust character counter + + while (nIntLen-- > 0) // write all digits + { + // check for valid digit + _ASSERT(pbyNum[nIntLen] >= 0 && pbyNum[nIntLen] <= 9); + *cp++ = _T('0') + pbyNum[nIntLen]; // and write + } + *cp = 0; // set EOS + return i; +} + +static INT RPL_SetZInt(LPCTSTR cp,LPBYTE pbyNum,INT nSize) +{ + BYTE bySign; + INT nStrLen,nNumSize; + + _ASSERT(nSize > 0); // target buffer size + + nStrLen = lstrlen(cp); // source string length + + if ( nStrLen == 0 // empty string + // precisition integer contain only these numbers + || _tcsspn(cp,_T("0123456789+-")) != (SIZE_T) nStrLen) + return 0; + + bySign = (*cp != _T('-')) ? 0 : 9; // set sign nibble + if (*cp == _T('-') || *cp == _T('+')) // skip sign character + { + ++cp; + --nStrLen; + } + + if (nStrLen == 1 && *cp == _T('0')) // special code for zero + { + *pbyNum = 0; // zero data + return 1; // finish + } + + // nStrLen = no. of digits without sign + if (nStrLen >= nSize) // destination buffer too small + return 0; + + nNumSize = nStrLen + 1; // no. of written data + + while (--nStrLen >= 0) // eval all digits + { + TCHAR c = cp[nStrLen]; + + // only '0' .. '9' are valid here + if (!((c >= _T('0')) || (c <= _T('9')))) + return 0; + + c -= _T('0'); + *pbyNum++ = (BYTE) c; + } + *pbyNum = bySign; // add sign + + return nNumSize; +} + +static INT RPL_GetBcd(BYTE CONST *pbyNum,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPTSTR cp,INT nSize) +{ + BYTE byNib; + LONG v,lExp; + BOOL bPflag,bExpflag; + INT i; + + lExp = 0; + for (v = 1; nExpLen--; v *= 10) // fetch exponent + { + lExp += (LONG) *pbyNum++ * v; // calc. exponent + } + + if (lExp > v / 2) lExp -= v; // negative exponent + + lExp -= nMantLen - 1; // set decimal point to end of mantissa + + i = 0; // first character + bPflag = FALSE; // show no decimal point + + // scan mantissa + for (v = (LONG) nMantLen - 1; v >= 0 || bPflag; v--) + { + if (v >= 0L) // still mantissa digits left + byNib = *pbyNum++; + else + byNib = 0; // zero for negativ exponent + + if (!i) // still delete zeros at end + { + if (byNib == 0 && lExp && v > 0) // delete zeros + { + lExp++; // adjust exponent + continue; + } + + // TRUE at x.E + bExpflag = v + lExp > 14 || v + lExp < -nMantLen; + bPflag = !bExpflag && v < -lExp; // decimal point flag at neg. exponent + } + + // set decimal point + if ((bExpflag && v == 0) || (!lExp && i)) + { + if (i >= nSize) return 0; // dest buffer overflow + cp[i++] = cDec; // write decimal point + if (v < 0) // no mantissa digits any more + { + if (i >= nSize) return 0; // dest buffer overflow + cp[i++] = _T('0'); // write heading zero + } + bPflag = FALSE; // finished with negativ exponents + } + + if (v >= 0 || bPflag) + { + if (i >= nSize) return 0; // dest buffer overflow + cp[i++] = (TCHAR) byNib + _T('0'); // write character + } + + lExp++; // next position + } + + if (*pbyNum == 9) // negative number + { + if (i >= nSize) return 0; // dest buffer overflow + cp[i++] = _T('-'); // write sign + } + + if (i >= nSize) return 0; // dest buffer overflow + cp[i] = 0; // set EOS + + for (v = 0; v < (i / 2); v++) // reverse string + { + TCHAR cNib = cp[v]; // swap chars + cp[v] = cp[i-v-1]; + cp[i-v-1] = cNib; + } + + // write number with exponent + if (bExpflag) + { + if (i + 5 >= nSize) return 0; // dest buffer overflow + i += wsprintf(&cp[i],_T("E%d"),lExp-1); + } + return i; +} + +static INT RPL_SetBcd(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPBYTE pbyNum,INT nSize) +{ + TCHAR cVc[] = _T(".0123456789eE+-"); + + BYTE byNum[80]; + INT i,nIp,nDp,nMaxExp; + LONG lExp; + + cVc[0] = cDec; // replace decimal char + + if ( nMantLen + nExpLen >= nSize // destination buffer too small + || !*cp // empty string + || _tcsspn(cp,cVc) != (SIZE_T) lstrlen(cp) // real contain only these numbers + || lstrlen(cp) >= ARRAYSIZEOF(byNum)) // ignore too long reals + return 0; + + byNum[0] = (*cp != _T('-')) ? 0 : 9; // set sign nibble + if (*cp == _T('-') || *cp == _T('+')) // skip sign character + cp++; + + // only '.', '0' .. '9' are valid here + if (!((*cp == cDec) || (*cp >= _T('0')) || (*cp <= _T('9')))) + return 0; + + nIp = 0; // length of integer part + if (*cp != cDec) // no decimal point + { + // count integer part + while (*cp >= _T('0') && *cp <= _T('9')) + byNum[++nIp] = *cp++ - _T('0'); + if (!nIp) return 0; + } + + // only '.', 'E', 'e' or end are valid here + if (!(!*cp || (*cp == cDec) || (*cp == _T('E')) || (*cp == _T('e')))) + return 0; + + nDp = 0; // length of decimal part + if (*cp == cDec) // decimal point + { + cp++; // skip '.' + + // count decimal part + while (*cp >= _T('0') && *cp <= _T('9')) + byNum[nIp + ++nDp] = *cp++ - _T('0'); + } + + // count number of heading zeros in mantissa + for (i = 0; byNum[i+1] == 0 && i + 1 < nIp + nDp; ++i) { } + + if (i > 0) // have to normalize + { + INT j; + + nIp -= i; // for later ajust of exponent + for (j = 1; j <= nIp + nDp; ++j) // normalize mantissa + byNum[j] = byNum[j + i]; + } + + if(byNum[1] == 0) // number is 0 + { + ZeroMemory(pbyNum,nMantLen + nExpLen + 1); + return nMantLen + nExpLen + 1; + } + + for (i = nIp + nDp; i < nMantLen;) // fill rest of mantissa with 0 + byNum[++i] = 0; + + // must be 'E', 'e' or end + if (!(!*cp || (*cp == _T('E')) || (*cp == _T('e')))) + return 0; + + lExp = 0; + if (*cp == _T('E') || *cp == _T('e')) + { + cp++; // skip 'E' + + i = FALSE; // positive exponent + if (*cp == _T('-') || *cp == _T('+')) + { + i = (*cp++ == _T('-')); // adjust exponent sign + } + + // exponent symbol must be followed by number + if (*cp < _T('0') || *cp > _T('9')) return 0; + + while (*cp >= _T('0') && *cp <= _T('9')) + lExp = lExp * 10 + *cp++ - _T('0'); + + if(i) lExp = -lExp; + } + + if (*cp != 0) return 0; + + // adjust exponent value with exponent from normalized mantissa + lExp += nIp - 1; + + // calculate max. posive exponent + for (nMaxExp = 5, i = 1; i < nExpLen; ++i) + nMaxExp *= 10; + + // check range of exponent + if ((lExp < 0 && -lExp >= nMaxExp) || (lExp >= nMaxExp)) + return 0; + + if (lExp < 0) lExp += 2 * nMaxExp; // adjust negative offset + + for (i = nExpLen; i > 0; --i) // convert number into digits + { + byNum[nMantLen + i] = (BYTE) (lExp % 10); + lExp /= 10; + } + + // copy to target in reversed order + for (i = nMantLen + nExpLen; i >= 0; --i) + *pbyNum++ = byNum[i]; + + return nMantLen + nExpLen + 1; +} + +//################ +//# +//# Object subroutines +//# +//################ + +#if 0 +static INT IsRealNumber(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,LPBYTE pbyNum,INT nSize) +{ + LPTSTR lpszNumber; + INT nLength = 0; + + if ((lpszNumber = DuplicateString(cp)) != NULL) + { + LPTSTR p = lpszNumber; + INT i; + + // cut heading whitespaces + for (; *p == _T(' ') || *p == _T('\t'); ++p) { } + + // cut tailing whitespaces + for (i = lstrlen(p); --i >= 0;) + { + if (p[i] != _T(' ') && p[i] != _T('\t')) + break; + } + p[++i] = 0; // new EOS + + nLength = RPL_SetBcd(p,nMantLen,nExpLen,cDec,pbyNum,nSize); + HeapFree(hHeap,0,lpszNumber); + } + return nLength; +} +#endif + +static TCHAR GetRadix(VOID) +{ + // get locale decimal point + // GetLocaleInfo(LOCALE_USER_DEFAULT,LOCALE_SDECIMAL,&cDecimal,1); + + return RPL_GetSystemFlag(fnRadix) ? _T(',') : _T('.'); +} + +static INT DoInt(DWORD dwAddress,LPTSTR cp,INT nSize) +{ + LPBYTE lpbyData; + INT nLength,nIntLen; + + nIntLen = Read5(dwAddress) - 5; // no. of digits + if (nIntLen <= 0) return 0; // error in calculator object + + nLength = 0; + if ((lpbyData = HeapAlloc(hHeap,0,nIntLen))) + { + // get precisition integer object content and decode it + Npeek(lpbyData,dwAddress+5,nIntLen); + nLength = RPL_GetZInt(lpbyData,nIntLen,cp,nSize); + HeapFree(hHeap,0,lpbyData); + } + return nLength; +} + +static INT DoReal(DWORD dwAddress,LPTSTR cp,INT nSize) +{ + BYTE byNumber[16]; + + // get real object content and decode it + Npeek(byNumber,dwAddress,ARRAYSIZEOF(byNumber)); + return RPL_GetBcd(byNumber,12,3,GetRadix(),cp,nSize); +} + + +//################ +//# +//# Stack routines +//# +//################ + +// +// ID_STACK_COPY +// +LRESULT OnStackCopy(VOID) // copy data from stack +{ + TCHAR cBuffer[128]; + HANDLE hClipObj; + LPBYTE lpbyData; + DWORD dwAddress,dwObject,dwSize; + UINT uClipboardFormat; + + _ASSERT(nState == SM_RUN); // emulator must be in RUN state + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + { + InfoMessage(_T("The emulator is busy.")); + return 0; + } + + _ASSERT(nState == SM_SLEEP); + + if ((dwAddress = RPL_Pick(1)) == 0) // pick address of level1 object + { + MessageBeep(MB_OK); // error beep + goto error; + } + + switch (dwObject = Read5(dwAddress)) // select object + { + case DOINT: // Precision Integer (HP49G) + case DOREAL: // real object + dwAddress += 5; // object content + + switch (dwObject) + { + case DOINT: // Precision Integer (HP49G) + // get precision integer object content and decode it + dwSize = DoInt(dwAddress,cBuffer,ARRAYSIZEOF(cBuffer)); + break; + case DOREAL: // real object + // get real object content and decode it + dwSize = DoReal(dwAddress,cBuffer,ARRAYSIZEOF(cBuffer)); + break; + } + + // calculate buffer size + dwSize = (dwSize + 1) * sizeof(*cBuffer); + + // memory allocation for clipboard data + if ((hClipObj = GlobalAlloc(GMEM_MOVEABLE,dwSize)) == NULL) + goto error; + + if ((lpbyData = GlobalLock(hClipObj))) + { + // copy data to memory + CopyMemory(lpbyData,cBuffer,dwSize); + GlobalUnlock(hClipObj); // unlock memory + } + + #if defined _UNICODE + uClipboardFormat = CF_UNICODETEXT; + #else + uClipboardFormat = CF_TEXT; + #endif + break; + case DOCSTR: // string + dwAddress += 5; // address of string length + dwSize = (Read5(dwAddress) - 5) / 2; // length of string + + // memory allocation for clipboard data + if ((hClipObj = GlobalAlloc(GMEM_MOVEABLE,dwSize + 1)) == NULL) + goto error; + + if ((lpbyData = GlobalLock(hClipObj))) // lock memory + { + // copy data into clipboard buffer + for (dwAddress += 5;dwSize-- > 0;dwAddress += 2,++lpbyData) + *lpbyData = Read2(dwAddress); + *lpbyData = 0; // set EOS + + GlobalUnlock(hClipObj); // unlock memory + uClipboardFormat = CF_TEXT; // always text + } + break; + default: + MessageBeep(MB_OK); // error beep + goto error; + } + + if (OpenClipboard(hWnd)) + { + if (EmptyClipboard()) + SetClipboardData(uClipboardFormat,hClipObj); + else + GlobalFree(hClipObj); + CloseClipboard(); + } + else // clipboard open failed + { + GlobalFree(hClipObj); + } + +error: + SwitchToState(SM_RUN); + return 0; +} + +// +// ID_STACK_PASTE +// +LRESULT OnStackPaste(VOID) // paste data to stack +{ + #if defined _UNICODE + #define CF_TEXTFORMAT CF_UNICODETEXT + #else + #define CF_TEXTFORMAT CF_TEXT + #endif + + HANDLE hClipObj; + + BOOL bSuccess = FALSE; + + // check if clipboard format is available + if (!IsClipboardFormatAvailable(CF_TEXTFORMAT)) + { + MessageBeep(MB_OK); // error beep + return 0; + } + + SuspendDebugger(); // suspend debugger + bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control + + // calculator off, turn on + if (!(Chipset.IORam[BITOFFSET]&DON)) + { + KeyboardEvent(TRUE,0,0x8000); + KeyboardEvent(FALSE,0,0x8000); + + // wait for sleep mode + while(Chipset.Shutdn == FALSE) Sleep(0); + } + + _ASSERT(nState == SM_RUN); // emulator must be in RUN state + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state + { + InfoMessage(_T("The emulator is busy.")); + goto cancel; + } + + _ASSERT(nState == SM_SLEEP); + + if (OpenClipboard(hWnd)) + { + if ((hClipObj = GetClipboardData(CF_TEXTFORMAT))) + { + LPCTSTR lpstrClipdata; + LPBYTE lpbyData; + + if ((lpstrClipdata = GlobalLock(hClipObj))) + { + BYTE byNumber[128]; + DWORD dwAddress; + INT s; + + do + { + // HP49G or HP49G+ in exact mode + if ( (cCurrentRomType == 'X' || cCurrentRomType == 'Q') + && !RPL_GetSystemFlag(fnApprox)) + { + // try to convert string to HP49 precision integer + s = RPL_SetZInt(lpstrClipdata,byNumber,sizeof(byNumber)); + + if (s > 0) // is a real number for exact mode + { + // get TEMPOB memory for HP49 precision integer object + dwAddress = RPL_CreateTemp(s+5+5); + if ((bSuccess = (dwAddress > 0))) + { + Write5(dwAddress,DOINT); // prolog + Write5(dwAddress+5,s+5); // size + Nwrite(byNumber,dwAddress+10,s); // data + + // push object to stack + RPL_Push(1,dwAddress); + } + break; + } + } + + // try to convert string to real format + s = RPL_SetBcd(lpstrClipdata,12,3,GetRadix(),byNumber,sizeof(byNumber)); + + if (s > 0) // is a real number + { + // get TEMPOB memory for real object + dwAddress = RPL_CreateTemp(16+5); + if ((bSuccess = (dwAddress > 0))) + { + Write5(dwAddress,DOREAL); // prolog + Nwrite(byNumber,dwAddress+5,s); // data + + // push object to stack + RPL_Push(1,dwAddress); + } + break; + } + + // any other format + { + DWORD dwSize = lstrlen(lpstrClipdata); + if ((lpbyData = HeapAlloc(hHeap,0,dwSize * 2))) + { + LPBYTE lpbySrc,lpbyDest; + DWORD dwLoop; + + #if defined _UNICODE + // copy data UNICODE -> ASCII + WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, + lpstrClipdata, dwSize, + lpbyData+dwSize, dwSize, NULL, NULL); + #else + // copy data + memcpy(lpbyData+dwSize,lpstrClipdata,dwSize); + #endif + + // unpack data + lpbySrc = lpbyData+dwSize; + lpbyDest = lpbyData; + dwLoop = dwSize; + while (dwLoop-- > 0) + { + BYTE byTwoNibs = *lpbySrc++; + *lpbyDest++ = (BYTE) (byTwoNibs & 0xF); + *lpbyDest++ = (BYTE) (byTwoNibs >> 4); + } + + dwSize *= 2; // size in nibbles + + // get TEMPOB memory for string object + dwAddress = RPL_CreateTemp(dwSize+10); + if ((bSuccess = (dwAddress > 0))) + { + Write5(dwAddress,DOCSTR); // String + Write5(dwAddress+5,dwSize+5); // length of String + Nwrite(lpbyData,dwAddress+10,dwSize); // data + + // push object to stack + RPL_Push(1,dwAddress); + } + HeapFree(hHeap,0,lpbyData); + } + } + } + while(FALSE); + + GlobalUnlock(hClipObj); + } + } + CloseClipboard(); + } + + SwitchToState(SM_RUN); // run state + while (nState!=nNextState) Sleep(0); + _ASSERT(nState == SM_RUN); + + if (bSuccess == FALSE) // data not copied + goto cancel; + + KeyboardEvent(TRUE,0,0x8000); + KeyboardEvent(FALSE,0,0x8000); + + // wait for sleep mode + while(Chipset.Shutdn == FALSE) Sleep(0); + +cancel: + bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control + ResumeDebugger(); + return 0; + #undef CF_TEXTFORMAT +} diff --git a/SOURCE/TIMER.C b/SOURCE/TIMER.C new file mode 100644 index 0000000..d71cea9 --- /dev/null +++ b/SOURCE/TIMER.C @@ -0,0 +1,439 @@ +/* + * timer.c + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ +#include "pch.h" +#include "Emu48.h" +#include "ops.h" +#include "io.h" // I/O definitions + +#define AUTO_OFF 10 // Time in minutes for 'auto off' + +// Ticks for 01.01.1970 00:00:00 +#define UNIX_0_TIME ((ULONGLONG) 0x0001cf2e8f800000) + +// Ticks for 'auto off' +#define OFF_TIME ((ULONGLONG) (AUTO_OFF * 60) << 13) + +// memory address for clock and auto off +// S(X) = 0x70052-0x70070, G(X) = 0x80058-0x80076, 49G = 0x80058-0x80076 +#define RPLTIME ((cCurrentRomType=='S')?0x52:0x58) + +#define T1_FREQ 62 // Timer1 1/frequency in ms +#define T2_FREQ 8192 // Timer2 frequency + +static BOOL bStarted = FALSE; +static BOOL bOutRange = FALSE; // flag if timer value out of range +static UINT uT1TimerId = 0; +static UINT uT2TimerId = 0; + +static BOOL bNINT2T1 = FALSE; // state of NINT2 affected from timer1 +static BOOL bNINT2T2 = FALSE; // state of NINT2 affected from timer2 + +static BOOL bAccurateTimer; // flag if accurate timer is used +static LARGE_INTEGER lT2Ref; // counter value at timer2 start +static TIMECAPS tc; // timer information +static UINT uT2MaxTicks; // max. timer2 ticks handled by one timer event + +static DWORD dwT2Ref; // timer2 value at last timer2 access +static DWORD dwT2Cyc; // cpu cycle counter at last timer2 access + +static void CALLBACK TimeProc(UINT uEventId, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2); + +static DWORD CalcT2(VOID) // calculate timer2 value +{ + DWORD dwT2 = Chipset.t2; // get value from chipset + if (bStarted) // timer2 running + { + LARGE_INTEGER lT2Act; + DWORD dwT2Dif; + + // timer should run a little bit faster (10%) than maschine in authentic speed mode + DWORD dwCycPerTick = (9 * T2CYCLES) / 5; + + QueryPerformanceCounter(&lT2Act); // actual time + // calculate realtime timer2 ticks since reference point + dwT2 -= (DWORD) + (((lT2Act.QuadPart - lT2Ref.QuadPart) * T2_FREQ) + / lFreq.QuadPart); + + dwT2Dif = dwT2Ref - dwT2; // timer2 ticks since last request + + // checking if the MSB of dwT2Dif can be used as sign flag + _ASSERT((DWORD) tc.wPeriodMax < ((1<<(sizeof(dwT2Dif)*8-1))/8192)*1000); + + // 2nd timer call in a 32ms time frame or elapsed time is negative (Win2k bug) + if (!Chipset.Shutdn && ((dwT2Dif > 0x01 && dwT2Dif <= 0x100) || (dwT2Dif & 0x80000000) != 0)) + { + DWORD dwT2Ticks = ((DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwT2Cyc) / dwCycPerTick; + + // estimated < real elapsed timer2 ticks or negative time + if (dwT2Ticks < dwT2Dif || (dwT2Dif & 0x80000000) != 0) + { + // real time too long or got negative time elapsed + dwT2 = dwT2Ref - dwT2Ticks; // estimated timer2 value from CPU cycles + dwT2Cyc += dwT2Ticks * dwCycPerTick; // estimated CPU cycles for the timer2 ticks + } + else + { + // reached actual time -> new synchronizing + dwT2Cyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwCycPerTick; + } + } + else + { + // valid actual time -> new synchronizing + dwT2Cyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwCycPerTick; + } + + // check if timer2 interrupt is active -> no timer2 value below 0xFFFFFFFF + if ( Chipset.inte + && (dwT2 & 0x80000000) != 0 + && (!Chipset.Shutdn || (Chipset.IORam[TIMER2_CTRL]&WKE)) + && (Chipset.IORam[TIMER2_CTRL]&INTR) + ) + { + dwT2 = 0xFFFFFFFF; + dwT2Cyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwCycPerTick; + } + + dwT2Ref = dwT2; // new reference time + } + return dwT2; +} + +static VOID CheckT1(BYTE nT1) +{ + // implementation of TSRQ + bNINT2T1 = (Chipset.IORam[TIMER1_CTRL]&INTR) != 0 && (nT1&8) != 0; + IOBit(SRQ1,TSRQ,bNINT2T1 || bNINT2T2); + + if ((nT1&8) == 0) // timer1 MSB not set + { + Chipset.IORam[TIMER1_CTRL] &= ~SRQ; // clear SRQ bit + return; + } + + _ASSERT((nT1&8) != 0); // timer1 MSB set + + // timer MSB and INT or WAKE bit is set + if ((Chipset.IORam[TIMER1_CTRL]&(WKE|INTR)) != 0) + Chipset.IORam[TIMER1_CTRL] |= SRQ; // set SRQ + // cpu not sleeping and T1 -> Interrupt + if ( (!Chipset.Shutdn || (Chipset.IORam[TIMER1_CTRL]&WKE)) + && (Chipset.IORam[TIMER1_CTRL]&INTR)) + { + Chipset.SoftInt = TRUE; + bInterrupt = TRUE; + } + // cpu sleeping and T1 -> Wake Up + if (Chipset.Shutdn && (Chipset.IORam[TIMER1_CTRL]&WKE)) + { + Chipset.IORam[TIMER1_CTRL] &= ~WKE; // clear WKE bit + Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode + SetEvent(hEventShutdn); // wake up emulation thread + } + return; +} + +static VOID CheckT2(DWORD dwT2) +{ + // implementation of TSRQ + bNINT2T2 = (Chipset.IORam[TIMER2_CTRL]&INTR) != 0 && (dwT2&0x80000000) != 0; + IOBit(SRQ1,TSRQ,bNINT2T1 || bNINT2T2); + + if ((dwT2&0x80000000) == 0) // timer2 MSB not set + { + Chipset.IORam[TIMER2_CTRL] &= ~SRQ; // clear SRQ bit + return; + } + + _ASSERT((dwT2&0x80000000) != 0); // timer2 MSB set + + // timer MSB and INT or WAKE bit is set + if ((Chipset.IORam[TIMER2_CTRL]&(WKE|INTR)) != 0) + Chipset.IORam[TIMER2_CTRL] |= SRQ; // set SRQ + // cpu not sleeping and T2 -> Interrupt + if ( (!Chipset.Shutdn || (Chipset.IORam[TIMER2_CTRL]&WKE)) + && (Chipset.IORam[TIMER2_CTRL]&INTR)) + { + Chipset.SoftInt = TRUE; + bInterrupt = TRUE; + } + // cpu sleeping and T2 -> Wake Up + if (Chipset.Shutdn && (Chipset.IORam[TIMER2_CTRL]&WKE)) + { + Chipset.IORam[TIMER2_CTRL] &= ~WKE; // clear WKE bit + Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode + SetEvent(hEventShutdn); // wake up emulation thread + } + return; +} + +static VOID RescheduleT2(BOOL bRefPoint) +{ + UINT uDelay; + _ASSERT(uT2TimerId == 0); // timer2 must stopped + if (bRefPoint) // save reference time + { + dwT2Ref = Chipset.t2; // timer2 value at last timer2 access + dwT2Cyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); // cpu cycle counter at last timer2 access + QueryPerformanceCounter(&lT2Ref); // time of corresponding Chipset.t2 value + uDelay = Chipset.t2; // timer value for delay + } + else // called without new refpoint, restart t2 with actual value + { + uDelay = CalcT2(); // actual timer value for delay + } + if ((bOutRange = uDelay > uT2MaxTicks)) // delay greater maximum delay + uDelay = uT2MaxTicks; // wait maximum delay time + uDelay = (uDelay * 125 + 1023) / 1024; // timer delay in ms (1000/8192 = 125/1024) + uDelay = __max(tc.wPeriodMin,uDelay); // wait minimum delay of timer + _ASSERT(uDelay <= tc.wPeriodMax); // inside maximum event delay + // start timer2; schedule event, when Chipset.t2 will be zero + VERIFY(uT2TimerId = timeSetEvent(uDelay,0,&TimeProc,2,TIME_ONESHOT)); + return; +} + +static VOID AbortT2(VOID) +{ + _ASSERT(uT2TimerId); + timeKillEvent(uT2TimerId); // kill event + uT2TimerId = 0; // then reset var + return; +} + +static void CALLBACK TimeProc(UINT uEventId, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) +{ + _ASSERT(uEventId); // illegal EventId + + if (uEventId == uT1TimerId) // called from timer1 event (default period 16 Hz) + { + EnterCriticalSection(&csT1Lock); + { + Chipset.t1 = (Chipset.t1-1)&0xF;// decrement timer value + CheckT1(Chipset.t1); // test timer1 control bits + } + LeaveCriticalSection(&csT1Lock); + return; + } + if (uEventId == uT2TimerId) // called from timer2 event, Chipset.t2 should be zero + { + EnterCriticalSection(&csT2Lock); + { + uT2TimerId = 0; // single shot timer timer2 stopped + if (!bOutRange) // timer event elapsed + { + // timer2 overrun, test timer2 control bits else restart timer2 + Chipset.t2 = CalcT2(); // calculate new timer2 value + CheckT2(Chipset.t2); // test timer2 control bits + } + RescheduleT2(!bOutRange); // restart timer2 + } + LeaveCriticalSection(&csT2Lock); + return; + } + return; + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(dwUser); + UNREFERENCED_PARAMETER(dw1); + UNREFERENCED_PARAMETER(dw2); +} + + +VOID SetHP48Time(VOID) // set date and time +{ + SYSTEMTIME ts; + ULONGLONG ticks, time; + DWORD dw; + WORD crc, i; + BYTE p[4]; + + _ASSERT(sizeof(ULONGLONG) == 8); // check size of datatype + + GetLocalTime(&ts); // local time, _ftime() cause memory/resource leaks + + // calculate days until 01.01.1970 + dw = (DWORD) ts.wMonth; + if (dw > 2) + dw -= 3L; + else + { + dw += 9L; + --ts.wYear; + } + dw = (DWORD) ts.wDay + (153L * dw + 2L) / 5L; + dw += (146097L * (((DWORD) ts.wYear) / 100L)) / 4L; + dw += (1461L * (((DWORD) ts.wYear) % 100L)) / 4L; + dw -= 719469L; + + // convert into seconds and add time + dw = dw * 24L + (DWORD) ts.wHour; + dw = dw * 60L + (DWORD) ts.wMinute; + dw = dw * 60L + (DWORD) ts.wSecond; + + // create timerticks = (s + ms) * 8192 + ticks = ((ULONGLONG) dw << 13) | (((ULONGLONG) ts.wMilliseconds << 10) / 125); + + ticks += UNIX_0_TIME; // add offset ticks from year 0 + ticks += Chipset.t2; // add actual timer2 value + + time = ticks; // save for calc. timeout + time += OFF_TIME; // add 10 min for auto off + + dw = RPLTIME; // HP addresses for clock in port0 + + crc = 0x0; // reset crc value + for (i = 0; i < 13; ++i, ++dw) // write date and time + { + *p = (BYTE) ticks & 0xf; + crc = (crc >> 4) ^ (((crc ^ ((WORD) *p)) & 0xf) * 0x1081); + Chipset.Port0[dw] = *p; // always store in port0 + ticks >>= 4; + } + + Nunpack(p,crc,4); // write crc + memcpy(Chipset.Port0+dw,p,4); // always store in port0 + + dw += 4; // HP addresses for timeout + + for (i = 0; i < 13; ++i, ++dw) // write time for auto off + { + // always store in port0 + Chipset.Port0[dw] = (BYTE) time & 0xf; + time >>= 4; + } + + Chipset.Port0[dw] = 0xf; // always store in port0 + return; +} + +VOID StartTimers(VOID) +{ + if (bStarted) // timer running + return; // -> quit + if (Chipset.IORam[TIMER2_CTRL]&RUN) // start timer1 and timer2 ? + { + bStarted = TRUE; // flag timer running + // initialisation of NINT2 lines + bNINT2T1 = (Chipset.IORam[TIMER1_CTRL]&INTR) != 0 && (Chipset.t1 & 8) != 0; + bNINT2T2 = (Chipset.IORam[TIMER2_CTRL]&INTR) != 0 && (Chipset.t2 & 0x80000000) != 0; + timeGetDevCaps(&tc,sizeof(tc)); // get timer resolution + + // max. timer2 ticks that can be handled by one timer event + uT2MaxTicks = __min((0xFFFFFFFF / 1024),tc.wPeriodMax); + uT2MaxTicks = __min((0xFFFFFFFF - 1023) / 125,uT2MaxTicks * 1024 / 125); + + CheckT1(Chipset.t1); // check for timer1 interrupts + CheckT2(Chipset.t2); // check for timer2 interrupts + // set timer resolution to greatest possible one + bAccurateTimer = (timeBeginPeriod(tc.wPeriodMin) == TIMERR_NOERROR); + // set timer1 with given period + VERIFY(uT1TimerId = timeSetEvent(T1_FREQ,0,&TimeProc,1,TIME_PERIODIC)); + RescheduleT2(TRUE); // start timer2 + } + return; +} + +VOID StopTimers(VOID) +{ + if (!bStarted) // timer stopped + return; // -> quit + if (uT1TimerId != 0) // timer1 running + { + // Critical Section handler may cause a dead lock + timeKillEvent(uT1TimerId); // stop timer1 + uT1TimerId = 0; // set flag timer1 stopped + } + if (uT2TimerId != 0) // timer2 running + { + EnterCriticalSection(&csT2Lock); + { + Chipset.t2 = CalcT2(); // update chipset timer2 value + } + LeaveCriticalSection(&csT2Lock); + AbortT2(); // stop timer2 outside critical section + } + bStarted = FALSE; + if (bAccurateTimer) // "Accurate timer" running + { + timeEndPeriod(tc.wPeriodMin); // finish service + } + return; +} + +DWORD ReadT2(VOID) +{ + DWORD dwT2; + EnterCriticalSection(&csT2Lock); + { + dwT2 = CalcT2(); // calculate timer2 value or if stopped last timer value + CheckT2(dwT2); // update timer2 control bits + } + LeaveCriticalSection(&csT2Lock); + return dwT2; +} + +VOID SetT2(DWORD dwValue) +{ + // calling AbortT2() inside Critical Section handler may cause a dead lock + if (uT2TimerId != 0) // timer2 running + AbortT2(); // stop timer2 + EnterCriticalSection(&csT2Lock); + { + Chipset.t2 = dwValue; // set new value + CheckT2(Chipset.t2); // test timer2 control bits + if (bStarted) // timer running + RescheduleT2(TRUE); // restart timer2 + } + LeaveCriticalSection(&csT2Lock); + return; +} + +BYTE ReadT1(VOID) +{ + BYTE nT1; + EnterCriticalSection(&csT1Lock); + { + nT1 = Chipset.t1; // read timer1 value + CheckT1(nT1); // update timer1 control bits + } + LeaveCriticalSection(&csT1Lock); + return nT1; +} + +VOID SetT1(BYTE byValue) +{ + BOOL bEqual; + + _ASSERT(byValue < 0x10); // timer1 is only a 4bit counter + + EnterCriticalSection(&csT1Lock); + { + bEqual = (Chipset.t1 == byValue); // check for same value + } + LeaveCriticalSection(&csT1Lock); + if (bEqual) return; // same value doesn't restart timer period + + if (uT1TimerId != 0) // timer1 running + { + timeKillEvent(uT1TimerId); // stop timer1 + uT1TimerId = 0; // set flag timer1 stopped + } + EnterCriticalSection(&csT1Lock); + { + Chipset.t1 = byValue; // set new timer1 value + CheckT1(Chipset.t1); // test timer1 control bits + } + LeaveCriticalSection(&csT1Lock); + if (bStarted) // timer running + { + // restart timer1 to get full period of frequency + VERIFY(uT1TimerId = timeSetEvent(T1_FREQ,0,&TimeProc,1,TIME_PERIODIC)); + } + return; +} diff --git a/SOURCE/TYPES.H b/SOURCE/TYPES.H new file mode 100644 index 0000000..40c9a98 --- /dev/null +++ b/SOURCE/TYPES.H @@ -0,0 +1,110 @@ +/* + * types.h + * + * This file is part of Emu48 + * + * Copyright (C) 1995 Sebastien Carlier + * + */ + +// HST bits +#define XM 1 +#define SB 2 +#define SR 4 +#define MP 8 + +#define SWORD SHORT // signed 16 Bit variable +#define QWORD ULONGLONG // unsigned 64 Bit variable + +#define CHIPSET Chipset_t +typedef struct +{ + SWORD nPosX; // position of window + SWORD nPosY; + BYTE type; // calculator type + + DWORD Port0Size; // real size of module in KB + DWORD Port1Size; // real size of module in KB + DWORD Port2Size; // real size of module in KB (HP49G only) + LPBYTE Port0; + LPBYTE Port1; + LPBYTE Port2; + + DWORD pc; + DWORD d0; + DWORD d1; + DWORD rstkp; + DWORD rstk[8]; + BYTE A[16]; + BYTE B[16]; + BYTE C[16]; + BYTE D[16]; + BYTE R0[16]; + BYTE R1[16]; + BYTE R2[16]; + BYTE R3[16]; + BYTE R4[16]; + BYTE ST[4]; + BYTE HST; + BYTE P; + WORD out; + WORD in; + BOOL SoftInt; + BOOL Shutdn; + BOOL mode_dec; + BOOL inte; // interrupt status flag (FALSE = int in service) + BOOL intk; // 1 ms keyboard scan flag (TRUE = enable) + BOOL intd; // keyboard interrupt pending (TRUE = int pending) + BOOL carry; + + WORD crc; + WORD wPort2Crc; // fingerprint of port2 + WORD wRomCrc; // fingerprint of ROM +#if defined _USRDLL // DLL version + QWORD cycles; // oscillator cycles +#else // EXE version + DWORD cycles; // oscillator cycles + DWORD cycles_reserved; // reserved for MSB of oscillator cycles +#endif + DWORD dwKdnCycles; // cpu cycles at start of 1ms key handler + + UINT Bank_FF; // save state of HP48GX port2 or state of HP49G ROM FF + UINT FlashRomState; // WSM state of flash memory (unused) + BYTE cards_status; + BYTE IORam[64]; // I/O hardware register + UINT IOBase; // address of I/O modules page + BOOL IOCfig; // I/O module configuration flag + BYTE P0Base, BSBase, P1Base, P2Base; // address of modules first 2KB page + BYTE P0Size, BSSize, P1Size, P2Size; // mapped size of module in 2KB + BYTE P0End, BSEnd, P1End, P2End; // address of modules last 2KB page + BOOL P0Cfig, BSCfig, P1Cfig, P2Cfig; // module address configuration flag + BOOL P0Cfg2, BSCfg2, P1Cfg2, P2Cfg2; // module size configuration flag + + BYTE t1; + DWORD t2; + + BOOL bShutdnWake; // flag for wake up from SHUTDN mode + + BYTE Keyboard_Row[9]; + WORD IR15X; + UINT Keyboard_State; // not used + + signed short loffset; + signed int width; + UINT boffset; + UINT lcounter; + UINT sync; // not used + BYTE contrast; + BOOL dispon; // not used + DWORD start1; + DWORD start12; + DWORD end1; + DWORD start2, end2; + + // CdB for HP: add apples header + DWORD d0size; // no. of header display lines + BYTE d0memory[4096*2]; // memory for header display area + DWORD d0offset; // offset inside the header display for the content + DWORD d0address; // address in saturn addr area for d0memory (2 pages) +// BOOL d0Cfig; // modul configured +} Chipset_t; diff --git a/SOURCE/gpl.txt b/SOURCE/gpl.txt new file mode 100644 index 0000000..45645b4 --- /dev/null +++ b/SOURCE/gpl.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License.