2017-08-18: Updated to version 57

Signed-off-by: Gwenhael Le Moine <gwenhael.le.moine@gmail.com>
This commit is contained in:
Gwenhael Le Moine 2024-03-20 07:46:28 +01:00
parent 5b61a0678e
commit d048b845e4
No known key found for this signature in database
GPG key ID: FDFE3669426707A7
41 changed files with 2197 additions and 6426 deletions

View file

@ -1,373 +0,0 @@
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.) System Menu / Debugger Settings
This is the configuration dialog for the debugger.
In the "Disassembler" section you can switch between HP and Class Mnemonics for the disassembler. This switch is exactly the same like in the main Settings dialog.
In the "Symbolic" section you can setup the debugger for symbolic debugging. Therefore the debugger needs a reference file. The reference file must contain a table for converting an absolute address into a symbolic name. I decided to use the HP-TOOLS object files (file extension *.o) with the supported entries. These object files are usually provided by HP for the HP48 and HP49 series and are necessary for linking object files. In the case of older calculators you have to make them by your own. Using the object file for the linker as reference has two disadvantages. First such an object file has an assembler specific output format, in our case the debugger only understands the output format of HP SASM v3.x. Second, in some cases an entries has two or more different names and I cannot control which name is returned.
Example from ENTRIES.A (HP48 Supported ROM Entry Points)
=UNROT EQU #60FAC *
=3UNROLL EQU #60FAC *
=XYZ>ZXY EQU #60FAC *
My implementation returns the last name inside the linker object file to the chosen address.
If you don't have the linker object file, the HP28S entry point list for example is only distributed as assembler source file SUPROM28.A, you can simply generate it with
sasm -N SUPROM28.A
- Enable
With an unchecked Enable check box you can disable the symbolic debugging without removing the reference file.
- Model
Each calculator model needs his own symbol reference file. The given models exactly corresponds to the one used in the KML script. When you opening the settings dialog automatically the actual model is chosen in the combo box. You can switch to all other possible calculator models to enter the corresponding reference files. When you exit the dialog with the Ok button all filenames are saved.
- Edit field
The edit field must contain the filename to the model specific symbol reference file which is chosen by the combo box.
2.) 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.
3.) 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.
4.) 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.
5.) 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.
6.) 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. Therefore you can enter a hexadecimal number or the symbolic reference name.
- 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!
- Find
Search in the mapped CPU address area for an address which contain a PCO (Primitive Code Object) header. The disassembled code of this address is shown in the Code window.
- Previous PCO
Search for a PCO before the address shown in the first line of the Code window.
- Next PCO
Search for a PCO behind the address shown in the first line of the Code window.
7.) 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.
8.) 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, the + and - keys change the memory position by one nibble under the cursor. 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. Therefore you can enter a hexadecimal number or the symbolic reference name.
- 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. If the data sequence is found the Memory window and an opened "RPL Object Viewer" window will be updated.
With the button "Previous" you can search for the previous and with the button "Next" you can search for the next occurrence of the data sequence.
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
- Load Memory Data...
The "Load Memory Data" dialog box allows loading memory dump files to the specified address inside the Saturn address area. The specified address must point to RAM, writing into ROM areas isn't possible. The memory dump file must be in packed data format, meaning each byte in file contain two Saturn data nibbles with the low nibble containing the even and the high nibble the following odd address. The disadvantage of packed files is that you cannot load memory files with an odd number of data nibbles, but the advantage is that you can directly load an assembler output to memory.
- Save Memory Data...
The "Save Memory Data" dialog box allows saving the data of the specified Saturn address area into a memory dump file. The memory dump file contain the data in packed data format, meaning each byte in file contain two Saturn data nibbles with the low nibble containing the even and the high nibble the following odd address.
- RPL Object Viewer...
This opens a small toolbox window showing the decompiled RPL object in the selected memory map mode at the memory address marked by the cursor. If the toolbox window is already open the content will be updated. There's a problem if you want to select an address inside the marked two addresses. The easiest way to switch the address is the use of the + and - keys changing the memory position by one nibble under the cursor.
9.) 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.
10.) 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.
11.) 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.
01/09/13 (c) by Christoph Gießelink

482
EMU48.TXT
View file

@ -1,482 +0,0 @@
Emu48 - A freeware HP38/39/40/48/49 Emulator
for Windows 9x, ME, NT, 2000, XP, Vista and 7
********************
* OPERATING SYSTEM *
********************
This version of Emu48 should work with all Intel x86 and x64 platforms.
****************
* INSTALLATION *
****************
Emu48 is distributed in 1 archive:
- Emu48v15xSetup.zip All files and sources
To install Emu48, just start the executable file inside the Emu48v15xSetup.zip
archive. The installer will guide you through the installation. When you first
run Emu48, it will detect the directory in which you installed it, and will
write its configuration to the registry HKCU\Software\Emu48. If you move the
Emu48 directory to another place you have to change the directory path inside
the "Choose Your KML Script" dialog.
You can also update your current version with the Service Packs:
- E48BP5x.ZIP New EXE-File
- E48SP5x.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.hpcalc.org/details.php?id=633. 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-file> ROM.38G
Where <rom-file> 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://hp.giesselink.com/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-file> ROM.39G
There's also a HP39G/HP40G beta ROM for emulators inside an old Emu48 package
available at http://www.hpcalc.org/details.php?id=4272.
- 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-file> ROM.48G
or Convert <rom-file> ROM.48S
Where <rom-file> 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.hpcalc.org/details.php?id=3686
ROMDump Wizard http://hp.giesselink.com/emu48.htm
- HP49G:
There's no ROM download program available so far. But you can create a ROM image
with the UPD49ROM tool available at http://hp.giesselink.com/emu48.htm and a ROM
update file for the HP49G calculator available at
http://www.hpcalc.org/hp49/pc/rom/. I suggested to use version 1.19-6
(http://www.hpcalc.org/details.php?id=3240).
To create a HP49G ROM image file, start a Command Prompt while running Windows,
and type:
UPD49ROM -f hp49119-6.flash ROM.49G
This will create a HP49G ROM image file with an empty User Port 2.
****************
* 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://hp.giesselink.com/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 call 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) or press the "..." button
for the file browser. If this field is disabled you have chosen the Port 2 file
over the 2nd command line argument, so change the name there please.
The "Port 2 is Writeable" check box represents the actual read/write state of
the Port 2 file. Changing the state will also change the state for the
calculator by modifying the Read-Only attribute of the file.
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.
Please remember, all port configuration changes mostly behave like on the
original calculator. This means when you do this changes with the emulated
calculator on, it's the same like when you do this with a real calculator on. In
many times, this depends on the current state of the calculator, this will work
without any problems by doing an automatically calculator warmstart. But for the
most secure way, switch off the emulated calculator first, please!
***********************
* 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 Stack" and "Paste Stack" in the "Edit" menu you're
able to copy real and complex numbers and 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.
********************************
* Registry HKCU\Software\Emu48 *
********************************
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.
*******************************
* INFRARED PRINTER SIMULATION *
*******************************
The emulator has the ability to print decoded data to a HP82240A/B printer
simulation. The data transfer to the printer simulator is done over UDP. You can
the define the IPv4 address and the port the printer simulator is listening.
****************
* 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.
************
* DEBUGGER *
************
Emu48 has an integrated Saturn assembler debugger inside. For further
information read the separated "Debugger.txt" documentation please.
**************
* 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. In difference
you can choose the stack level for the transfer in the DDE item field. Take care
to transmit data only after the acknowledge of the last DDE transaction.
Technical data:
Servername: Emu48
Topicname: Stack
Item: 1 (stack level)
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://hp.giesselink.com/index.htm to get more
information please.
***********
* SUPPORT *
***********
We cannot provide any individual support for Emu48. Information about Emu48 will
be on the Emu48 Homepage or on the Emu48 FAQ at
http://hp.giesselink.com/index.htm
It's also a good idea to look at the Usenet forum comp.sys.hp48. Emu48 topics
have discussed there in different threads for years now.
***************
* LEGAL STUFF *
***************
Emu48 - An HP38/39/40/48/49 Emulator
Copyright (C) 2013 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 *
***************
Sebastien Carlier
Christoph Giesselink
E-Mail:
c dot giesselink at gmx dot de
Homepage:
http://hp.giesselink.com/

View file

@ -1,29 +0,0 @@
Emu48 1.56+ (based on Emu48 1.58)
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.

BIN
Emu48.dll

Binary file not shown.

BIN
Emu48.exe

Binary file not shown.

651
Emu48.htm Normal file
View file

@ -0,0 +1,651 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Emu48 Manual</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta http-equiv="Content-Language" content="en-us">
<meta name="author" content="Christoph Giesselink">
<meta name="description" content="Emulator Manual Emu48">
<style type="text/css">
<!--
body { background-color:white;
font-family:'Times New Roman',Times,Arial,serif;
/* font-size:76%; */
}
p { font-size:1.0em; }
h1 { color:red; font-size:1.5em; }
h2 { color:red; font-size:1.5em; }
h3 { color:red; font-size:1.1em; }
#headline { margin-left: 20%; text-align:center; font-size:1.5em; }
#navi { float: left; width: 15%; }
#main { margin-left: 20%; }
.nav1 { font-weight:bold; font-size:0.8em; }
.nav2 { font-weight:normal; font-size:0.8em; }
-->
</style>
</head>
<body>
<div id="headline">
<p>Emu48 - A freeware HP38G/39G/40G/48SX/48GX/49G Emulator<br>
for Windows 9x, ME, NT, 2000, XP, Vista, 7, 8 and 10</p>
</div>
<div id="navi">
<p><a class="nav1" href="#s1">1. General</a></p>
<p><a class="nav1" href="#s2">2. Acknowledgements</a></p>
<p><a class="nav1" href="#s3">3. ROM Images</a></p>
<p><a class="nav1" href="#s4">4. Installation</a></p>
<p><a class="nav1" href="#s5">5. How to Start</a></p>
<p><a class="nav1" href="#s6">6. Command Line</a></p>
<p><a class="nav1" href="#s7">7. Virtual Keyboard</a></p>
<p><a class="nav1" href="#s8">8. File Menu</a><br>
<span class="nav2">
<a href="#ss8.1">8.1 New...</a><br>
<a href="#ss8.2">8.2 Open...</a><br>
<a href="#ss8.3">8.3 Save</a><br>
<a href="#ss8.4">8.4 Save As...</a><br>
<a href="#ss8.5">8.5 Close</a><br>
<a href="#ss8.6">8.6 Settings</a><br>
<a href="#ss8.6.1">8.6.1 Settings General</a><br>
<a href="#ss8.6.2">8.6.2 Settings Memory</a><br>
<a href="#ss8.6.3">8.6.3 Settings Peripheral</a><br>
<a href="#ss8.7">8.7 Exit</a>
</span></p>
<p><a class="nav1" href="#s9">9. Edit Menu</a><br>
<span class="nav2">
<a href="#ss9.1">9.1 Load Object...</a><br>
<a href="#ss9.2">9.2 Save Object...</a><br>
<a href="#ss9.3">9.3 Copy Screen</a><br>
<a href="#ss9.4">9.4 Copy Stack</a><br>
<a href="#ss9.5">9.5 Paste Stack</a><br>
<a href="#ss9.6">9.6 Reset Calculator</a><br>
<a href="#ss9.7">9.7 Backup</a><br>
<a href="#ss9.7.1">9.7.1 Backup Save</a><br>
<a href="#ss9.7.2">9.7.2 Backup Restore</a><br>
<a href="#ss9.7.3">9.7.3 Backup Delete</a>
</span></p>
<p><a class="nav1" href="#s10">10. View Menu</a><br>
<a class="nav2" href="#ss10.1">10.1 Change KML Script...</a></p>
<p><a class="nav1" href="#s11">11. Tools Menu</a><br>
<span class="nav2">
<a href="#ss11.1">11.1 Disassembler...</a><br>
<a href="#ss11.2">11.2 Debugger...</a><br>
<a href="#ss11.3">11.3 Macro</a><br>
<a href="#ss11.3.1">11.3.1 Macro Record...</a><br>
<a href="#ss11.3.2">11.3.2 Macro Play...</a><br>
<a href="#ss11.3.3">11.3.3 Macro Stop</a><br>
<a href="#ss11.3.4">11.3.4 Macro Settings...</a>
</span></p>
<p><a class="nav1" href="#s12">12. Help Menu</a><br>
<span class="nav2">
<a href="#ss12.1">12.1 Help Topics</a><br>
<a href="#ss12.2">12.2 About Emu48...</a>
</span></p>
<p><a class="nav1" href="#s13">13. DDE Server</a></p>
<p><a class="nav1" href="#s14">14. Emu48+ Changes</a></p>
<p><a class="nav1" href="#s15">15. License</a></p>
</div>
<div id="main">
<h1><a name=s1>1. General</a></h1>
<p>Emu48 is an emulator for the Hewlett Packard HP38G, HP39G,
HP40G, HP48SX, HP48GX and HP49G calculator hardware. These calculators
are based on the 1LT8 Clarke (HP48SX) and on the Yorke chip.</p>
<h1><a name=s2>2. Acknowledgements</a></h1>
<p>First of all a big thank to S&eacute;bastien Carlier for publishing
Emu48 v1.0 under the GPL. Without this decision newer versions of the
emulator wouldn't have been possible or ports to other similar
calculators wouldn't have been made. Also a big thank to Jean-Yves
Avenard for his technical assistance in the beginning. And finally I
want to thank all the unnamed authors for publishing material about
these calculators.</p>
<h1><a name=s3>3. ROM Images</a></h1>
<p>Emu48 needs an image of a calculator ROM to be able to run. ROM
images are valid in a packed (even address lower nibble, odd address
higher nibble) or unpacked (one nibble per byte with even address first)
form.</p>
<p>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.</p>
<ul>
<li>HP38
<p>To upload the ROM of your HP38G, you will need a special aplet
called <a href="http://www.hpcalc.org/details.php?id=633">&quot;ROM UPLOAD&quot;</a>.
Once you've uploaded the ROM, you have to convert it using the
Convert utility.</p>
<p>To do that, start a Command Prompt while running Windows, and
type:</p>
<blockquote>Convert &lt;rom-file&gt; ROM.38G</blockquote>
<p>Where &lt;rom-file&gt; is the path to your ROM image. This will
create a file named ROM.38G. This tool will also check its validity.
</p></li>
<li>HP39/40
<p>To upload the ROM of your HP39G/HP40G, you will need a special aplet
called <a href="http://hp.giesselink.com/emu48.htm">&quot;ROM UPLOAD&quot;</a>.
Once you've uploaded the ROM, you have to convert it using the Rom2emu utility.
</p>
<p>To do that, start a Command Prompt while running Windows, and
type:</p>
<blockquote>Rom2emu &lt;rom-file&gt; ROM.39G</blockquote>
<p>There's also a HP39G/HP40G beta ROM for emulators inside an old
<a href="http://www.hpcalc.org/details.php?id=4272">Emu48 package</a>.
</p></li>
<li>HP48
<p>If you have already used another HP48 emulator, you can convert
the ROM using the Convert utility.
</p>
<p>To do that, start a Command Prompt while running Windows, and
type:</p>
<blockquote>Convert &lt;rom-file&gt; ROM.48G</blockquote>
<p>or</p>
<blockquote>Convert &lt;rom-file&gt; ROM.48S</blockquote>
<p>Where &lt;rom-file&gt; 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.</p>
<p>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.</p>
<p>You can find the latest version of the ROM dump programs on:</p>
<blockquote>
ROMUPL.BIN <a href="http://www.hpcalc.org/details.php?id=3686">
http://www.hpcalc.org/details.php?id=3686</a><br>
ROMDump Wizard
<a href="http://hp.giesselink.com/emu48.htm">
http://hp.giesselink.com/emu48.htm</a>
</blockquote></li>
<li>HP49G
<p>There's no ROM download program available so far. But you can
create a ROM image with the
<a href="http://hp.giesselink.com/emu48.htm">UPD49ROM tool</a>
and a <a href="http://www.hpcalc.org/hp49/pc/rom/">
ROM update file</a> for the HP49G calculator. I suggested to use
<a href="http://www.hpcalc.org/details.php?id=3240">version 1.19-6</a>
</p>
<p>To create a HP49G ROM image file, start a Command Prompt while
running Windows, and type:</p>
<blockquote>UPD49ROM -f hp49119-6.flash ROM.49G</blockquote>
<p>This will create a HP49G ROM image file with an empty User
Port 2.</p></li>
</ul>
<h1><a name=s4>4. Installation</a></h1>
<p>To install Emu48 you may use the installer package which contain,
among the binaries, some HP48 KML scripts or just unzip the emulator
and the required emulator skins from archives into an empty directory.
Finally you have to copy your ROM images into this directory and
adjust the ROM image name to the name used in the corresponding KML
script. When you first run Emu48, it will detect the directory in
which you installed it, and will write the configuration to the
registry at <i>HKCU\Software\Emu48</i>.</p>
<h1><a name=s5>5. How to Start</a></h1>
<p>When Emu48 is installed and you have put valid KML scripts and the
corresponding ROM image(s) into your Emu48 installation directory, you
can start Emu48. You'll see a &quot;Choose Your KML Script&quot;
box.</p>
<p>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.</p>
<p>Check that the path in the &quot;Emu48 Directory&quot; text area is
correct. Modify it if the directory in which you installed Emu48 is not
the directory displayed. Click the refresh button (&quot;V&quot;) after
modifying it to update the list box or use the (&quot;...&quot;) button to
start the directory browser.</p>
<p>Choose a KML script in the list box for your calculator ROM you put
into Emu48's directory.</p>
<p>Several HP48 scripts are included in the Emu48 archive:</p>
<ul>
<li>Emu48's Default Faceplate for HP48G/GX</li>
<li>Emu48's Default Faceplate for HP48S/SX
<p>These two are simple scripts, good for 800x600 display
resolution.</p></li>
<li>Casey's Gx with Toolbar and Touch Screen</li>
<li>Casey's Sx with Toolbar and Touch Screen
<p>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!</p></li>
<li>Floating buttons
<p>This one looks really great.</p></li>
<li>Small but realistic HP48 Gx
<p>This one has been designed for small resolutions such as
640x480.
</p></li>
</ul>
<p>If you want other great scripts, visit Rechlin's great HP archive
<a href="http://www.hpcalc.org/"></a></p>
<p>And if you are interested in writing new scripts, get the KML 2.0
documentation from <a href="http://hp.giesselink.com/emu48.htm">the
authors Emu48 page</a>.</p>
<p>Once you have selected a script, press OK to start the emulator. In
most cases, when Emu48 crash after pressing the OK button, you are using
an invalid ROM image. While it's running, you can use the View/Change KML
Script... command to change the visual aspect of Emu48.</p>
<h1><a name=s6>6. Command Line</a></h1>
<p>The command line syntax is &quot;<i>Emu48 [E48file [Port2file]]</i>&quot;.
The first parameter sets the filename for the emulation data
independent from the &quot;LastDocument&quot; setting, 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.</p>
<h1><a name=s7>7. Virtual Keyboard</a></h1>
<p>There are two ways to use the virtual keyboard on the emulated
calculator:</p>
<ol>
<li><a href="#mouse">by Mouse</a></li>
<li><a href="#keyboard">by PC keyboard</a></li>
</ol>
<p><a name=mouse></a>
The easiest way to use the emulated calculator is using the mouse. The KML
script define buttons with an area where mouse input is active. The mouse
cursor change from an arrow to a hand cursor in these areas. The state of
the virtual key follow the state of your left mouse button. When the mouse
cursor leaves the virtual key area the virtual button automatically
release. In some cases you need to press more than one key on the
emulator. For these cases press the virtual key with the right mouse
button. When you release the mouse button or leave the area of the virtual
key, the key is still hold. To release all hold virtual buttons, just use
the left mouse button again. A single release of a hold virtual key isn't
possible.</p>
<p><a name=keyboard></a>
Another convenient way is using the PC keyboard. The KML script language
support a large variety of commands to implement this feature. So keyboard
usage depends on your used KML script and not on the emulator. Because of
this it's impossible to say what's happen when you press a key on the PC
keyboard. For further details read the KML 2.0 documentation mentioned
before please.</p>
<h1><a name=s8>8. File Menu</a></h1>
<h2><a name=ss8.1>8.1 New...</a></h2>
<p>Creates a new emulation session. You're asked for a new KML script
where you can select the calculator type and skin to emulate.</p>
<h2><a name=ss8.2>8.2 Open...</a></h2>
<p>Opens an existing emulation session. The emulation continues at the
same position where the loaded session was aborted. Loading emulation
sessions made with a different ROM revision may <u>destroy</u> the memory
content or may cause other unpredictable results.</p>
<h2><a name=ss8.3>8.3 Save</a></h2>
<p>Saves the current running session with the actual name.</p>
<h2><a name=ss8.4>8.4 Save As...</a></h2>
<p>Saves the current running session with a new name. You're also get in
this dialog when you Exit a new session without a state file name.</p>
<h2><a name=ss8.5>8.5 Close</a></h2>
<p>Closes the current session without closing the emulator.</p>
<h2><a name=ss8.6>8.6 Settings</a></h2>
<p>This calls the Settings dialog. This dialog has three tabs:
General, Memory and Peripheral.</p>
<h3><a name=ss8.6.1>8.6.1 Settings General</a></h3>
<h4>8.6.1.1 Section General</h4>
<ul>
<li><i>Authentic Calculator Speed</i>
<p>When this option is checked, the emulation speed will be similar
to the real calculator depending on the RATE control register
content.</p></li>
<li><i>Enable Virtual LCD Delay</i>
<p>Try this option for a better 4 color gray scale display simulation
output.</p></li>
<li><i>Always On Top</i>
<p>When this option is checked, the emulator window will always be the
topmost one.</p></li>
<li><i>Activation Follows Mouse</i>
<p>This option enables a X-Mouse style windows activation. When the
mouse is moved over the emulator window, the emulator is getting the
focus and popping up into foreground.</p></li>
<li><i>Single Instance</i>
<p>When this option is checked, the program is only allowed to run in
a single instance. If another running instance is detected, the detected
instance is set into foreground as active window and get a request to
change his state file to the given one by the current instance. Finally
the current instance is terminated.</p></li>
<li><i>Automatically Save Files</i>
<p>When this option is checked, the current state file will automatically
saved when you change to another state file, but not when you close the
emulator program.</p></li>
<li><i>Automatically Save Files On Exit</i>
<p>When this option is checked, the current state file will be saved
automatically at the end when the emulator program is closed.</p></li>
<li><i>Show Load Object Warning</i>
<p>When this option is checked, you'll get a warning message box when you
try to load an object with the <i>Load Object...</i> menu command. If
this option is unchecked, the warning will be skipped.</p></li>
<li><i>Always Show KML Compilation Result</i>
<p>When this option is checked, you see the results of the KML
(Keyboard Macro Language) interpreter at every KML script load.</p></li>
</ul>
<h4>8.6.1.2 Section Style</h4>
<ul>
<li><i>Show Title</i>
<p>When this option is checked, the window title bar is visible.</p></li>
<li><i>Show Menu</i>
<p>When this option is checked, the menu bar is enabled. If unchecked,
the menu is accessible as context menu in the client area outside the
calculator button definitions.</p></li>
</ul>
<h4>8.6.1.3 Section Disassembler</h4>
<p>Choosing the assembler syntax:</p>
<ul>
<li><i>HP Mnemonics</i>
<p>This is the standard syntax used by HP.</p></li>
<li><i>Class Mnemonics</i>
<p>Class (Clarke assembler) was written by Lutz Vieweg in 1991, at a time
when HP had not published their own development tools. The syntax is very
similar to the AG and STAR mnemonics used at this time. Especially
published assembler programs written for the HP28S use the similar AG
syntax.</p></li>
</ul>
<h3><a name=ss8.6.2>8.6.2 Settings Memory</a></h3>
<h4>8.6.2.1 Section Memory Cards</h4>
<ul>
<li><i>Port 1 is Plugged</i>
<p>When this option is checked, a 128 KB RAM card is emulated in card
Port 1. The RAM card content is saved in the current emulator state
file.</p></li>
<li><i>Port 1 is Writeable</i>
<p>When this option is checked, the RAM card in card Port 1 is
writable else the card is Read-Only by simulating the card write
protect switch.</p></li>
<li><i>Port 2 is Shared</i>
<p>When this option is unchecked, only the first instance of Emu48
will allow you to use the RAM card in Port 2. When this option 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.</p></li>
<li><i>Port 2 is Writeable</i>
<p>This option represents the actual read/write state of the Port 2
file. Changing the option will also change the state for the
calculator by modifying the Read-Only attribute of the file.</p></li>
<li><i>Port 2 File</i>
<p>You can add a 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 one. 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!</p>
<p>If you already have a Port 2 card file in unpacked format, you
have to copy the file into the emulator directory. If you choose a
different directory you have to use a full path file name.</p>
<p>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 4 MB RAM card. You always
get the message &quot;Warning: Invalid Card Data&quot; at startup
and Port 33 is inaccessible. This is not a bug of the emulator!</p>
<p>When you have created or copied the file, enter the card file
name into the Port 2 File edit box.</p>
<p>Please remember, all port configuration changes mostly behave
like on the original calculator. This means when you do this changes
with the emulated calculator on, it's the same like when you do this
with a real calculator on. In many times, this depends on the current
state of the calculator, this will work without any problems by doing
an automatically calculator warmstart. But for the most secure way,
switch off the emulated calculator first, please!</p></li>
</ul>
<h3><a name=ss8.6.3>8.6.3 Settings Peripheral</a></h3>
<h4>8.6.3.1 Section Sound</h4>
<p>A new implementation of the sound engine made ROM patches for sound
output obsolete. The new sound engine emulates the behavior of the beeper
output ports and only work in connection with a sound card. Using the
internal PC speaker isn't possible any more. The old beeper method with a
ROM patch is still working but deprecated, it's strongly recommended to
remove all beep patches from your current KML scripts to enable the new
sound engine. The support of the old sound implementation by a ROM patch
maybe removed in later versions of the emulator and remaining beep
patches will corrupt the ROM with an illegal opcode then.
</p>
<p>
For the sound generation the calculator must know his own CPU strobe
frequency. On the real calculator the speed depends on various settings
like component tolerances, actual temperature, humidity and other
variables. The resulting speed is measured by the calculator firmware
at a cold- or at a warmstart and stored in the =CSPEED variable. The
content of this calculator variable has direct influence on the
resulting frequency and duration. On the emulator the HP48SX CPU
strobe frequency is set by the registry key
<i>HKCU\Software\Emu48\Emulator\SXCycles</i>, for all other
calculators at <i>HKCU\Software\Emu48\Emulator\GXCycles</i>.
For some reasons the CPU cycles are only estimated and so the
strobe frequency value in the registry is not the exact CPU strobe
frequency of the calculator in Hz divided by 16384 like in the
other emulators. Because older versions of the emulator were not
able to measure the CPU strobe frequency properly or the strobe
frequency registry content has been changed since the last
measurement, the =CSPEED variable of this session file may contain
a wrong frequency value. You easily may discover this by measuring
the real duration of a 10s beep. Is the difference to 10s less
than 1s everything is ok, if not, you should perform a
<u style="color:red">warmstart</u> of the calculator in this
session file. Alternatively you may execute a
<a href=#ss9.6>Reset Calculator</a>. This recalls the measuring
routine and save the result in the speed variable. Both restart
variants purge the stack content!
</p>
<ul>
<li><i>Volume</i>
<p>The output volume can be selected with the Volume slider relative to
the Master Volume control.
</p></li>
<li><i>Device</i>
<p>By default the sound device is set to &quot;Standard Audio&quot;, but
you can also manually choose the output device. The device name is
somehow cut since Window Vista, but the method of reading the device
name is used for backwards compatibility to older versions of the
Operating System. When you change the Standard Audio device in the
Operating System settings dialog, the internal device numbering may
change, and so the manually selected audio device.</p></li>
</ul>
<h4>8.6.3.2 Section Infrared Printer</h4>
<p>The emulator has the ability to print data to a HP82240A/B printer
simulation. The data transfer to the printer simulator is done over UDP.
In this section you can the define the IPv4 address and the port the
printer simulator is listening. A suitable HP82240B printer simulation can
be found <a href="http://hp.giesselink.com/hp82240b.htm">here</a>.</p>
<h4>8.6.3.3 Section Serial Ports</h4>
<ul>
<li><i>Wire</i>
<p>In the Wire combo box you can select the COM port device connected
to the wire port of the calculator.</p></li>
<li><i>Ir</i>
<p>In the Ir combo box you can select the COM port device connected
to the IR port of the calculator. Please remember that the IR port
only work with 2400 baud.</p></li>
</ul>
<h2><a name=ss8.7>8.7 Exit</a></h2>
<p>Quit emulation. The default actions at finishing are defined in the
<a href=#ss8.6>Settings</a> dialog. If the current session is
&quot;Untitled&quot; you are asked for a session file name using the
<a href="#ss8.4">Save As...</a> dialog. If you quit the emulator
without a given filename, you're asked for choosing a KML script at
next startup.</p>
<h1><a name=s9>9. Edit Menu</a></h1>
<h2><a name=ss9.1>9.1 Load Object...</a></h2>
<p>This is only valid for the HP48SX, HP48GX and the HP49G emulation.
You can load HP48 and HP49G binary objects to stack level 1. Therefore
the object must begin with &quot;HPHP48-x&quot; for a HP48 or with
&quot;HPHP49-x&quot; for a HP49G binary object where x can be any
alphanumeric character. If the binary header isn't present, the object
is loaded as string. Dropping HP objects over the emulator window will
also load objects. Be sure that the emulator isn't busy before doing
this.</p>
<h2><a name=ss9.2>9.2 Save Object...</a></h2>
<p>This is only valid for the HP48SX, HP48GX and the HP49G emulation.
Save the current object in stack level 1 as binary object to disk.</p>
<h2><a name=ss9.3>9.3 Copy Screen</a></h2>
<p>Copy the screen content as bitmap to the clipboard.</p>
<h2><a name=ss9.4>9.4 Copy Stack</a></h2>
<p>This is only valid for the HP48SX, HP48GX and the HP49G emulation.</p>
<p>Copy a &quot;Real Number&quot;, &quot;Complex Number&quot; or
&quot;String&quot; object in stack level 1 to the clipboard. On all
other objects, the command will be ignored. This prevents sending
binary objects to the clipboard.</p>
<p>The decimal point (radix mark) of &quot;Real Numbers&quot; in the
clipboard is equal to the calculator setting. This point maybe
important when you try to paste the numbers into a program using the
locale settings of the host operating system.</p>
<h2><a name=ss9.5>9.5 Paste Stack</a></h2>
<p>This is only valid for the HP48SX, HP48GX and the HP49G emulation.</p>
<p>Paste the text field content of the clipboard to stack level 1 of
the emulated calculator. If the clipboard content is representing a
real number, the number will be saved as &quot;Real Number&quot;
object. Is the content a complex number object, the number will be
saved as &quot;Complex Number&quot; object, in all other cases as
&quot;String&quot; object.</p>
<p>To import &quot;Real or Complex Numbers&quot; from the clipboard,
the decimal point (radix mark) of the clipboard and calculator
<u>must</u> be equal. A real or complex number is only detected in the
case of valid real number characters in the clipboard. Especially
heading and tailing white spaces aren't valid number characters
also.</p>
<p>Complex numbers must be in the form <i>(a,b)</i> when using the
point radix mark or in the form <i>(a;b)</i> when using the comma
radix mark. The Cartesian or algebraic form <i>a+bi</i> is not
supported.</p>
<h2><a name=ss9.6>9.6 Reset Calculator</a></h2>
<p>This emulates the Reset pin of the internal CPU.</p>
<h2><a name=ss9.7>9.7 Backup</a></h2>
<h3><a name=ss9.7.1>9.7.1 Backup Save</a></h3>
<p>This saves the current emulator status into a backup slot. If the
backup slot already contain data, it will be overwritten.</p>
<h3><a name=ss9.7.2>9.7.2 Backup Restore</a></h3>
<p>This restores a previous saved emulator status without request. If you
changed the calculator model meanwhile, the emulator will switch back to
the old model.</p>
<h3><a name=ss9.7.3>9.7.3 Backup Delete</a></h3>
<p>This deletes the data in the backup slot.</p>
<h1><a name=s10>10. View Menu</a></h1>
<h2><a name=ss10.1>10.1 Change KML Script...</a></h2>
<p>This allows you to change the skin of the current emulated calculator.
In opposite to the New... command you see only scripts emulating the same
calculator model.</p>
<h1><a name=s11>11. Tools Menu</a></h1>
<h2><a name=ss11.1>11.1 Disassembler...</a></h2>
<p>This is a simple disassembler.</p>
<p>Enter the address to disassemble in hexadecimal into the &quot;Address
(HEX)&quot; field and press &lt;Return&gt;. With the &quot;Next Address&quot;
button the next opcode is disassembled. With the &quot;Copy Data&quot; button
you can copy all selected lines inside the list box to the clipboard.</p>
<h2><a name=ss11.2>11.2 Debugger...</a></h2>
<p>The assembler code debugger of the emulator. For more details refer to the
extra documentation of the debugger please.</p>
<h2><a name=ss11.3>11.3 Macro</a></h2>
<p>The keyboard macro recorder unit.</p>
<h3><a name=ss11.3.1>11.3.1 Macro Record...</a></h3>
<p>Prompts a dialog to enter the macro file for the data to record. After
accepting the confirm message, every key event is recorded into the macro
file with it's time information.</p>
<h3><a name=ss11.3.2>11.3.2 Macro Play...</a></h3>
<p>Prompts a dialog box to ask for the keyboard macro file to play. The
replay starts immediately after selecting the file.</p>
<h3><a name=ss11.3.3>11.3.3 Macro Stop</a></h3>
<p>Stops recording or replaying a keyboard macro file.</p>
<h3><a name=ss11.3.4>11.3.4 Macro Settings...</a></h3>
<p>Settings for the Macro Replay mode</p>
<ul>
<li><i>Real</i>
<p>Replay macro with the original recording speed.</p></li>
<li><i>Manual</i>
<p>Replay macro with the speed set by the speed slider.</p></li>
</ul>
<h1><a name=s12>12. Help Menu</a></h1>
<h2><a name=ss12.1>12.1 Help Topics</a></h2>
<p>Call this document.</p>
<h2><a name=ss12.2>12.2 About Emu48...</a></h2>
<p>The version, copyright and license message...</p>
<h1><a name=s13>13. DDE Server</a></h1>
<p>Emu48 has an integrated DDE server to transmit data from and to the HP
stack. Because only the HP48 and HP49 have a stack, all DDE transfers
are ignored on the other calculators. You have the same restrictions like
with the commands &quot;Load object...&quot; and &quot;Save
Object...&quot;, that a running program may corrupt memory. In difference
you can choose the stack level for the transfer in the DDE item field.
Take care to transmit data only after the acknowledge of the last DDE
transaction.</p>
<p>Technical data:</p>
<table border="0" cellpadding="0" cellspacing="0" width="651" summary="DDE settings">
<tr>
<td width="111">Servicename:</td>
<td width="536">Emu48</td>
</tr>
<tr>
<td width="111">Topicname:</td>
<td width="536">Stack</td>
</tr>
<tr>
<td width="111">Item:</td>
<td width="536">1 (stack level)</td>
</tr>
<tr>
<td width="111">Clipboardformat:</td>
<td width="536">&quot;CF_HPOBJ&quot; (user defined)</td>
</tr>
</table>
<p>The DDE commands CONNECT, POKE and REQUEST are supported.</p>
<p>The structure of the clipboard format &quot;CF_HPOBJ&quot;:</p>
<table border="1" cellpadding="0" cellspacing="0" width="100%" summary="CF_HPOBJ format">
<tr>
<td width="34%">4 Byte (length of object, LSB first)</td>
<td width="66%" align="center">HP object (normal HP object)</td>
</tr>
</table>
<h1><a name=s14>14. Emu48+ Changes</a></h1>
<p>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.</p>
<p>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.</p>
<p>At present, the additional calculators supported in Emu48+ are the
49g+, 48gII (hardware revision 1), 50g, and 39g+/39gs/40gs.</p>
<p>To create KML scripts for the additional calculator models, use the
following model codes:</p>
<table border="0" cellpadding="0" cellspacing="0" width="651" summary="DDE settings">
<tr>
<td width="111">39g+/39gs:</td>
<td width="536">Model "P"<br>
Class 39</td>
</tr>
<tr>
<td width="111">40gs:</td>
<td width="536">Model "P"<br>
Class 40</td>
</tr>
<tr>
<td width="111">48gII:</td>
<td width="536">Model "2"</td>
</tr>
<tr>
<td width="111">49g+:</td>
<td width="536">Model "Q"</td>
</tr>
<tr>
<td width="111">50g:</td>
<td width="536">Model "Q"<br>
Class 50</td>
</tr>
</table>
<p>Most of the code that was changed in Emu48+ over Emu48 was provided by
Cyrille de Brebisson of Hewlett-Packard.</p>
<h1><a name=s15>15. License</a></h1>
<p>Emu48 - A HP38G/39G/40G/48SX/48GX/49G Emulator<br>
Copyright (C) 2017 Christoph Gie&szlig;elink</p>
<p>Emu48+ - A 39g+/39gs/40gs/48gII/49g+/50g Emulator<br>
Copyright (C) 2017 Cyrille de Brebisson<br>
Additional changes by Bill Graves and Eric Rechlin</p>
<p>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.</p>
<p>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.</p>
<p>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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.</p>
</div>
</body>
</html>

View file

@ -1,4 +1,10 @@
Known bugs and restrictions of Emu48 V1.58
Additional known bugs and restrictions of Emu48 V1.57+
------------------------------------------------------
- some display issues, such as when scrolling up, that were fixed in
regular Emu48 haven't yet been merged into Emu48+
Known bugs and restrictions of Emu48 V1.59
------------------------------------------
- the following I/O bits aren't emulated (incomplete)
@ -47,4 +53,4 @@ Known bugs and restrictions of Emu48 V1.58
- quitting the emulator while programming the flash isn't allowed,
because the content of flash state machine isn't saved so far
08/16/16 (c) by Christoph Gießelink, c dot giesselink at gmx dot de
08/15/17 (c) by Christoph Gießelink, c dot giesselink at gmx dot de

File diff suppressed because it is too large Load diff

View file

@ -156,6 +156,50 @@ static VOID DisableMenuKeys(HWND hDlg)
return;
}
//
// read edit control and decode content as hex number or if enabled as symbol name
//
static BOOL GetAddr(HWND hDlg,INT nID,DWORD *pdwAddr,DWORD dwMaxAddr,BOOL bSymbEnable)
{
TCHAR szBuffer[48];
INT i;
BOOL bSucc = TRUE;
HWND hWnd = GetDlgItem(hDlg,nID);
GetWindowText(hWnd,szBuffer,ARRAYSIZEOF(szBuffer));
if (*szBuffer != 0)
{
// if address is not a symbol name decode number
if ( !bSymbEnable || szBuffer[0] != _T('=')
|| RplGetAddr(&szBuffer[1],pdwAddr))
{
// test if valid hex address
for (i = 0; bSucc && i < (LONG) lstrlen(szBuffer); ++i)
{
bSucc = (_istxdigit(szBuffer[i]) != 0);
}
if (bSucc) // valid characters
{
// convert string to number
*pdwAddr = _tcstoul(szBuffer,NULL,16);
}
}
// inside address range?
bSucc = bSucc && (*pdwAddr <= dwMaxAddr);
if (!bSucc) // invalid address
{
SendMessage(hWnd,EM_SETSEL,0,-1);
SetFocus(hWnd); // focus to edit control
}
}
return bSucc;
}
//
// set mapping menu
//
@ -212,50 +256,6 @@ static VOID SetMappingMenu(HWND hDlg,UINT uID)
return;
};
//
// read edit control and decode content as hex number or if enabled as symbol name
//
static BOOL GetAddr(HWND hDlg,INT nID,DWORD *pdwAddr,DWORD dwMaxAddr,BOOL bSymbEnable)
{
TCHAR szBuffer[48];
INT i;
BOOL bSucc = TRUE;
HWND hWnd = GetDlgItem(hDlg,nID);
GetWindowText(hWnd,szBuffer,ARRAYSIZEOF(szBuffer));
if (*szBuffer != 0)
{
// if address is not a symbol name decode number
if ( !bSymbEnable || szBuffer[0] != _T('=')
|| RplGetAddr(&szBuffer[1],pdwAddr))
{
// test if valid hex address
for (i = 0; bSucc && i < (LONG) lstrlen(szBuffer); ++i)
{
bSucc = (_istxdigit(szBuffer[i]) != 0);
}
if (bSucc) // valid characters
{
// convert string to number
*pdwAddr = _tcstoul(szBuffer,NULL,16);
}
}
// inside address range?
bSucc = bSucc && (*pdwAddr <= dwMaxAddr);
if (!bSucc) // invalid address
{
SendMessage(hWnd,EM_SETSEL,0,-1);
SetFocus(hWnd); // focus to edit control
}
}
return bSucc;
}
//
// get address of cursor in memory window
//
@ -1423,7 +1423,6 @@ static BOOL OnDblClick(HWND hWnd, WORD wId)
ViewMemWnd(hDlg,dwAdrMem); // update memory window
SendMessage(hWnd,LB_SETCURSEL,i,0);
return FALSE;
return FALSE;
}
//
@ -1874,7 +1873,6 @@ static INT_PTR CALLBACK Debugger(HWND hDlg, UINT message, WPARAM wParam, LPARAM
// add Settings item to sysmenu
_ASSERT((IDM_DEBUG_SETTINGS & 0xFFF0) == IDM_DEBUG_SETTINGS);
_ASSERT(IDM_DEBUG_SETTINGS < 0xF000);
if ((hSysMenu = GetSystemMenu(hDlg,FALSE)) != NULL)
{
VERIFY(AppendMenu(hSysMenu,MF_SEPARATOR,0,NULL));
@ -2122,7 +2120,7 @@ static INT_PTR CALLBACK Debugger(HWND hDlg, UINT message, WPARAM wParam, LPARAM
case WM_NOTIFY:
// tooltip for toolbar
if(((LPNMHDR) lParam)->code == TTN_GETDISPINFO)
if (((LPNMHDR) lParam)->code == TTN_GETDISPINFO)
{
((LPTOOLTIPTEXT) lParam)->hinst = hApp;
((LPTOOLTIPTEXT) lParam)->lpszText = MAKEINTRESOURCE(((LPTOOLTIPTEXT) lParam)->hdr.idFrom);
@ -2984,7 +2982,7 @@ static BOOL ToggleBreakpointItem(HWND hWnd, INT nItem)
static VOID DrawBreakpoint(HWND hWnd, INT i)
{
TCHAR *szText,szBuffer[32];
LPARAM nItem;
INT nItem;
switch(sBreakpoint[i].nType)
{
@ -3150,7 +3148,7 @@ static INT_PTR CALLBACK EditBreakpoint(HWND hDlg, UINT message, WPARAM wParam, L
}
case WM_VKEYTOITEM:
if(LOWORD(wParam) == VK_SPACE)
if (LOWORD(wParam) == VK_SPACE)
{
hWnd = GetDlgItem(hDlg,IDC_BREAKEDIT_WND);
for (nItem = 0; nItem < wBreakpointCount; ++nItem)

View file

@ -85,7 +85,7 @@ static CONST MODEL_MAP_T MemMap[] =
&Port2, &Chipset.Port2Size // Port 1 part 2
},
{ // CdB for HP: add Q type
'Q', // HP49G+
'Q', // HP49g+
&pbyRom, &dwRomSize, // Flash
&Port0, &Chipset.Port0Size, // RAM
&pbyNoMEM, NULL, // BS
@ -93,7 +93,7 @@ static CONST MODEL_MAP_T MemMap[] =
&Port2, &Chipset.Port2Size // Port 1 part 2
},
{ // CdB for HP: add 2 type
'2', // HP48Gii
'2', // HP48gII
&pbyRom, &dwRomSize, // ROM
&Port0, &Chipset.Port0Size, // RAM
&pbyNoMEM, NULL, // BS
@ -101,7 +101,7 @@ static CONST MODEL_MAP_T MemMap[] =
&pbyNoMEM, NULL, // Port 1 part 2
},
{ // CdB for HP: add P type
'P', // HP39G+
'P', // HP39g+/gs
&pbyRom, &dwRomSize, // ROM
&Port0, &Chipset.Port0Size, // RAM
&pbyNoMEM, NULL, // BS

View file

@ -15,8 +15,10 @@
// #define DEBUG_DISPLAY // switch for DISPLAY debug purpose
#define NOCOLORSGRAY 8
#define NOCOLORSBW 2
#define NOCOLORSGRAY 8 // no. of colors in gray scale mode
#define NOCOLORSBW 2 // no. of colors in black and white mode
#define DISPLAY_FREQ 19 // display update 1/frequency (1/64) in ms (gray scale mode)
#define B 0x00000000 // black
#define W 0x00FFFFFF // white
@ -29,29 +31,50 @@
|((((c)-1)>>1)<<8) \
|((((c)-1)>>1)))
#define DIBPIXEL(d,p) *((DWORD*)(d)) = ((*((DWORD*)(d)) & dwGrayMask) << 1) | (p); *((LPBYTE*) &(d)) += 4
#define DIBPIXEL4(d,p) *((DWORD*)(d)) = ((*((DWORD*)(d)) & dwGrayMask) << 1) | (p); \
*((LPBYTE*) &(d)) += 4
BOOL bGrayscale = FALSE; // Default is to not emulate grayscale
BOOL bGrayscale = FALSE;
UINT nBackgroundX = 0;
UINT nBackgroundY = 0;
UINT nBackgroundW = 0;
UINT nBackgroundH = 0;
UINT nLcdX = 0;
UINT nLcdY = 0;
UINT nLcdZoom = 1;
UINT nLcdZoom = 1; // memory DC zoom
UINT nGdiXZoom = 1; // GDI x zoom
UINT nGdiYZoom = 1; // GDI y zoom
HDC hLcdDC = NULL;
HDC hMainDC = NULL;
HDC hAnnunDC = NULL; // annunciator DC
BYTE (*GetLineCounter)(VOID) = NULL;
VOID (*StartDisplay)(BYTE byInitial) = NULL;
VOID (*StopDisplay)(VOID) = NULL;
static BYTE GetLineCounterGray(VOID);
static BYTE GetLineCounterBW(VOID);
static VOID StartDisplayGray(BYTE byInitial);
static VOID StartDisplayBW(BYTE byInitial);
static VOID StopDisplayGray(VOID);
static VOID StopDisplayBW(VOID);
static LPBYTE pbyLcd;
static HBITMAP hLcdBitmap;
static HBITMAP hMainBitmap;
static HBITMAP hAnnunBitmap;
static DWORD Pattern[16];
static BYTE Buf[36];
static DWORD dwGrayMask;
static LARGE_INTEGER lLcdRef; // reference time for VBL counter
static UINT uLcdTimerId = 0;
static BYTE byVblRef = 0; // VBL stop reference
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,
@ -116,9 +139,6 @@ VOID UpdateContrast(BYTE byContrast)
// 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;
}
@ -128,22 +148,42 @@ VOID SetLcdColor(UINT nId, UINT nRed, UINT nGreen, UINT nBlue)
return;
}
VOID SetLcdMode(BOOL bMode)
{
if ((bGrayscale = bMode))
{
// set pixel update mask
dwGrayMask = GRAYMASK(NOCOLORSGRAY);
GetLineCounter = GetLineCounterGray;
StartDisplay = StartDisplayGray;
StopDisplay = StopDisplayGray;
}
else
{
// set pixel update mask
dwGrayMask = GRAYMASK(NOCOLORSBW);
GetLineCounter = GetLineCounterBW;
StartDisplay = StartDisplayBW;
StopDisplay = StopDisplayBW;
}
UpdateContrast(Chipset.contrast);
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
bmiLcd.Lcd_bmih.biHeight = -SCREENHEIGHT; // CdB for HP: add 64/80 line display for apples
_ASSERT(hLcdDC == NULL);
hLcdDC = CreateCompatibleDC(hWindowDC);
_ASSERT(hLcdDC != NULL);
hLcdBitmap = CreateDIBSection(hWindowDC,(BITMAPINFO*)&bmiLcd,DIB_RGB_COLORS,(VOID **)&pbyLcd,NULL,0);
VERIFY(hLcdDC = CreateCompatibleDC(hWindowDC));
VERIFY(hLcdBitmap = CreateDIBSection(hWindowDC,(BITMAPINFO*)&bmiLcd,DIB_RGB_COLORS,(VOID **)&pbyLcd,NULL,0));
hLcdBitmap = (HBITMAP) 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);
SetLcdMode(bGrayscale); // init display update function pointer
return;
}
@ -151,8 +191,12 @@ 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;
while (i < 32) dwKMLColor[i++] = B;
while (i < 64) dwKMLColor[i++] = I;
GetLineCounter = NULL;
StartDisplay = NULL;
StopDisplay = NULL;
if (hLcdDC != NULL)
{
@ -167,11 +211,8 @@ VOID DestroyLcdBitmap(VOID)
BOOL CreateMainBitmap(LPCTSTR szFilename)
{
HPALETTE hAssertPalette;
_ASSERT(hWindowDC != NULL);
hMainDC = CreateCompatibleDC(hWindowDC);
_ASSERT(hMainDC != NULL);
VERIFY(hMainDC = CreateCompatibleDC(hWindowDC));
if (hMainDC == NULL) return FALSE; // quit if failed
hMainBitmap = LoadBitmapFile(szFilename);
if (hMainBitmap == NULL)
@ -182,8 +223,7 @@ BOOL CreateMainBitmap(LPCTSTR szFilename)
}
hMainBitmap = (HBITMAP) SelectObject(hMainDC,hMainBitmap);
_ASSERT(hPalette != NULL);
hAssertPalette = SelectPalette(hMainDC,hPalette,FALSE);
_ASSERT(hAssertPalette != NULL);
VERIFY(SelectPalette(hMainDC,hPalette,FALSE));
RealizePalette(hMainDC);
return TRUE;
}
@ -201,6 +241,40 @@ VOID DestroyMainBitmap(VOID)
return;
}
//
// load annunciator bitmap
//
BOOL CreateAnnunBitmap(LPCTSTR szFilename)
{
_ASSERT(hWindowDC != NULL);
VERIFY(hAnnunDC = CreateCompatibleDC(hWindowDC));
if (hAnnunDC == NULL) return FALSE; // quit if failed
hAnnunBitmap = LoadBitmapFile(szFilename);
if (hAnnunBitmap == NULL)
{
DeleteDC(hAnnunDC);
hAnnunDC = NULL;
return FALSE;
}
hAnnunBitmap = (HBITMAP) SelectObject(hAnnunDC,hAnnunBitmap);
return TRUE;
}
//
// destroy annunciator bitmap
//
VOID DestroyAnnunBitmap(VOID)
{
if (hAnnunDC != NULL)
{
VERIFY(DeleteObject(SelectObject(hAnnunDC,hAnnunBitmap)));
DeleteDC(hAnnunDC);
hAnnunDC = NULL;
hAnnunBitmap = NULL;
}
return;
}
//****************
//*
//* LCD functions
@ -242,8 +316,8 @@ VOID UpdateDisplayPointers(VOID)
VOID UpdateMainDisplay(VOID)
{
UINT x, y;
BYTE *p = pbyLcd+(Chipset.d0size*LCD_ROW); // CdB for HP: add 64/80 line display for apples
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
{
@ -264,7 +338,7 @@ VOID UpdateMainDisplay(VOID)
Npeek(Buf,d,36);
for (x=0; x<36; ++x) // every 4 pixel
{
DIBPIXEL(p,Pattern[Buf[x]]);
DIBPIXEL4(p,Pattern[Buf[x]]);
// check for display buffer overflow
_ASSERT(p <= pbyLcd + LCD_ROW * SCREENHEIGHT);
}
@ -273,14 +347,9 @@ VOID UpdateMainDisplay(VOID)
}
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);
// CdB for HP: add 64/80 line 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);
@ -305,13 +374,13 @@ VOID UpdateMenuDisplay(VOID)
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
p = pbyLcd + ((Chipset.d0size+MAINSCREENHEIGHT)*LCD_ROW); // CdB for HP: add 64/80 line 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]]);
DIBPIXEL4(p,Pattern[Buf[x]]);
// check for display buffer overflow
_ASSERT(p <= pbyLcd + LCD_ROW * SCREENHEIGHT);
}
@ -321,7 +390,7 @@ VOID UpdateMenuDisplay(VOID)
}
EnterCriticalSection(&csGDILock); // solving NT GDI problems
{
// CdB for HP: add 64/80 ligne display for apples
// CdB for HP: add 64/80 line display for apples
StretchBlt(hWindowDC,
nLcdX, nLcdY+(MAINSCREENHEIGHT+Chipset.d0size)*nLcdZoom,
131*nLcdZoom, MENUHEIGHT*nLcdZoom,
@ -359,19 +428,15 @@ VOID RefreshDisp0()
memcpy(Buf,d,34); // 34 nibbles are viewed
for (x=0; x<36; ++x) // every 4 pixel
{
DIBPIXEL(p,Pattern[Buf[x]]);
DIBPIXEL4(p,Pattern[Buf[x]]);
}
d+=34;
}
EnterCriticalSection(&csGDILock); // solving NT GDI problems
{
StretchBlt(hWindowDC,
nLcdX, nLcdY,
131*nLcdZoom, Chipset.d0size*nLcdZoom,
hLcdDC,
Chipset.d0offset, 0,
131, Chipset.d0size,
SRCCOPY);
StretchBlt(hWindowDC, nLcdX, nLcdY,
131*nLcdZoom*nGdiXZoom, Chipset.d0size*nLcdZoom*nGdiYZoom,
hLcdDC, Chipset.d0offset, 0, 131, Chipset.d0size, SRCCOPY);
GdiFlush();
}
LeaveCriticalSection(&csGDILock);
@ -386,10 +451,7 @@ VOID WriteToMainDisplay(LPBYTE a, DWORD d, UINT s)
INT lWidth = abs(Chipset.width); // display width
if (bGrayscale)
{
return;
}
if (bGrayscale) return; // no direct writing in grayscale mode
#if defined DEBUG_DISPLAY
{
@ -399,7 +461,9 @@ VOID WriteToMainDisplay(LPBYTE a, DWORD d, UINT s)
}
#endif
if (!(Chipset.IORam[BITOFFSET]&DON)) return; // display off
if (!(Chipset.IORam[BITOFFSET]&DON)) // display off
return; // no drawing
if (MAINSCREENHEIGHT == 0) return; // menu disabled
d -= Chipset.start1; // nibble offset to DISPADDR (start of display)
@ -417,27 +481,25 @@ VOID WriteToMainDisplay(LPBYTE a, DWORD d, UINT s)
{
*p = Pattern[*a];
}
a++; // next value to write
x++; // next x position
++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
++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++;
p = (DWORD*) (pbyLcd+y*LCD_ROW); // CdB for HP: add 64/80 line display for apples
}
else
p++;
}
if (y==y0) y++;
EnterCriticalSection(&csGDILock); // solving NT GDI problems
{
StretchBlt(hWindowDC,
nLcdX, nLcdY+y0*nLcdZoom,
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
hLcdDC, Chipset.boffset, y0, 131, y-y0, SRCCOPY); // CdB for HP: add 64/80 line display for apples
GdiFlush();
}
LeaveCriticalSection(&csGDILock);
@ -450,10 +512,7 @@ VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s)
UINT y0, y;
DWORD *p;
if (bGrayscale)
{
return;
}
if (bGrayscale) return; // no direct writing in grayscale mode
#if defined DEBUG_DISPLAY
{
@ -463,12 +522,12 @@ VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s)
}
#endif
if (!(Chipset.IORam[BITOFFSET]&DON)) return; // display off
if (!(Chipset.IORam[BITOFFSET]&DON)) return;
if (MENUHEIGHT == 0) return; // menu disabled
d -= Chipset.start2; // nibble offset to DISPADDR (start of display)
d -= Chipset.start2;
y0 = y = (d / 34) + MAINSCREENHEIGHT+Chipset.d0size; // bitmap row
x0 = x = d % 34; // bitmap coloumn
x0 = x = d % 34;
p = (DWORD*)(pbyLcd + y0*LCD_ROW + x0*sizeof(*p));
// outside menu display area
@ -495,13 +554,9 @@ VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s)
if (y==y0) y++;
EnterCriticalSection(&csGDILock); // solving NT GDI problems
{
StretchBlt(hWindowDC,
nLcdX, nLcdY+y0*nLcdZoom,
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
hLcdDC, 0, y0, 131, y-y0, SRCCOPY); // CdB for HP: add 64/80 line display for apples
GdiFlush();
}
LeaveCriticalSection(&csGDILock);
@ -557,33 +612,25 @@ VOID ResizeWindow(VOID)
SWP_NOMOVE | SWP_NOZORDER);
}
_ASSERT(hWindowDC); // move destination window
SetWindowOrgEx(hWindowDC, nBackgroundX, nBackgroundY, NULL);
EnterCriticalSection(&csGDILock); // solving NT GDI problems
{
_ASSERT(hWindowDC); // move origin of destination window
VERIFY(SetWindowOrgEx(hWindowDC, nBackgroundX, nBackgroundY, NULL));
GdiFlush();
}
LeaveCriticalSection(&csGDILock);
InvalidateRect(hWnd,NULL,TRUE);
}
return;
}
//################
//#
//# functions for gray scale implementation
//#
//################
#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;
}
// main display update routine
static VOID CALLBACK LcdProc(UINT uEventId, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
EnterCriticalSection(&csLcdLock);
@ -605,17 +652,11 @@ static VOID CALLBACK LcdProc(UINT uEventId, UINT uMsg, DWORD dwUser, DWORD dw1,
}
// LCD line counter calculation
BYTE GetLineCounter(VOID)
static BYTE GetLineCounterGray(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];
@ -629,15 +670,8 @@ BYTE GetLineCounter(VOID)
return 0x3F - byTime; // update display between VBL counter 0x3F-0x3E
}
VOID StartDisplay(BYTE byInitial)
static VOID StartDisplayGray(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
@ -649,20 +683,16 @@ VOID StartDisplay(BYTE byInitial)
_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
VERIFY(uLcdTimerId = timeSetEvent(DISPLAY_FREQ,0,(LPTIMECALLBACK)&LcdProc,0,TIME_PERIODIC));
}
return;
}
VOID StopDisplay(VOID)
static VOID StopDisplayGray(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
@ -678,3 +708,39 @@ VOID StopDisplay(VOID)
LeaveCriticalSection(&csLcdLock);
return;
}
//################
//#
//# functions for black and white implementation
//#
//################
// LCD line counter calculation in BW mode
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 BYTE GetLineCounterBW(VOID) // get line counter value
{
_ASSERT(byVblRef < 0x40);
return (0x40 + F4096Hz() - byVblRef) & 0x3F;
}
static VOID StartDisplayBW(BYTE byInitial)
{
// get positive VBL difference between now and stop time
byVblRef = (0x40 + F4096Hz() - byInitial) & 0x3F;
return;
}
static VOID StopDisplayBW(VOID)
{
BYTE a[2];
ReadIO(a,LINECOUNT,2,TRUE); // update VBL at display off time
return;
}

View file

@ -13,7 +13,7 @@
#include "kml.h"
#include "debugger.h"
#define VERSION "1.56+"
#define VERSION "1.57+"
#ifdef _DEBUG
LPCTSTR szNoTitle = _T("Emu48 ")_T(VERSION)_T(" Debug");
@ -318,35 +318,6 @@ static BOOL IsFileWriteable(LPCTSTR szFilename)
return bWriteable;
}
// set listfield for sound device combo box
static VOID SetSoundDeviceList(HWND hWnd,UINT uDeviceID)
{
WAVEOUTCAPS woc;
UINT uSelectDevice,uDevID,uDevNo;
SendMessage(hWnd,CB_RESETCONTENT,0,0);
// preset selector
uSelectDevice = (UINT) SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) _T("Standard Audio"));
SendMessage(hWnd,CB_SETITEMDATA,uSelectDevice,WAVE_MAPPER);
uDevNo = waveOutGetNumDevs();
for (uDevID = 0; uDevID < uDevNo; ++uDevID)
{
if ( waveOutGetDevCaps(uDevID,&woc,sizeof(woc)) == MMSYSERR_NOERROR
&& (woc.dwFormats & WAVE_FORMAT_4M08) != 0)
{
// copy product name to combo box
LONG i = (LONG) SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) woc.szPname);
SendMessage(hWnd,CB_SETITEMDATA,i,uDevID);
if (uDevID == uDeviceID) uSelectDevice = i;
}
}
SendMessage(hWnd,CB_SETCURSEL,uSelectDevice,0L);
return;
}
// set listfield for serial combo boxes
static VOID SetCommList(HWND hDlg,LPCTSTR szWireSetting,LPCTSTR szIrSetting)
{
@ -394,7 +365,7 @@ static VOID SetCommList(HWND hDlg,LPCTSTR szWireSetting,LPCTSTR szIrSetting)
// test if COM port is valid
hComm = CreateFile(szBuffer,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
if(hComm != INVALID_HANDLE_VALUE)
if (hComm != INVALID_HANDLE_VALUE)
{
VERIFY(CloseHandle(hComm));
bAddWire = bAddIr = TRUE;
@ -472,8 +443,7 @@ static INT_PTR CALLBACK SettingsGeneralProc(HWND hDlg, UINT uMsg, WPARAM wParam,
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
SetLcdMode(!bGrayscale); // set new display mode
SwitchToState(nOldState);
}
@ -534,7 +504,7 @@ static INT_PTR CALLBACK SettingsMemoryProc(HWND hDlg, UINT uMsg, WPARAM wParam,
EnableWindow(GetDlgItem(hDlg,IDC_PORT1WR),FALSE);
}
}
else // HP38G/HP39G/HP40G/HP49G/HP39G+/HP48Gii/HP49G+ // CdB for HP: add apples
else // HP38G/HP39G/HP40G/HP49G
{
EnableWindow(GetDlgItem(hDlg,IDC_PORT1EN),FALSE);
EnableWindow(GetDlgItem(hDlg,IDC_PORT1WR),FALSE);
@ -716,7 +686,7 @@ static INT_PTR CALLBACK SettingsPeripheralProc(HWND hDlg, UINT uMsg, WPARAM wPar
EnableWindow(GetDlgItem(hDlg,IDC_IR),FALSE);
}
if (cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q') // HP49G/HP48GII/HP49G+ // CdB for HP: add Apples
if (cCurrentRomType=='X') // HP49G
{
SendDlgItemMessage(hDlg,IDC_IR,CB_RESETCONTENT,0,0);
EnableWindow(GetDlgItem(hDlg,IDC_IR),FALSE);
@ -751,8 +721,7 @@ static INT_PTR CALLBACK SettingsPeripheralProc(HWND hDlg, UINT uMsg, WPARAM wPar
ResetUdp(); // invalidate saved UDP address
// 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
if (cCurrentRomType!='X') // HP49G Ir port is not connected
GetDlgItemText(hDlg,IDC_IR,szSerialIr,ARRAYSIZEOF(szSerialIr));
return TRUE;
}
@ -912,20 +881,25 @@ static LRESULT OnPaint(HWND hWindow)
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
SetWindowOrgEx(hPaintDC, nBackgroundX, nBackgroundY, NULL);
// redraw header display area
StretchBlt(hPaintDC, nLcdX, nLcdY,
131*nLcdZoom, Chipset.d0size*nLcdZoom,
131*nLcdZoom*nGdiXZoom, Chipset.d0size*nLcdZoom*nGdiYZoom,
hLcdDC, Chipset.d0offset, 0, 131, Chipset.d0size, SRCCOPY);
// redraw main display area
StretchBlt(hPaintDC, nLcdX, nLcdY+Chipset.d0size*nLcdZoom,
131*nLcdZoom, nLines*nLcdZoom,
hLcdDC, Chipset.boffset, Chipset.d0size, 131, MAINSCREENHEIGHT, SRCCOPY);
StretchBlt(hPaintDC, nLcdX, nLcdY+Chipset.d0size*nLcdZoom*nGdiYZoom,
131*nLcdZoom*nGdiXZoom, nLines*nLcdZoom*nGdiYZoom,
hLcdDC, Chipset.boffset, Chipset.d0size,
131, MAINSCREENHEIGHT, SRCCOPY);
// redraw menu display area
StretchBlt(hPaintDC, nLcdX, nLcdY+(MAINSCREENHEIGHT+Chipset.d0size)*nLcdZoom,
131*nLcdZoom, MENUHEIGHT*nLcdZoom,
hLcdDC, 0, (MAINSCREENHEIGHT+Chipset.d0size), 131, MENUHEIGHT, SRCCOPY);
StretchBlt(hPaintDC, nLcdX, nLcdY+(MAINSCREENHEIGHT+Chipset.d0size)*nLcdZoom*nGdiYZoom,
131*nLcdZoom*nGdiXZoom, MENUHEIGHT*nLcdZoom*nGdiYZoom,
hLcdDC, 0, (MAINSCREENHEIGHT+Chipset.d0size),
131, MENUHEIGHT, SRCCOPY);
GdiFlush();
}
LeaveCriticalSection(&csGDILock);
@ -981,7 +955,7 @@ static LRESULT OnDropFiles(HDROP hFilesInfo)
{
TCHAR szFileName[MAX_PATH];
WORD wNumFiles,wIndex;
BOOL bSuccess;
BOOL bSuccess = FALSE;
// get number of files dropped
wNumFiles = DragQueryFile (hFilesInfo,(UINT)-1,NULL,0);
@ -1030,7 +1004,7 @@ static LRESULT OnDropFiles(HDROP hFilesInfo)
Sleep(dwWakeupDelay);
KeyboardEvent(FALSE,0,0x8000);
// wait for sleep mode
while(Chipset.Shutdn == FALSE) Sleep(0);
while (Chipset.Shutdn == FALSE) Sleep(0);
cancel:
bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control
@ -1491,7 +1465,7 @@ static LRESULT OnObjectLoad(VOID)
KeyboardEvent(FALSE,0,0x8000);
// wait for sleep mode
while(Chipset.Shutdn == FALSE) Sleep(0);
while (Chipset.Shutdn == FALSE) Sleep(0);
}
if (nState != SM_RUN)
@ -1546,7 +1520,7 @@ static LRESULT OnObjectLoad(VOID)
KeyboardEvent(TRUE,0,0x8000);
Sleep(dwWakeupDelay);
KeyboardEvent(FALSE,0,0x8000);
while(Chipset.Shutdn == FALSE) Sleep(0);
while (Chipset.Shutdn == FALSE) Sleep(0);
cancel:
bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control
@ -1737,15 +1711,21 @@ 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 !"));
AbortMessage(_T("Disassembler Dialog Box Creation Error!"));
if (pbyRom) SwitchToState(SM_RUN);
return 0;
}
static LRESULT OnTopics(VOID)
{
ShellExecute(hWnd,_T("open"),_T("Emu48.htm"),NULL,szEmuDirectory,SW_SHOWNORMAL);
return 0;
}
static LRESULT OnAbout(VOID)
{
if (DialogBox(hApp, MAKEINTRESOURCE(IDD_ABOUT), hWnd, (DLGPROC)About) == -1)
AbortMessage(_T("About Dialog Box Creation Error !"));
AbortMessage(_T("About Dialog Box Creation Error!"));
return 0;
}
@ -1979,6 +1959,7 @@ LRESULT CALLBACK MainWndProc(HWND hWindow, UINT uMsg, WPARAM wParam, LPARAM lPar
case ID_TOOL_MACRO_PLAY: return OnToolMacroPlay();
case ID_TOOL_MACRO_STOP: return OnToolMacroStop();
case ID_TOOL_MACRO_SETTINGS: return OnToolMacroSettings();
case ID_HELP_TOPICS: return OnTopics();
case ID_ABOUT: return OnAbout();
}
// check if command ID belongs to MRU file area
@ -2004,8 +1985,8 @@ LRESULT CALLBACK MainWndProc(HWND hWindow, UINT uMsg, WPARAM wParam, LPARAM lPar
case WM_LBUTTONUP: return OnLButtonUp((UINT) wParam, LOWORD(lParam), HIWORD(lParam));
case WM_MOUSEMOVE: return OnMouseMove((UINT) wParam, LOWORD(lParam), HIWORD(lParam));
case WM_NCMOUSEMOVE: return OnNcMouseMove((UINT) wParam, LOWORD(lParam), HIWORD(lParam));
case WM_KEYUP: return OnKeyUp((int)wParam, lParam);
case WM_KEYDOWN: return OnKeyDown((int)wParam, lParam);
case WM_KEYUP: return OnKeyUp((int) wParam, lParam);
case WM_KEYDOWN: return OnKeyDown((int) wParam, lParam);
#if !defined _USRDLL // not in DLL version
case WM_COPYDATA: return OnCopyData((PCOPYDATASTRUCT) lParam);
#endif
@ -2125,10 +2106,10 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC
rectWindow.top = 0;
rectWindow.right = 256;
rectWindow.bottom = 0;
AdjustWindowRect(&rectWindow, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, TRUE);
AdjustWindowRect(&rectWindow, STYLE_TITLE, TRUE);
hWnd = CreateWindow(MAKEINTATOM(classAtom),_T("Emu48"),
WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED,
STYLE_TITLE,
CW_USEDEFAULT, CW_USEDEFAULT,
rectWindow.right - rectWindow.left,
rectWindow.bottom - rectWindow.top,
@ -2242,7 +2223,7 @@ start:
while (GetMessage(&msg, NULL, 0, 0))
{
if( !TranslateAccelerator(hWnd, hAccel, &msg)
if ( !TranslateAccelerator(hWnd, hAccel, &msg)
&& (hDlgDebug == NULL || !IsDialogMessage(hDlgDebug, &msg))
&& (hDlgFind == NULL || !IsDialogMessage(hDlgFind, &msg))
&& (hDlgProfile == NULL || !IsDialogMessage(hDlgProfile, &msg))

View file

@ -4,7 +4,7 @@
# TARGTYPE "Win32 (x86) Application" 0x0101
CFG=Emu48 - Win32 DebugRegDebug4x
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
@ -13,7 +13,7 @@ CFG=Emu48 - Win32 DebugRegDebug4x
!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 NMAKE /f "Emu48.mak" CFG="Emu48 - Win32 Release"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
@ -50,8 +50,8 @@ RSC=rc.exe
# ADD CPP /nologo /Gr /MT /W3 /GX /O2 /Ob2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /D "REGISTRY" /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"
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
@ -76,8 +76,8 @@ LINK32=link.exe
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /D "REGISTRY" /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"
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
@ -100,11 +100,11 @@ LINK32=link.exe
# 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" /D "_UNICODE" /D "UNICODE" /Yu"pch.h" /FD /c
# ADD CPP /nologo /Gr /MT /W3 /GX /O2 /Ob2 /D "NDEBUG" /D "_UNICODE" /D "UNICODE" /D "WIN32" /D "_WINDOWS" /D "STRICT" /D "REGISTRY" /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"
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
@ -127,11 +127,12 @@ LINK32=link.exe
# 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" /D "_UNICODE" /D "UNICODE" /FR /Yu"pch.h" /FD /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "_UNICODE" /D "UNICODE" /D "WIN32" /D "_WINDOWS" /D "STRICT" /D "REGISTRY" /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"
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
# SUBTRACT RSC /x
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
@ -143,14 +144,14 @@ LINK32=link.exe
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "DebugRegDebug4x"
# PROP BASE Intermediate_Dir "DebugRegDebug4x"
# 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 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
@ -170,14 +171,14 @@ LINK32=link.exe
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "ReleaseRegDebug4x"
# PROP BASE Intermediate_Dir "ReleaseRegDebug4x"
# 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 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
@ -280,6 +281,10 @@ SOURCE=.\kml.c
# End Source File
# Begin Source File
SOURCE=.\lowbat.c
# End Source File
# Begin Source File
SOURCE=.\mops.c
# End Source File
# Begin Source File
@ -313,6 +318,10 @@ SOURCE=.\settings.c
# End Source File
# Begin Source File
SOURCE=.\sndenum.c
# End Source File
# Begin Source File
SOURCE=.\sound.c
# End Source File
# Begin Source File
@ -381,6 +390,10 @@ SOURCE=.\pch.h
# End Source File
# Begin Source File
SOURCE=.\snddef.h
# End Source File
# Begin Source File
SOURCE=.\types.h
# End Source File
# End Group
@ -400,5 +413,9 @@ SOURCE=.\DBGTOOL.BMP
SOURCE=.\Emu48.ico
# End Source File
# End Group
# Begin Source File
SOURCE=.\EMU48.XML
# End Source File
# End Target
# End Project

View file

@ -3,7 +3,7 @@ Microsoft Developer Studio Workspace File, Format Version 6.00
###############################################################################
Project: "Emu48"=".\Emu48.dsp" - Package Owner=<4>
Project: "Emu48"=.\Emu48.dsp - Package Owner=<4>
Package=<5>
{{{

View file

@ -61,7 +61,7 @@
// window styles
#define STYLE_TITLE (WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED)
#define STYLE_NOTITLE (WS_POPUP|WS_CLIPSIBLINGS)
#define STYLE_NOTITLE (WS_POPUP|WS_SYSMENU|WS_MINIMIZEBOX|WS_CLIPSIBLINGS)
// WM_COPYDATA identifier
#define CDID_FILENAME 1 // send file name
@ -154,6 +154,7 @@ extern VOID WriteSettingsInt(LPCTSTR lpszSection, LPCTSTR lpszEntry, INT nValue)
extern VOID DelSettingsKey(LPCTSTR lpszSection, LPCTSTR lpszEntry);
// Display.c
extern BOOL bGrayscale;
extern UINT nBackgroundX;
extern UINT nBackgroundY;
extern UINT nBackgroundW;
@ -161,14 +162,23 @@ extern UINT nBackgroundH;
extern UINT nLcdX;
extern UINT nLcdY;
extern UINT nLcdZoom;
extern UINT nGdiXZoom;
extern UINT nGdiYZoom;
extern HDC hLcdDC;
extern HDC hMainDC;
extern HDC hAnnunDC;
extern BYTE (*GetLineCounter)(VOID);
extern VOID (*StartDisplay)(BYTE byInitial);
extern VOID (*StopDisplay)(VOID);
extern VOID UpdateContrast(BYTE byContrast);
extern VOID SetLcdColor(UINT nId, UINT nRed, UINT nGreen, UINT nBlue);
extern VOID SetLcdMode(BOOL bMode);
extern VOID CreateLcdBitmap(VOID);
extern VOID DestroyLcdBitmap(VOID);
extern BOOL CreateMainBitmap(LPCTSTR szFilename);
extern VOID DestroyMainBitmap(VOID);
extern BOOL CreateAnnunBitmap(LPCTSTR szFilename);
extern VOID DestroyAnnunBitmap(VOID);
extern VOID UpdateDisplayPointers(VOID);
extern VOID UpdateMainDisplay(VOID);
extern VOID UpdateMenuDisplay(VOID);
@ -177,9 +187,6 @@ 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;
@ -191,7 +198,6 @@ extern BOOL bKeySlow;
extern BOOL bSoundSlow;
extern UINT nOpcSlow;
extern BOOL bCommInit;
extern BOOL bGrayscale;
extern CHIPSET Chipset;
extern TCHAR szSerialWire[16];
extern TCHAR szSerialIr[16];
@ -283,6 +289,8 @@ extern BOOL GetSaveObjectFilename(LPCTSTR lpstrFilter,LPCTSTR lpstrDefExt);
extern WORD WriteStack(UINT nStkLevel,LPBYTE lpBuf,DWORD dwSize);
extern BOOL LoadObject(LPCTSTR szFilename);
extern BOOL SaveObject(LPCTSTR szFilename);
extern BOOL LoadIconFromFile(LPCTSTR szFilename);
extern VOID LoadIconDefault(VOID);
extern HBITMAP LoadBitmapFile(LPCTSTR szFilename);
extern HRGN CreateRgnFromBitmap(HBITMAP hBmp,COLORREF color,DWORD dwTol);
@ -320,6 +328,12 @@ 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);
// Lowbat.c
extern BOOL bLowBatDisable;
extern VOID StartBatMeasure(VOID);
extern VOID StopBatMeasure(VOID);
extern VOID GetBatteryState(BOOL *pbLBI, BOOL *pbVLBI);
// Keyboard.c
extern DWORD dwKeyMinDelay;
extern VOID ScanKeyboard(BOOL bActive, BOOL bReset);
@ -364,6 +378,9 @@ extern VOID RPL_Push(UINT l,DWORD n);
extern VOID External(CHIPSET* w);
extern VOID RCKBp(CHIPSET* w);
// SndEnum.c
extern VOID SetSoundDeviceList(HWND hWnd,UINT uDeviceID);
// Sound.c
extern DWORD dwWaveVol;
extern DWORD dwWaveTime;

View file

@ -106,7 +106,6 @@ BEGIN
VERTGUIDE, 14
VERTGUIDE, 147
VERTGUIDE, 154
VERTGUIDE, 161
VERTGUIDE, 237
TOPMARGIN, 7
BOTTOMMARGIN, 127
@ -115,7 +114,7 @@ BEGIN
IDD_CHOOSEKML, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 188
RIGHTMARGIN, 208
TOPMARGIN, 7
BOTTOMMARGIN, 59
END
@ -296,7 +295,7 @@ FONT 8, "MS Sans Serif"
BEGIN
ICON IDI_EMU48,IDC_STATIC,7,6,20,20,SS_REALSIZEIMAGE
LTEXT "",IDC_VERSION,29,6,151,8,NOT WS_GROUP
LTEXT "Copyright © 2015 Christoph Gießelink && Sébastien Carlier",
LTEXT "Copyright © 2017 Christoph Gießelink && Sébastien Carlier",
IDC_STATIC,29,18,181,8
DEFPUSHBUTTON "OK",IDOK,215,12,39,14
EDITTEXT IDC_LICENSE,7,33,247,112,ES_MULTILINE | ES_AUTOHSCROLL |
@ -384,18 +383,18 @@ BEGIN
GROUPBOX "Serial Ports",IDC_STATIC,161,64,83,50
END
IDD_CHOOSEKML DIALOG DISCARDABLE 0, 0, 195, 66
IDD_CHOOSEKML DIALOG DISCARDABLE 0, 0, 215, 66
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Choose Your KML Script"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "OK",IDOK,138,7,50,14
PUSHBUTTON "Cancel",IDCANCEL,138,27,50,14
COMBOBOX IDC_KMLSCRIPT,7,47,181,120,CBS_DROPDOWNLIST |
DEFPUSHBUTTON "OK",IDOK,158,7,50,14
PUSHBUTTON "Cancel",IDCANCEL,158,27,50,14
COMBOBOX IDC_KMLSCRIPT,7,47,201,120,CBS_DROPDOWNLIST |
CBS_OEMCONVERT | CBS_SORT | WS_VSCROLL | WS_TABSTOP
EDITTEXT IDC_EMUDIR,7,17,106,14,ES_AUTOHSCROLL
PUSHBUTTON "...",IDC_EMUDIRSEL,113,17,10,14
PUSHBUTTON "V",IDC_UPDATE,123,17,10,14
EDITTEXT IDC_EMUDIR,7,17,126,14,ES_AUTOHSCROLL
PUSHBUTTON "...",IDC_EMUDIRSEL,133,17,10,14
PUSHBUTTON "V",IDC_UPDATE,143,17,10,14
LTEXT "Emu48 Directory :",IDC_STATIC,7,7,115,8
LTEXT "Current KML Script :",IDC_STATIC,7,37,115,8
END
@ -411,9 +410,9 @@ BEGIN
IDC_ALWAYSDISPLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
7,146,139,14
GROUPBOX "",IDC_STATIC,7,7,287,36
CTEXT "Title of the Script",IDC_TITLE,71,14,158,8
CTEXT "by",IDC_STATIC,71,22,158,8
CTEXT "The Author",IDC_AUTHOR,71,30,158,8,NOT WS_GROUP
CTEXT "Title of the Script",IDC_TITLE,7,14,287,8
CTEXT "by",IDC_STATIC,7,22,287,8
CTEXT "The Author",IDC_AUTHOR,7,30,287,8,NOT WS_GROUP
EDITTEXT IDC_KMLLOG,7,48,287,92,ES_MULTILINE | ES_AUTOHSCROLL |
ES_OEMCONVERT | ES_READONLY | WS_VSCROLL | NOT
WS_TABSTOP
@ -692,8 +691,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,5,6,0
PRODUCTVERSION 1,5,6,0
FILEVERSION 1,5,7,0
PRODUCTVERSION 1,5,7,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -709,13 +708,13 @@ BEGIN
BLOCK "04090000"
BEGIN
VALUE "CompanyName", "Christoph Gießelink & Sebastien Carlier\0"
VALUE "FileDescription", "HP38/39/40/48/49 Emulator\0"
VALUE "FileVersion", "1, 5, 6, 0\0"
VALUE "FileDescription", "HP38/39/40/48/49/50 Emulator\0"
VALUE "FileVersion", "1, 5, 7, 0\0"
VALUE "InternalName", "Emu48\0"
VALUE "LegalCopyright", "Copyright © 2015\0"
VALUE "LegalCopyright", "Copyright © 2017\0"
VALUE "OriginalFilename", "Emu48.exe\0"
VALUE "ProductName", "Emu48\0"
VALUE "ProductVersion", "1, 5, 6, 0\0"
VALUE "ProductVersion", "1, 5, 7, 0\0"
END
END
BLOCK "VarFileInfo"
@ -797,6 +796,8 @@ BEGIN
END
POPUP "&Help"
BEGIN
MENUITEM "&Help Topics", ID_HELP_TOPICS
MENUITEM SEPARATOR
MENUITEM "&About Emu48...", ID_ABOUT
END
END
@ -862,6 +863,8 @@ BEGIN
END
POPUP "&Help"
BEGIN
MENUITEM "&Help Topics", ID_HELP_TOPICS
MENUITEM SEPARATOR
MENUITEM "&About Emu48...", ID_ABOUT
END
END

View file

@ -120,6 +120,10 @@ SOURCE=.\display.c
# End Source File
# Begin Source File
SOURCE=.\disrpl.c
# End Source File
# Begin Source File
SOURCE=.\Emu48.c
# End Source File
# Begin Source File
@ -164,6 +168,10 @@ SOURCE=.\kml.c
# End Source File
# Begin Source File
SOURCE=.\lowbat.c
# End Source File
# Begin Source File
SOURCE=.\mops.c
# End Source File
# Begin Source File
@ -197,6 +205,10 @@ SOURCE=.\settings.c
# End Source File
# Begin Source File
SOURCE=.\sndenum.c
# End Source File
# Begin Source File
SOURCE=.\sound.c
# End Source File
# Begin Source File
@ -229,6 +241,10 @@ SOURCE=.\debugger.h
# End Source File
# Begin Source File
SOURCE=.\disrpl.h
# End Source File
# Begin Source File
SOURCE=.\Emu48.h
# End Source File
# Begin Source File
@ -261,6 +277,10 @@ SOURCE=.\pch.h
# End Source File
# Begin Source File
SOURCE=.\snddef.h
# End Source File
# Begin Source File
SOURCE=.\types.h
# End Source File
# End Group
@ -269,6 +289,14 @@ SOURCE=.\types.h
# 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

View file

@ -31,8 +31,8 @@ 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
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
@ -571,9 +571,10 @@ loop:
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));
StartBatMeasure(); // start battery measurement
StartTimers();
}
PCHANGED;
while (!bInterrupt)
@ -631,6 +632,7 @@ loop:
_ASSERT(nNextState != SM_RUN);
StopDisplay(); // stop display counter/update
StopBatMeasure(); // stop battery measurement
StopTimers();
while (nNextState == SM_SLEEP) // go into sleep state

View file

@ -4,14 +4,13 @@
* This file is part of Emu48
*
* Copyright (C) 1995 Sebastien Carlier
* Copyright (C) 2005 Christoph Gießelink
*
*/
#include "pch.h"
#include "Emu48.h"
#include "ops.h"
#define SAMPLES_PER_SEC 44100 // sound sampling rate
//| 38G | 39G | 40G | 48SX | 48GX | 49G | Name
//#F0E4F #80F0F #80F0F #706D2 #80850 #80F0F =SFLAG53_56

View file

@ -225,7 +225,7 @@ static TREENODE *nodePatch = NULL;
static BOOL PatchNibble(DWORD dwAddress, BYTE byPatch)
{
TREENODE *p;
PTREENODE p;
_ASSERT(pbyRom); // ROM defined
if ((p = (PTREENODE) malloc(sizeof(TREENODE))) == NULL)
@ -830,7 +830,7 @@ BOOL NewDocument(VOID)
// 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
if (Chipset.type == 'X' || Chipset.type == '2' || Chipset.type == 'Q') // HP49G/HP48gII/HP49g+/50g // CdB for HP: add apples
{
Chipset.Port0Size = 256;
Chipset.Port1Size = 128;
@ -841,11 +841,11 @@ BOOL NewDocument(VOID)
FlashInit(); // init flash structure
}
if (Chipset.type == 'Q') // HP49G+ // CdB for HP: add apples
if (Chipset.type == 'Q') // HP49g+/50g // CdB for HP: add apples
{
Chipset.d0size = 16;
}
Chipset.IORam[LPE] = RST; // set ReSeT bit at power on reset
// allocate port memory
@ -861,7 +861,7 @@ BOOL NewDocument(VOID)
}
if (Chipset.Port2Size)
{
Port2 = (LPBYTE) calloc(Chipset.Port2Size*2048,sizeof(*Port2));
Port2 = (LPBYTE) calloc(Chipset.Port2Size*2048,sizeof(*Port1));
_ASSERT(Port2 != NULL);
}
LoadBreakpointList(NULL); // clear debugger breakpoint list
@ -873,14 +873,13 @@ restore:
ResetBackup();
// HP48SX/GX
if(Chipset.type == 'S' || Chipset.type == 'G')
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;
@ -1060,7 +1059,7 @@ BOOL OpenDocument(LPCTSTR szFilename)
}
// HP48SX/GX
if(cCurrentRomType=='S' || cCurrentRomType=='G')
if (cCurrentRomType=='S' || cCurrentRomType=='G')
{
MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]);
// port2 changed and card detection enabled
@ -1128,7 +1127,7 @@ restore:
ResetBackup();
// HP48SX/GX
if(cCurrentRomType=='S' || cCurrentRomType=='G')
if (cCurrentRomType=='S' || cCurrentRomType=='G')
{
// use 2nd command line argument if defined
MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]);
@ -1400,12 +1399,12 @@ BOOL GetSaveAsFilename(VOID)
ofn.lpstrDefExt = _T("e38");
ofn.nFilterIndex = 1;
}
if (cCurrentRomType=='E' || cCurrentRomType=='P') // HP39/40G/hp439G+ // CdB for HP: add apples
if (cCurrentRomType=='E' || cCurrentRomType=='P') // HP39/40 // 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
if (cCurrentRomType=='X' || cCurrentRomType=='2' || cCurrentRomType=='Q') // HP49G/HP48gII/HP49g+/HP50g // CdB for HP: add apples
{
ofn.lpstrDefExt = _T("e49");
ofn.nFilterIndex = 4;
@ -1446,7 +1445,7 @@ BOOL GetSaveObjectFilename(LPCTSTR lpstrFilter,LPCTSTR lpstrDefExt)
InitializeOFN(&ofn);
ofn.lpstrFilter = lpstrFilter;
ofn.lpstrDefExt = lpstrDefExt;
ofn.lpstrDefExt = NULL;
ofn.nFilterIndex = 1;
ofn.lpstrFile = szBuffer;
ofn.lpstrFile[0] = 0;
@ -1455,6 +1454,19 @@ BOOL GetSaveObjectFilename(LPCTSTR lpstrFilter,LPCTSTR lpstrDefExt)
if (GetSaveFileName(&ofn) == FALSE) return FALSE;
_ASSERT(ARRAYSIZEOF(szBufferFilename) == ofn.nMaxFile);
lstrcpy(szBufferFilename, ofn.lpstrFile);
if (ofn.nFileExtension == 0) // given filename has no extension
{
// actual name length
UINT nLength = lstrlen(szBufferFilename);
// destination buffer has room for the default extension
if (nLength + 1 + lstrlen(lpstrDefExt) < ARRAYSIZEOF(szBufferFilename))
{
// add default extension
szBufferFilename[nLength++] = _T('.');
lstrcpy(&szBufferFilename[nLength], lpstrDefExt);
}
}
return TRUE;
}
@ -1595,6 +1607,39 @@ BOOL SaveObject(LPCTSTR szFilename) // separated stack reading part
//################
//#
//# Load Icon
//#
//################
BOOL LoadIconFromFile(LPCTSTR szFilename)
{
HANDLE hIcon;
SetCurrentDirectory(szEmuDirectory);
// not necessary to destroy because icon is shared
hIcon = LoadImage(NULL, szFilename, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE|LR_LOADFROMFILE|LR_SHARED);
SetCurrentDirectory(szCurrentDirectory);
if (hIcon)
{
SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIcon);
SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon);
}
return hIcon != NULL;
}
VOID LoadIconDefault(VOID)
{
// use window class icon
SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) NULL);
SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) NULL);
return;
}
//################
//#
//# Load Bitmap
@ -2325,7 +2370,7 @@ HBITMAP LoadBitmapFile(LPCTSTR szFilename)
if ( Bmp.dwFileSize >= 6
&& (memcmp(Bmp.pbyFile,"GIF87a",6) == 0 || memcmp(Bmp.pbyFile,"GIF89a",6) == 0))
{
hBitmap = DecodeGif(&Bmp,NULL);
hBitmap = DecodeGif(&Bmp,&dwTColor);
break;
}

View file

@ -76,15 +76,18 @@ static CONST KmlToken pLexToken[] =
{TOK_VIRTUAL, 000000, 7,_T("Virtual")},
{TOK_INCLUDE, 000002, 7,_T("Include")},
{TOK_NOTFLAG, 000001, 7,_T("NotFlag")},
{TOK_MENUBAR, 000001, 7,_T("Menubar")}, // for PPC compatibility reasons
{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_ZOOMXY, 000011, 6,_T("Zoomxy")},
{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_LOCALE, 000001, 6,_T("Locale")},
{TOK_TOPBAR, 000001, 6,_T("Topbar")}, // for PPC compatibility reasons
{TOK_TITLE, 000002, 5,_T("Title")},
{TOK_OUTIN, 000011, 5,_T("OutIn")},
{TOK_PATCH, 000002, 5,_T("Patch")},
@ -95,14 +98,17 @@ static CONST KmlToken pLexToken[] =
{TOK_CLASS, 000001, 5,_T("Class")},
{TOK_PRESS, 000001, 5,_T("Press")},
{TOK_IFMEM, 000111, 5,_T("IfMem")},
{TOK_SCALE, 000011, 5,_T("Scale")},
{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_ICON, 000002, 4,_T("Icon")},
{TOK_MAP, 000011, 3,_T("Map")},
{TOK_ROM, 000002, 3,_T("Rom")},
{TOK_VGA, 000001, 3,_T("Vga")}, // for PPC compatibility reasons
{TOK_LCD, 000000, 3,_T("Lcd")},
{TOK_END, 000000, 3,_T("End")},
{TOK_NONE, 000000, 0,_T("")}
@ -134,6 +140,9 @@ static UINT uButtonClicked = 0;
static BOOL bKeyPressed = FALSE; // no key pressed
static UINT uLastKeyPressed = 0; // var for last pressed key
static INT nScaleMul = 0; // no scaling
static INT nScaleDiv = 0;
//################
//#
//# Compilation Result
@ -1239,6 +1248,14 @@ static VOID InitGlobal(KmlBlock* pBlock)
nCurrentClass = (UINT) pLine->nParam[0];
PrintfToLog(_T("Calculator Class : %u"), nCurrentClass);
break;
case TOK_ICON:
if (!LoadIconFromFile((LPTSTR) pLine->nParam[0]))
{
PrintfToLog(_T("Cannot load Icon %s."), (LPTSTR)pLine->nParam[0]);
break;
}
PrintfToLog(_T("Icon %s loaded."), (LPTSTR)pLine->nParam[0]);
break;
case TOK_DEBUG:
bDebug = (BOOL) pLine->nParam[0]&1;
PrintfToLog(_T("Debug %s"), bDebug?_T("On"):_T("Off"));
@ -1287,6 +1304,10 @@ static VOID InitGlobal(KmlBlock* pBlock)
dwTColorTol = (DWORD) pLine->nParam[0];
dwTColor = RGB((BYTE) pLine->nParam[1],(BYTE) pLine->nParam[2],(BYTE) pLine->nParam[3]);
break;
case TOK_SCALE:
nScaleMul = (INT) pLine->nParam[0];
nScaleDiv = (INT) pLine->nParam[1];
break;
default:
PrintfToLog(_T("Command %s Ignored in Block %s"), GetStringOf(pLine->eCommand), GetStringOf(pBlock->eType));
}
@ -1332,13 +1353,45 @@ static KmlLine* InitLcd(KmlBlock* pBlock)
nLcdY = (UINT) pLine->nParam[1];
break;
case TOK_ZOOM:
if ((nLcdZoom = (UINT) pLine->nParam[0]) == 0)
nLcdZoom = 1;
if ((nGdiXZoom = (UINT) pLine->nParam[0]) == 0)
nGdiXZoom = 1; // default zoom
// search for memory DC zoom (1-4)
for (nLcdZoom = 4; (nGdiXZoom % nLcdZoom) != 0; --nLcdZoom) { };
_ASSERT(nLcdZoom > 0); // because (nGdiXZoom % 1) == 0
nGdiXZoom /= nLcdZoom; // remainder is GDI zoom
nGdiYZoom = nGdiXZoom;
break;
case TOK_ZOOMXY:
if ((nGdiXZoom = (UINT) pLine->nParam[0]) == 0)
nGdiXZoom = 1; // default zoom
if ((nGdiYZoom = (UINT) pLine->nParam[1]) == 0)
nGdiYZoom = 1; // default zoom
// search for memory DC zoom (1-4)
for (nLcdZoom = 4; ((nGdiXZoom % nLcdZoom) | (nGdiYZoom % nLcdZoom)) != 0 ; --nLcdZoom) { };
_ASSERT(nLcdZoom > 0); // because (nGdiYZoom % 1) == 0 && (nGdiYZoom % 1) == 0
nGdiXZoom /= nLcdZoom; // remainder is GDI zoom
nGdiYZoom /= nLcdZoom;
break;
case TOK_COLOR:
SetLcdColor((UINT) pLine->nParam[0],(UINT) pLine->nParam[1],
(UINT) pLine->nParam[2],(UINT) pLine->nParam[3]);
break;
case TOK_BITMAP:
if (hAnnunDC != NULL)
{
PrintfToLog(_T("Bitmap %s ignored."), (LPCTSTR)pLine->nParam[0]);
AddToLog(_T("Please put only one Bitmap command in the Lcd block."));
break;
}
if (!CreateAnnunBitmap((LPCTSTR)pLine->nParam[0]))
{
PrintfToLog(_T("Cannot load Annunciator Bitmap %s."), (LPCTSTR)pLine->nParam[0]);
break;
}
PrintfToLog(_T("Annunciator Bitmap %s loaded."), (LPCTSTR)pLine->nParam[0]);
break;
case TOK_END:
return pLine;
default:
@ -1628,6 +1681,7 @@ VOID KillKML(VOID)
}
UnmapRom();
DestroyLcdBitmap();
DestroyAnnunBitmap();
DestroyMainBitmap();
if (hPalette)
{
@ -1649,6 +1703,7 @@ VOID KillKML(VOID)
}
hRgn = NULL;
}
LoadIconDefault();
bClicking = FALSE;
uButtonClicked = 0;
FreeBlocks(pKml);
@ -1670,8 +1725,12 @@ VOID KillKML(VOID)
nBackgroundW = 256;
nBackgroundH = 0;
nLcdZoom = 1;
nGdiXZoom = 1;
nGdiYZoom = 1;
dwTColor = (DWORD) -1;
dwTColorTol = 0;
nScaleMul = 0;
nScaleDiv = 0;
cCurrentRomType = 0;
nCurrentClass = 0;
ResizeWindow();
@ -2065,17 +2124,22 @@ VOID RefreshButtons(RECT *rc)
VOID DrawAnnunciator(UINT nId, BOOL bOn)
{
HDC hDC;
UINT nSx,nSy;
--nId; // zero based ID
if (nId >= ARRAYSIZEOF(pAnnunciator)) return;
if (bOn)
{
hDC = hAnnunDC != NULL ? hAnnunDC : hMainDC;
nSx = pAnnunciator[nId].nDx; // position of annunciator
nSy = pAnnunciator[nId].nDy;
}
else
{
hDC = hMainDC;
nSx = pAnnunciator[nId].nOx; // position of background
nSy = pAnnunciator[nId].nOy;
}
@ -2084,7 +2148,7 @@ VOID DrawAnnunciator(UINT nId, BOOL bOn)
BitBlt(hWindowDC,
pAnnunciator[nId].nOx, pAnnunciator[nId].nOy,
pAnnunciator[nId].nCx, pAnnunciator[nId].nCy,
hMainDC,
hDC,
nSx, nSy,
SRCCOPY);
GdiFlush();
@ -2280,6 +2344,105 @@ VOID PlayKey(UINT nOut, UINT nIn, BOOL bPressed)
//#
//################
static VOID ResizeMainBitmap(INT nMul, INT nDiv)
{
if (nMul * nDiv > 0) // resize main picture
{
BITMAP Bitmap;
int nMode;
INT nWidth,nHeight;
UINT i;
// update graphic
nBackgroundX = MulDiv(nBackgroundX,nMul,nDiv);
nBackgroundY = MulDiv(nBackgroundY,nMul,nDiv);
nBackgroundW = MulDiv(nBackgroundW,nMul,nDiv);
nBackgroundH = MulDiv(nBackgroundH,nMul,nDiv);
nLcdX = MulDiv(nLcdX,nMul,nDiv);
nLcdY = MulDiv(nLcdY,nMul,nDiv);
nGdiXZoom = MulDiv(nGdiXZoom * nLcdZoom,nMul,nDiv);
nGdiYZoom = MulDiv(nGdiYZoom * nLcdZoom,nMul,nDiv);
// search for memory DC zoom (1-4)
for (nLcdZoom = 4; ((nGdiXZoom % nLcdZoom) | (nGdiYZoom % nLcdZoom)) != 0 ; --nLcdZoom) { };
_ASSERT(nLcdZoom > 0); // because (nGdiYZoom % 1) == 0 && (nGdiYZoom % 1) == 0
nGdiXZoom /= nLcdZoom; // remainder is GDI zoom
nGdiYZoom /= nLcdZoom;
// update script coordinates (buttons)
for (i = 0; i < nButtons; ++i)
{
pButton[i].nOx = (UINT) (pButton[i].nOx * nMul / nDiv);
pButton[i].nOy = (UINT) (pButton[i].nOy * nMul / nDiv);
pButton[i].nDx = (UINT) (pButton[i].nDx * nMul / nDiv);
pButton[i].nDy = (UINT) (pButton[i].nDy * nMul / nDiv);
pButton[i].nCx = (UINT) (pButton[i].nCx * nMul / nDiv);
pButton[i].nCy = (UINT) (pButton[i].nCy * nMul / nDiv);
}
// update script coordinates (annunciators)
for (i = 0; i < ARRAYSIZEOF(pAnnunciator); ++i)
{
// translate offset
pAnnunciator[i].nOx = (UINT) (pAnnunciator[i].nOx * nMul / nDiv);
pAnnunciator[i].nOy = (UINT) (pAnnunciator[i].nOy * nMul / nDiv);
if (hAnnunDC == NULL) // no external annunciator bitmap
{
// translate down (with rounding)
pAnnunciator[i].nDx = (UINT) MulDiv(pAnnunciator[i].nDx,nMul,nDiv);
pAnnunciator[i].nDy = (UINT) MulDiv(pAnnunciator[i].nDy,nMul,nDiv);
// translate size (with rounding)
pAnnunciator[i].nCx = (UINT) MulDiv(pAnnunciator[i].nCx,nMul,nDiv);
pAnnunciator[i].nCy = (UINT) MulDiv(pAnnunciator[i].nCy,nMul,nDiv);
}
}
EnterCriticalSection(&csGDILock); // solving NT GDI problems
{
// get bitmap size
GetObject(GetCurrentObject(hMainDC,OBJ_BITMAP),sizeof(Bitmap),&Bitmap);
// resulting bitmap size
nWidth = MulDiv(Bitmap.bmWidth,nMul,nDiv);
nHeight = MulDiv(Bitmap.bmHeight,nMul,nDiv);
VERIFY(nMode = SetStretchBltMode(hMainDC,HALFTONE));
if (nMul <= nDiv) // shrinking bitmap
{
VERIFY(StretchBlt(
hMainDC,0,0,nWidth,nHeight,
hMainDC,0,0,Bitmap.bmWidth,Bitmap.bmHeight,
SRCCOPY));
}
else // expanding bitmap
{
// bitmap with new size
HDC hMemDC = CreateCompatibleDC(hMainDC);
HBITMAP hMainBitMap = CreateCompatibleBitmap(hMainDC,nWidth,nHeight);
HBITMAP hMemBitMap = (HBITMAP) SelectObject(hMemDC,SelectObject(hMainDC,hMainBitMap));
VERIFY(StretchBlt(
hMainDC,0,0,nWidth,nHeight,
hMemDC,0,0,Bitmap.bmWidth,Bitmap.bmHeight,
SRCCOPY));
// delete original bitmap
VERIFY(DeleteObject(SelectObject(hMemDC,hMemBitMap)));
DeleteDC(hMemDC);
}
VERIFY(SetStretchBltMode(hMainDC,nMode));
GdiFlush();
}
LeaveCriticalSection(&csGDILock);
ResizeWindow();
}
return;
}
static KmlBlock* LoadKMLGlobal(LPCTSTR szFilename)
{
HANDLE hFile;
@ -2418,6 +2581,7 @@ BOOL InitKML(LPCTSTR szFilename, BOOL bNoLog)
goto quit;
}
ResizeMainBitmap(nScaleMul,nScaleDiv); // resize main picture
CreateLcdBitmap();
PrintfToLog(_T("%i Buttons Defined"), nButtons);
@ -2430,7 +2594,7 @@ BOOL InitKML(LPCTSTR szFilename, BOOL bNoLog)
quit:
if (bOk)
{
// HP38G/HP39G(+)/HP40G have no object loading, ignore // CdB for HP: add apples
// HP38G/HP39G(+|s)/HP40G(s) have no object loading, ignore // CdB for HP: add apples
DragAcceptFiles(hWnd,cCurrentRomType != '6' && cCurrentRomType != 'A' && cCurrentRomType != 'E' && cCurrentRomType != 'P');
if (!bNoLog)

View file

@ -32,35 +32,39 @@ typedef enum eTokenId
TOK_GLOBAL, //16
TOK_AUTHOR, //17
TOK_BITMAP, //18
TOK_OFFSET, //19
TOK_BUTTON, //20
TOK_IFFLAG, //21
TOK_ONDOWN, //22
TOK_NOHOLD, //23
TOK_LOCALE, //24
TOK_TOPBAR, //25
TOK_TITLE, //26
TOK_OUTIN, //27
TOK_PATCH, //28
TOK_PRINT, //29
TOK_DEBUG, //30
TOK_COLOR, //31
TOK_MODEL, //32
TOK_CLASS, //33
TOK_PRESS, //34
TOK_IFMEM, //35
TOK_TYPE, //36
TOK_SIZE, //37
TOK_DOWN, //38
TOK_ZOOM, //39
TOK_ELSE, //40
TOK_ONUP, //41
TOK_EOL, //42
TOK_MAP, //43
TOK_ROM, //44
TOK_VGA, //45
TOK_LCD, //46
TOK_END //47
TOK_ZOOMXY, //19
TOK_OFFSET, //20
TOK_BUTTON, //21
TOK_IFFLAG, //22
TOK_ONDOWN, //23
TOK_NOHOLD, //24
TOK_LOCALE, //25
TOK_TOPBAR, //26
TOK_MENUBAR, //27
TOK_TITLE, //28
TOK_OUTIN, //29
TOK_PATCH, //30
TOK_PRINT, //31
TOK_DEBUG, //32
TOK_COLOR, //33
TOK_MODEL, //34
TOK_CLASS, //35
TOK_PRESS, //36
TOK_IFMEM, //37
TOK_SCALE, //38
TOK_TYPE, //39
TOK_SIZE, //40
TOK_DOWN, //41
TOK_ZOOM, //42
TOK_ELSE, //43
TOK_ONUP, //44
TOK_ICON, //45
TOK_EOL, //46
TOK_MAP, //47
TOK_ROM, //48
TOK_VGA, //49
TOK_LCD, //50
TOK_END //51
} TokenId;
#define TYPE_NONE 00

122
source/LOWBAT.C Normal file
View file

@ -0,0 +1,122 @@
/*
* lowbat.c
*
* This file is part of Emu48
*
* Copyright (C) 2006 Christoph Gießelink
*
*/
#include "pch.h"
#include "Emu48.h"
#include "io.h" // I/O definitions
// #define BAT_SIMULATION // switch low bat simulation
#define BAT_FREQ (60*1000) // bat update time in ms (real machine = 60us, HP28C = 60s)
BOOL bLowBatDisable = FALSE;
static HANDLE hCThreadBat = NULL;
static HANDLE hEventBat;
static DWORD WINAPI LowBatThread(LPVOID pParam)
{
BOOL bLBI,bVLBI;
do
{
GetBatteryState(&bLBI,&bVLBI); // get battery state
// very low bat detection
bVLBI = bVLBI && (Chipset.IORam[LPE] & EVLBI) != 0;
IOBit(LPD,VLBI,bVLBI); // set VLBI
IOBit(SRQ1,VSRQ,bVLBI); // and service bit
if (bVLBI) // VLBI detected
{
Chipset.SoftInt = TRUE;
bInterrupt = TRUE;
if (Chipset.Shutdn) // CPU shut down
{
Chipset.bShutdnWake = TRUE; // wake up from SHUTDN mode
SetEvent(hEventShutdn); // wake up emulation thread
}
}
}
while (WaitForSingleObject(hEventBat,BAT_FREQ) == WAIT_TIMEOUT);
return 0;
UNREFERENCED_PARAMETER(pParam);
}
VOID StartBatMeasure(VOID)
{
DWORD dwThreadId;
if (hCThreadBat) // Bat measuring thread running
return; // -> quit
// event to cancel Bat refresh loop
hEventBat = CreateEvent(NULL,FALSE,FALSE,NULL);
VERIFY(hCThreadBat = CreateThread(NULL,0,&LowBatThread,NULL,0,&dwThreadId));
return;
}
VOID StopBatMeasure(VOID)
{
if (hCThreadBat == NULL) // thread stopped
return; // -> quit
SetEvent(hEventBat); // leave Bat update thread
WaitForSingleObject(hCThreadBat,INFINITE);
CloseHandle(hCThreadBat);
hCThreadBat = NULL; // set flag Bat update stopped
CloseHandle(hEventBat); // close Bat event
return;
}
VOID GetBatteryState(BOOL *pbLBI, BOOL *pbVLBI)
{
#if defined BAT_SIMULATION
switch (GetPrivateProfileInt(_T("LowBat"),_T("Level"),2,_T(".\\Lowbat.ini")))
{
case 0: // empty
*pbLBI = TRUE;
*pbVLBI = TRUE;
break;
case 1: // low
*pbLBI = TRUE;
*pbVLBI = FALSE;
break;
default: // full
*pbLBI = FALSE;
*pbVLBI = FALSE;
break;
}
#else
SYSTEM_POWER_STATUS sSps;
*pbLBI = FALSE; // no battery warning
*pbVLBI = FALSE;
VERIFY(GetSystemPowerStatus(&sSps));
// low bat emulation enabled and battery powered
if (!bLowBatDisable && sSps.ACLineStatus == AC_LINE_OFFLINE)
{
// on critical battery state make sure that lowbat flag is also set
if ((sSps.BatteryFlag & BATTERY_FLAG_CRITICAL) != 0)
sSps.BatteryFlag |= BATTERY_FLAG_LOW;
// low bat detection
*pbLBI = ((sSps.BatteryFlag & BATTERY_FLAG_LOW) != 0);
// very low bat detection
*pbVLBI = ((sSps.BatteryFlag & BATTERY_FLAG_CRITICAL) != 0);
}
#endif
return;
}

View file

@ -326,7 +326,7 @@ static VOID MapROM(BYTE a, BYTE b)
UINT i;
DWORD p, m;
// HP39(+)/40G, HP49G(+) HP48Gii // CdB for HP: add apples memory
// HP39(+)/40G, HP49G/g+ HP48gII // CdB for HP: add apples memory
if (cCurrentRomType == 'E' || cCurrentRomType == 'X' || cCurrentRomType == 'P' || cCurrentRomType == '2' || cCurrentRomType == 'Q')
{
if (bFlashRomArray) // view flash ROM data
@ -397,7 +397,7 @@ VOID Map(BYTE a, BYTE b) // maps 2KB pages with priority
}
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
// @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]);
@ -760,8 +760,9 @@ VOID Npeek(BYTE *a, DWORD d, UINT s)
}
}
}
if (s-=c) {a+=c; d=(d+c)&0xFFFFF;}
} while (s);
a+=c;
d=(d+c)&0xFFFFF;
} while (s-=c);
return;
}
@ -850,8 +851,9 @@ VOID Nread(BYTE *a, DWORD d, UINT s)
for (u=0; u<c; u++) // update CRC
UpCRC(a[u]);
}
if (s-=c) {a+=c; d=(d+c)&0xFFFFF;}
} while (s);
a+=c;
d=(d+c)&0xFFFFF;
} while (s-=c);
return;
}
@ -941,8 +943,7 @@ VOID Nwrite(BYTE *a, DWORD d, UINT s)
if ((p=WMap[u]) != NULL) memcpy(p+v, a, c);
}
}
if (!bGrayscale)
UpdateDisplay(d, c); // update display
if (!bGrayscale) UpdateDisplay(d, c); // update display
a+=c;
d=(d+c)&0xFFFFF;
} while (s-=c);
@ -1020,8 +1021,8 @@ static DWORD ReadT2Acc(VOID)
VOID ReadIO(BYTE *a, DWORD d, DWORD s, BOOL bUpdate)
{
BOOL bNINT;
BOOL bNINT2;
BOOL bNINT,bNINT2;
BOOL bLBI,bVLBI;
BYTE c = 0xFF; // LINECOUNT not initialized
BOOL rbr_acc = FALSE; // flag to receive data
@ -1046,7 +1047,22 @@ VOID ReadIO(BYTE *a, DWORD d, DWORD s, BOOL bUpdate)
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 0x08: // LPD
// LB2 and LB1 not emulated, must be 0
_ASSERT((Chipset.IORam[LPD] & (LB2 | LB1)) == 0);
GetBatteryState(&bLBI,&bVLBI); // get battery state
// check if battery states enabled
bLBI = bLBI && ((Chipset.IORam[LPE] & ELBI) != 0);
bVLBI = bVLBI && ((Chipset.IORam[LPE] & EVLBI) != 0);
// set IO bits
IOBit(LPD,LB0,bLBI);
IOBit(LPD,VLBI,bVLBI);
IOBit(SRQ1,VSRQ,bVLBI);
*a = Chipset.IORam[d];
break;
case 0x09: // LPE
*a = Chipset.IORam[d];
if (bUpdate)
@ -1073,6 +1089,7 @@ VOID ReadIO(BYTE *a, DWORD d, DWORD s, BOOL bUpdate)
{
*a = Chipset.IORam[d] & 0x7;
#if defined DEBUG_SERIAL // return BAUD value
if (bUpdate)
{
TCHAR buffer[256];
wsprintf(buffer,_T("%.5lx: BAUD Read: %x\n"),Chipset.pc,*a);
@ -1099,14 +1116,15 @@ VOID ReadIO(BYTE *a, DWORD d, DWORD s, BOOL bUpdate)
}
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
// on a HP30/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
if (bUpdate)
{
TCHAR buffer[256];
wsprintf(buffer,_T("%.5lx: IOC Read: %x\n"),Chipset.pc,*a);
@ -1117,6 +1135,7 @@ VOID ReadIO(BYTE *a, DWORD d, DWORD s, BOOL bUpdate)
case 0x11: // RCS
*a = Chipset.IORam[d] | RX; // return RCS value
#if defined DEBUG_SERIAL
if (bUpdate)
{
TCHAR buffer[256];
wsprintf(buffer,_T("%.5lx: RCS Read: %x\n"),Chipset.pc,*a);
@ -1127,6 +1146,7 @@ VOID ReadIO(BYTE *a, DWORD d, DWORD s, BOOL bUpdate)
case 0x12: // TCS
*a = Chipset.IORam[d]; // return TCS value
#if defined DEBUG_SERIAL
if (bUpdate)
{
TCHAR buffer[256];
wsprintf(buffer,_T("%.5lx: TCS Read: %x\n"),Chipset.pc,*a);
@ -1179,6 +1199,7 @@ VOID ReadIO(BYTE *a, DWORD d, DWORD s, BOOL bUpdate)
case 0x18: // SREQ? LSB
*a = Chipset.IORam[d]; // return SREQ value
#if defined DEBUG_SERIAL
if (bUpdate)
{
TCHAR buffer[256];
wsprintf(buffer,_T("%.5lx: SEQ %s Read: %x\n"),Chipset.pc,(d==0x18) ? "LSB" : "MSB",*a);
@ -1421,8 +1442,7 @@ VOID WriteIO(BYTE *a, DWORD d, DWORD s)
}
else // Clarke / Yorke chip
{
// bit 3 is read-only
Chipset.IORam[d] = (Chipset.IORam[d]&8)|(c&7);
Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); // bit 3 is read-only
}
CommSetBaud(); // set baudrate
#if defined DEBUG_SERIAL

View file

@ -12,7 +12,6 @@
#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;}}
@ -2445,7 +2444,7 @@ VOID o_goyes5(LPBYTE I)
}
//////// EXTENSIONS ////////
VOID o81B1(LPBYTE I)
VOID o81B1(LPBYTE I) // beep patch
{
if (cCurrentRomType=='Q' || cCurrentRomType=='2' || cCurrentRomType=='P')
{

View file

@ -180,6 +180,7 @@
#define ID_OBJECT_LOAD 40009
#define ID_OBJECT_SAVE 40010
#define ID_ABOUT 40011
#define ID_HELP_TOPICS 40012
#define ID_FILE_CLOSE 40013
#define ID_BACKUP_SAVE 40014
#define ID_BACKUP_RESTORE 40015
@ -247,6 +248,6 @@
#define _APS_NEXT_RESOURCE_VALUE 130
#define _APS_NEXT_COMMAND_VALUE 40072
#define _APS_NEXT_CONTROL_VALUE 1136
#define _APS_NEXT_SYMED_VALUE 108
#define _APS_NEXT_SYMED_VALUE 109
#endif
#endif

View file

@ -68,7 +68,7 @@
#define DOROMP 0x02E92 // XLIB Name
#define SEMI 0x0312B // ;
#define GARBAGECOL 0x0613E // =GARBAGECOL entry for HP48S/G/GII and HP49G(+)
#define GARBAGECOL 0x0613E // =GARBAGECOL entry for HP48S/G and HP49G
// check for Metakernel version
#define METAKERNEL Metakernel()
@ -105,7 +105,7 @@ static DWORD RPL_GarbageCol(VOID) // RPL variables must be in system RAM
CHIPSET OrgChipset;
DWORD dwAVMEM;
// only for HP48SX, HP48GX, HP49G, HP48GII and HP49G+
// only for HP48SX, HP48GX, HP49G, HP48gII and HP49g+
_ASSERT( cCurrentRomType == 'S' || cCurrentRomType == 'G' || cCurrentRomType == 'X'
|| cCurrentRomType == '2' || cCurrentRomType == 'Q');
@ -226,7 +226,7 @@ DWORD RPL_SkipOb(DWORD d)
break;
case DOLNGREAL: // Precision Real (HP49G)
l = 5;
if (cCurrentRomType=='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') // CdB for HP: add apples
if (cCurrentRomType=='X')
{
l += Read5(d+l);
l += Read5(d+l);
@ -234,7 +234,7 @@ DWORD RPL_SkipOb(DWORD d)
break;
case DOLNGCMP: // Precision Complex (HP49G)
l = 5;
if (cCurrentRomType=='X' && cCurrentRomType!='2' && cCurrentRomType!='Q') // CdB for HP: add apples
if (cCurrentRomType=='X')
{
l += Read5(d+l);
l += Read5(d+l);
@ -401,7 +401,7 @@ DWORD RPL_CreateTemp(DWORD l,BOOL bGarbageCol)
Nwrite(p,a+l,b-a);
free(p);
Write5(a+l-5,l); // set object length field
return a+1; // return base address of new object
return (a+1); // return base address of new object
}
UINT RPL_Depth(VOID)

View file

@ -120,7 +120,7 @@ BOOL CommOpen(LPTSTR strWirePort,LPTSTR strIrPort)
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if(hComm != INVALID_HANDLE_VALUE)
if (hComm != INVALID_HANDLE_VALUE)
{
DWORD dwThreadId;
@ -353,13 +353,13 @@ VOID CommReceive(VOID)
// 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)
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
if (dwBytesRead == 0L) // receive buffer empty
break;
#if defined DEBUG_SERIAL
@ -379,10 +379,10 @@ VOID CommReceive(VOID)
--dwBytesRead;
Chipset.IORam[RCS] |= RBF; // receive buffer full
if(UpdateUSRQ()) // update USRQ bit
if (UpdateUSRQ()) // update USRQ bit
INTERRUPT;
}
while(0);
while (FALSE);
LeaveCriticalSection(&csRecvLock);
return;
}

View file

@ -199,6 +199,8 @@ VOID ReadSettings(VOID)
uWaveDevId = ReadInt(_T("Emulator"),_T("WaveDeviceId"),uWaveDevId);
dwWaveVol = ReadInt(_T("Emulator"),_T("WaveVolume"),dwWaveVol);
dwWaveTime = ReadInt(_T("Emulator"),_T("WaveTime"),dwWaveTime);
// LowBat
bLowBatDisable = ReadInt(_T("LowBat"),_T("Disable"),bLowBatDisable);
// Macro
bMacroRealSpeed = ReadInt(_T("Macro"),_T("RealSpeed"),bMacroRealSpeed);
nMacroTimeout = ReadInt(_T("Macro"),_T("ReplayTimeout"),nMacroTimeout);
@ -252,6 +254,8 @@ VOID WriteSettings(VOID)
WriteInt(_T("Emulator"),_T("WaveDeviceId"),uWaveDevId);
WriteInt(_T("Emulator"),_T("WaveVolume"),dwWaveVol);
WriteInt(_T("Emulator"),_T("WaveTime"),dwWaveTime);
// LowBat
WriteInt(_T("LowBat"),_T("Disable"),bLowBatDisable);
// Macro
WriteInt(_T("Macro"),_T("RealSpeed"),bMacroRealSpeed);
WriteInt(_T("Macro"),_T("ReplayTimeout"),nMacroTimeout);

102
source/SNDDEF.H Normal file
View file

@ -0,0 +1,102 @@
/*
* snddef.h
*
* This file is part of Emu48
*
* Copyright (C) 2015 Christoph Gießelink
*
*/
#include <initguid.h>
#if _MSC_VER >= 1600 // valid for VS2010 and later
#include <DSound.h>
#include <Dsconf.h>
#else // create the necessary definitions manually
//
// IKsPropertySet
//
#ifndef _IKsPropertySet_
#define _IKsPropertySet_
#ifdef __cplusplus
struct IKsPropertySet;
#endif // __cplusplus
typedef struct IKsPropertySet *LPKSPROPERTYSET;
DEFINE_GUID(IID_IKsPropertySet, 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93);
#undef INTERFACE
#define INTERFACE IKsPropertySet
DECLARE_INTERFACE_(IKsPropertySet, IUnknown)
{
// IUnknown methods
STDMETHOD(QueryInterface) (THIS_ REFIID, LPVOID*) PURE;
STDMETHOD_(ULONG,AddRef) (THIS) PURE;
STDMETHOD_(ULONG,Release) (THIS) PURE;
// IKsPropertySet methods
STDMETHOD(Get) (THIS_ REFGUID rguidPropSet, ULONG ulId, LPVOID pInstanceData, ULONG ulInstanceLength,
LPVOID pPropertyData, ULONG ulDataLength, PULONG pulBytesReturned) PURE;
STDMETHOD(Set) (THIS_ REFGUID rguidPropSet, ULONG ulId, LPVOID pInstanceData, ULONG ulInstanceLength,
LPVOID pPropertyData, ULONG ulDataLength) PURE;
STDMETHOD(QuerySupport) (THIS_ REFGUID rguidPropSet, ULONG ulId, PULONG pulTypeSupport) PURE;
};
#endif // _IKsPropertySet_
// DirectSound Configuration Component GUID {11AB3EC0-25EC-11d1-A4D8-00C04FC28ACA}
DEFINE_GUID(CLSID_DirectSoundPrivate, 0x11ab3ec0, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x0, 0xc0, 0x4f, 0xc2, 0x8a, 0xca);
// DirectSound Device Properties {84624F82-25EC-11d1-A4D8-00C04FC28ACA}
DEFINE_GUID(DSPROPSETID_DirectSoundDevice, 0x84624f82, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x0, 0xc0, 0x4f, 0xc2, 0x8a, 0xca);
typedef enum
{
DSPROPERTY_DIRECTSOUNDDEVICE_WAVEDEVICEMAPPING_A = 1,
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_1 = 2,
DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_1 = 3,
DSPROPERTY_DIRECTSOUNDDEVICE_WAVEDEVICEMAPPING_W = 4,
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_A = 5,
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W = 6,
DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_A = 7,
DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W = 8,
} DSPROPERTY_DIRECTSOUNDDEVICE;
#ifdef UNICODE
#define DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W
#else // UNICODE
#define DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_A
#endif // UNICODE
typedef enum
{
DIRECTSOUNDDEVICE_TYPE_EMULATED,
DIRECTSOUNDDEVICE_TYPE_VXD,
DIRECTSOUNDDEVICE_TYPE_WDM
} DIRECTSOUNDDEVICE_TYPE;
typedef enum
{
DIRECTSOUNDDEVICE_DATAFLOW_RENDER,
DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE
} DIRECTSOUNDDEVICE_DATAFLOW;
typedef struct _DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA
{
DIRECTSOUNDDEVICE_TYPE Type; // Device type
DIRECTSOUNDDEVICE_DATAFLOW DataFlow; // Device dataflow
GUID DeviceId; // DirectSound device id
LPTSTR Description; // Device description
LPTSTR Module; // Device driver module
LPTSTR Interface; // Device interface
ULONG WaveDeviceId; // Wave device id
} DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA, *PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA;
#endif

256
source/SNDENUM.C Normal file
View file

@ -0,0 +1,256 @@
/*
* SndEnum.c
*
* This file is part of Emu48
*
* Copyright (C) 2015 Christoph Gießelink
*
*/
#include "pch.h"
#include "Emu48.h"
#include "snddef.h"
typedef HRESULT (WINAPI *LPFNDLLGETCLASSOBJECT)(REFCLSID,REFIID,LPVOID *);
static LPFNDLLGETCLASSOBJECT pfnDllGetClassObject = NULL;
//
// create a IKsPropertySet interface
//
static __inline HRESULT DirectSoundPrivateCreate(LPKSPROPERTYSET *ppKsPropertySet)
{
LPCLASSFACTORY pClassFactory = NULL;
HRESULT hr;
// create a class factory object
#if defined __cplusplus
hr = pfnDllGetClassObject(CLSID_DirectSoundPrivate,IID_IClassFactory,(LPVOID *) &pClassFactory);
#else
hr = pfnDllGetClassObject(&CLSID_DirectSoundPrivate,&IID_IClassFactory,(LPVOID *) &pClassFactory);
#endif
// create the DirectSoundPrivate object and query for an IKsPropertySet interface
if (SUCCEEDED(hr))
{
#if defined __cplusplus
hr = pClassFactory->CreateInstance(NULL,IID_IKsPropertySet,(LPVOID *) ppKsPropertySet);
#else
hr = pClassFactory->lpVtbl->CreateInstance(pClassFactory,NULL,&IID_IKsPropertySet,(LPVOID *) ppKsPropertySet);
#endif
}
if (pClassFactory) // release the class factory object
{
#if defined __cplusplus
pClassFactory->Release();
#else
pClassFactory->lpVtbl->Release(pClassFactory);
#endif
}
if (FAILED(hr) && *ppKsPropertySet) // handle failure
{
#if defined __cplusplus
(*ppKsPropertySet)->Release();
#else
(*ppKsPropertySet)->lpVtbl->Release(*ppKsPropertySet);
#endif
}
return hr;
}
//
// get the device information about a DirectSound GUID.
//
static BOOL GetInfoFromDSoundGUID(CONST GUID *lpGUID, UINT *puWaveDeviceID)
{
LPKSPROPERTYSET pKsPropertySet = NULL;
HRESULT hr;
BOOL bSuccess = FALSE;
hr = DirectSoundPrivateCreate(&pKsPropertySet);
if (SUCCEEDED(hr))
{
ULONG ulBytesReturned = 0;
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA sDirectSoundDeviceDescription;
ZeroMemory(&sDirectSoundDeviceDescription,sizeof(sDirectSoundDeviceDescription));
sDirectSoundDeviceDescription.DeviceId = *lpGUID;
// get the size of the direct sound device description
#if defined __cplusplus
hr = pKsPropertySet->Get(
DSPROPSETID_DirectSoundDevice,
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION,
NULL,
0,
&sDirectSoundDeviceDescription,
sizeof(sDirectSoundDeviceDescription),
&ulBytesReturned
);
#else
hr = pKsPropertySet->lpVtbl->Get(pKsPropertySet,
&DSPROPSETID_DirectSoundDevice,
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION,
NULL,
0,
&sDirectSoundDeviceDescription,
sizeof(sDirectSoundDeviceDescription),
&ulBytesReturned
);
#endif
if (SUCCEEDED(hr) && ulBytesReturned)
{
PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA psDirectSoundDeviceDescription = NULL;
// fetch the direct sound device description
psDirectSoundDeviceDescription = (PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_DATA) malloc(ulBytesReturned);
if (psDirectSoundDeviceDescription != NULL)
{
// init structure with data from length request
*psDirectSoundDeviceDescription = sDirectSoundDeviceDescription;
#if defined __cplusplus
hr = pKsPropertySet->Get(
DSPROPSETID_DirectSoundDevice,
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION,
NULL,
0,
psDirectSoundDeviceDescription,
ulBytesReturned,
&ulBytesReturned
);
#else
hr = pKsPropertySet->lpVtbl->Get(pKsPropertySet,
&DSPROPSETID_DirectSoundDevice,
DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION,
NULL,
0,
psDirectSoundDeviceDescription,
ulBytesReturned,
&ulBytesReturned
);
#endif
if ((bSuccess = SUCCEEDED(hr)))
{
// the requested device ID
*puWaveDeviceID = psDirectSoundDeviceDescription->WaveDeviceId;
}
free(psDirectSoundDeviceDescription);
}
}
#if defined __cplusplus
pKsPropertySet->Release();
#else
pKsPropertySet->lpVtbl->Release(pKsPropertySet);
#endif
}
return bSuccess;
}
//
// callback function for DirectSoundEnumerate()
//
static BOOL CALLBACK DSEnumProc(LPGUID lpGUID,LPCTSTR lpszDesc,LPCTSTR lpszDrvName,LPVOID lpContext)
{
HWND hWnd = (HWND) lpContext; // window handle of the combo box
if (lpGUID != NULL) // NULL only for "Primary Sound Driver"
{
UINT uDevID;
if (GetInfoFromDSoundGUID(lpGUID,&uDevID))
{
WAVEOUTCAPS woc;
// has device the necessary capabilities?
if ( waveOutGetDevCaps(uDevID,&woc,sizeof(woc)) == MMSYSERR_NOERROR
&& (woc.dwFormats & WAVE_FORMAT_4M08) != 0)
{
// copy product name and wave device ID to combo box
LONG i = (LONG) SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) lpszDesc);
SendMessage(hWnd,CB_SETITEMDATA,i,uDevID);
}
}
}
return TRUE;
UNREFERENCED_PARAMETER(lpszDrvName);
}
// set listfield for sound device combo box
VOID SetSoundDeviceList(HWND hWnd,UINT uDeviceID)
{
typedef BOOL (CALLBACK *LPDSENUMCALLBACK)(LPGUID, LPCTSTR, LPCTSTR, LPVOID);
typedef HRESULT (WINAPI *LPFN_SDE)(LPDSENUMCALLBACK lpDSEnumCallback,LPVOID lpContext);
LPFN_SDE pfnDirectSoundEnumerate = NULL;
UINT uSelectDevice,uDevID,uDevNo;
HMODULE hDSound = LoadLibrary(_T("dsound.dll"));
if (hDSound != NULL) // direct sound dll found
{
#if defined _UNICODE
pfnDirectSoundEnumerate = (LPFN_SDE) GetProcAddress(hDSound,"DirectSoundEnumerateW");
#else
pfnDirectSoundEnumerate = (LPFN_SDE) GetProcAddress(hDSound,"DirectSoundEnumerateA");
#endif
pfnDllGetClassObject = (LPFNDLLGETCLASSOBJECT) GetProcAddress(hDSound,"DllGetClassObject");
}
SendMessage(hWnd,CB_RESETCONTENT,0,0);
// preset selector
uSelectDevice = (UINT) SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) _T("Standard Audio"));
SendMessage(hWnd,CB_SETITEMDATA,uSelectDevice,WAVE_MAPPER);
// check for direct sound interface functions
if (pfnDirectSoundEnumerate != NULL && pfnDllGetClassObject != NULL)
{
// copy product name and wave device ID to combo box
if (SUCCEEDED(pfnDirectSoundEnumerate((LPDSENUMCALLBACK) DSEnumProc,hWnd)))
{
UINT i;
uDevNo = (UINT) SendMessage(hWnd,CB_GETCOUNT,0,0);
for (i = 0; i < uDevNo; ++i)
{
// translate device ID to combo box position
uDevID = (UINT) SendMessage(hWnd,CB_GETITEMDATA,i,0);
if (uDevID == uDeviceID) uSelectDevice = i;
}
}
}
else // direct sound not available, detect over wave capabilities
{
WAVEOUTCAPS woc;
uDevNo = waveOutGetNumDevs();
for (uDevID = 0; uDevID < uDevNo; ++uDevID)
{
if ( waveOutGetDevCaps(uDevID,&woc,sizeof(woc)) == MMSYSERR_NOERROR
&& (woc.dwFormats & WAVE_FORMAT_4M08) != 0)
{
// copy product name and wave device ID to combo box
LONG i = (LONG) SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM) woc.szPname);
SendMessage(hWnd,CB_SETITEMDATA,i,uDevID);
if (uDevID == uDeviceID) uSelectDevice = i;
}
}
}
// activate last selected combo box item
SendMessage(hWnd,CB_SETCURSEL,uSelectDevice,0L);
if (hDSound != NULL) // direct sound dll loaded
{
pfnDllGetClassObject = NULL;
VERIFY(FreeLibrary(hDSound));
}
return;
}

View file

@ -7,7 +7,6 @@
*
*/
#include "pch.h"
#include "resource.h"
#include "Emu48.h"
#include "io.h"
@ -296,7 +295,7 @@ static __inline INT SetBcd(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,
byNum[j] = byNum[j + i];
}
if(byNum[1] == 0) // number is 0
if (byNum[1] == 0) // number is 0
{
ZeroMemory(pbyNum,nMantLen + nExpLen + 1);
return nMantLen + nExpLen + 1;
@ -326,7 +325,7 @@ static __inline INT SetBcd(LPCTSTR cp,INT nMantLen,INT nExpLen,CONST TCHAR cDec,
while (*cp >= _T('0') && *cp <= _T('9'))
lExp = lExp * 10 + *cp++ - _T('0');
if(i) lExp = -lExp;
if (i) lExp = -lExp;
}
if (*cp != 0) return 0;
@ -805,7 +804,7 @@ LRESULT OnStackPaste(VOID) // paste data to stack
}
}
}
while(FALSE);
while (FALSE);
GlobalUnlock(hClipObj);
}
@ -825,7 +824,7 @@ LRESULT OnStackPaste(VOID) // paste data to stack
KeyboardEvent(FALSE,0,0x8000);
// wait for sleep mode
while(Chipset.Shutdn == FALSE) Sleep(0);
while (Chipset.Shutdn == FALSE) Sleep(0);
cancel:
bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control

View file

@ -16,6 +16,7 @@
#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
@ -106,7 +107,7 @@ typedef struct
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;
} Chipset_t;
// additional Saturnator registers and Apple hardware
typedef struct

View file

@ -1,340 +0,0 @@
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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.
<signature of Ty Coon>, 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.