From 5eac6ac58d0d862aec35ca314042306ace8b5f97 Mon Sep 17 00:00:00 2001 From: Gwenhael Le Moine Date: Tue, 19 Mar 2024 22:37:54 +0100 Subject: [PATCH] 2002-03-24: Updated to version 1.30 --- BEEP.EXT | 1 + CARDCOPY.EXE | Bin 32768 -> 32768 bytes DEBUGGER.TXT | 145 ++++-- EMU48.EXE | Bin 233472 -> 241664 bytes EMU48.TXT | 102 ++-- Emu48asc/EMU48ASC.BIN | Bin 147 -> 165 bytes Emu48asc/EMU48ASC.SRC | 14 +- Emu48asc/EMU48ASC.TXT | 2 +- Emu48asc/EMU49ASC.BIN | Bin 147 -> 165 bytes Emu48asc/EMU49ASC.S | 50 ++ Emu48asc/EMU49ASC.SRC | 60 --- JEMAC.KML | 4 +- MkShared.exe | Bin 28672 -> 28672 bytes PROBLEMS.TXT | 22 +- sources/COPYING.TXT | 340 ------------- sources/Cardcopy/CARDCOPY.C | 24 +- sources/EMU48.TXT | 449 ----------------- sources/Emu48/CHANGES.TXT | 330 ++++++++++++ sources/Emu48/DDESERV.C | 3 +- sources/Emu48/DEBUGGER.C | 530 ++++++++++++++++---- sources/Emu48/DEBUGGER.H | 10 +- sources/Emu48/DISPLAY.C | 402 +++++++++++---- sources/Emu48/EMU48.C | 218 ++++---- sources/Emu48/EMU48.DSP | 79 ++- sources/Emu48/EMU48.H | 30 +- sources/Emu48/EMU48.RC | 190 ++++--- sources/Emu48/EMU48.XML | 22 + sources/Emu48/ENGINE.C | 146 ++++-- sources/Emu48/FILES.C | 121 +++-- sources/Emu48/I28F160.C | 145 +++--- sources/Emu48/KEYBOARD.C | 2 +- sources/Emu48/KML.C | 247 ++++----- sources/Emu48/KML.H | 34 +- sources/Emu48/MOPS.C | 111 ++-- sources/Emu48/OPCODES.C | 36 +- sources/Emu48/OPCODES.H | 4 +- sources/Emu48/PCH.H | 3 + sources/Emu48/RESOURCE.H | 59 ++- sources/Emu48/SERIAL.C | 210 ++++++-- sources/Emu48/TIMER.C | 43 +- sources/Emu48/TYPES.H | 2 +- sources/GCCPatch/EMU48GCC.RC | 105 +++- sources/GCCPatch/Makefile | 4 +- sources/GCCPatch/PCH.H | 25 +- sources/GCCPatch/{ReadMe.txt => README.TXT} | 18 +- sources/MkShared/MKSHARED.C | 172 +++++++ sources/MkShared/MKSHARED.DSP | 114 +++++ sources/MkShared/MKSHARED.DSW | 29 ++ sources/MkShared/MKSHARED.ICO | Bin 0 -> 1318 bytes sources/MkShared/MKSHARED.RC | 180 +++++++ sources/MkShared/Main.c | 65 --- sources/MkShared/MkShared.mak | 186 ------- sources/MkShared/MkShared.mdp | Bin 34816 -> 0 bytes sources/MkShared/RESOURCE.H | 31 ++ 54 files changed, 2992 insertions(+), 2127 deletions(-) create mode 100644 Emu48asc/EMU49ASC.S delete mode 100644 Emu48asc/EMU49ASC.SRC delete mode 100644 sources/COPYING.TXT delete mode 100644 sources/EMU48.TXT create mode 100644 sources/Emu48/EMU48.XML rename sources/GCCPatch/{ReadMe.txt => README.TXT} (63%) create mode 100644 sources/MkShared/MKSHARED.C create mode 100644 sources/MkShared/MKSHARED.DSP create mode 100644 sources/MkShared/MKSHARED.DSW create mode 100644 sources/MkShared/MKSHARED.ICO create mode 100644 sources/MkShared/MKSHARED.RC delete mode 100644 sources/MkShared/Main.c delete mode 100644 sources/MkShared/MkShared.mak delete mode 100644 sources/MkShared/MkShared.mdp create mode 100644 sources/MkShared/RESOURCE.H diff --git a/BEEP.EXT b/BEEP.EXT index 7a499cb..5f2b210 100644 --- a/BEEP.EXT +++ b/BEEP.EXT @@ -1 +1,2 @@ +;01477:6300; disable 10 min auto off (internal, undocumented) 017A6:81B1; =makebeep diff --git a/CARDCOPY.EXE b/CARDCOPY.EXE index b6517bcf7c210521187faa9269d363986708419d..541c28009d608390d5317430d5b9387cf0b38aba 100644 GIT binary patch delta 1764 zcmdUv{ZCU@7{||3V5z|50{1EiIMgb&16N=oTFgo;$V=4%(sminDlod*L}qT6y4lDq zY+}X8j)}ODNivo&Wt;A1#vc}fqeGYZgUi$yW2SqFVS)==j3LqO?t7^(`xka^KKFa> zIp6b~^PF?;?dZ^Tbm$I;>sILAyBT`SoxaPVIK02};SZrXP*2~*5Y4v#6v~I*>AMno z0b2ihvsdfG9$k(K#_SZwc7FbxrT4O&aHNGXBQMu&j`uKjws%n$A0;!%%Aa&$Zj;+% zWsz^UZV5-lvBOWV@RZmP$;m)tqtg}HV=(%dcDvLcBL(rO?XhW9jvl)q+b7hy)GJ|l zgp{r!%wfdbK5GzpR_gP61G^kB=a0mrMe}DkHT!DWU<|k;n+<}Ge5@XEpGN$TqeOGm z`sPKO;KJZ;Fe^z)9LbxqOo} zOh)CQB*zPb7$nljQk9#uLPr0Df#)H+ltpC4Vk8_v`vFB83}>@egJl`4E` zk&f(qJZ~vg@u|FxWaAd&Cef;;Ha6K1p3w#DmIr}kRjT428P`%7zhT@#O}uLPC&c(4 z%iSrPHS0`RRpt3ERbsr+R6-7Z(6o?R_(@X^d6e%=VVzJ4Ti0kHy{D_`A64}Vys4nP zIJw2jToaiYjD@2@PnSnU%XBlQJJFcPSggY3m~cON@lNqe1$h+ZGX*BSHZl^+=B5=g z&d%`_-85G_9+7L|2WmjCNED;vZUTDt@qNTe+KY6}q!#!7>8W2=uW!Ac%E)*%K zj2AA8Cz;<}d5vt!cu6UdUr|@p675lHSNmvQC;EqH{tgJ|6I$N3GHox_r5*hHH3#5u zT8FkSY6j=VhbQYShK)}wKBkPodZM%WwcGBfrgg5`X7WpY4N-xOTx%88H_$H>p0 zwaK)f@3VbX=*EJm(h)6*8OsB?Ewb`3%3iy+PU_XJ(T}|tT6TELVijJa;;lGNNqsP3 zakZ3r+WPC%uiRaqOS<+JeoLIsmsMw&yy!5ZO&Z5j8k)7s`tF>5gqvLKay58cV9?qI zxwpEC2KnjgRW!hVuXf>CUL(;EchwZ(d7!2Z&%bI6I4SdMD|Gu~V+3lm7HebRw4aJa zkB^K`=JZtj=5WShH>lDu|FX82hWYJUE%ritWBLHRmN6N7Av&D1dv;2B*M250$sNlG zUqg_ucbtrC_gEFGQ|I=YllRVRV{Bh5V~0RLI04RpD7Xryf$lZN1h5bkfl9C)1i|}Y z7<>b+gL~i^#FWv`04)JFP!Bx74}zc*9BE@kYygE5;0zcCm%(jtA0&DiTL8?!3LId& z@~P+7xU{eF(|dgVg0YyVchS86VK#hA>)OnVWZ&7m&0o2zwUu3nmCOD9oy|-+)>@sY zB)7K_6)N%@2XzVNZB~}c6XY-U_T$^uBk^|V9H++g-3@Jmz6G^IA7XD<6$&aUN|m*NvBZA?n1DI) delta 1722 zcmdUv{clrM7{||Z*O{^ncE@_5V;dP*SgCAwY>dH1EV9X7TIk+d)BzQWyOxmXXcj3qNQWh8QBKgiJOJ8e$efkKenA_%C>y&;6cz zp11Ry(|aQkDH4$mMeFBF2d;S@a4RwDNy2l-Tio`VQ=>NWBoP@CPGs`zqk}DFu@S7w8)vkgg=wX|rO<=YLGyvN^w7-c&sJ zY{#(C)zFsBo6aGMJ$QcDHlN7wkcF?B{W~@3MVTKEo#odo3n+(^;vp;Fpq!yR{l21^ z?Ur!ah+`hq5hM3sUcw=-@3cJhih0}!M;&0nmQwhIn-$9I5FL{r={Ffyg)7^&a zWK;-ku3so#-s~u)YTo0RPc8hkBbPSnUpS(Y?1Wp_U^p}0)$*4Rs+qSGtSL4HUCck0 zWo0ZnEXTV70+z9DSngPJ3S-j~e(%`Ee;?Ev{Of{z>gSUM4zp1ijaj&(P@^{ecwslq zGX5K}E75x*Y!*tpUg}f`o98ZaXSKu9U1c7>5ck~m*Nd<$(3R5&?<=xUyFOC1*hFpo z_VO!Kp^uc75e0RzqK-(@>sE$n&K|@^C;tq~7m|aox|nfTV$m-C)vAN=@Rh$wSi+r( zV-f{;?1@eU%dVqI)X7h+Ek@d_YqP8oWGKJT{Zv}mB6iAtv3%?`7ZXa5&#cra!uM5v zQsl*k2&KnpVzT_6)*4&>2F7Zou}|XLuEBRzl-9gz$@GMpr>j-RDfJMXu(?LdeD=EU z=!kxMT^>nlkYBgV<%?^qjsOA%j79r!NGGP;HFI}vFVc-K^!ppt7C0ETUar#C&4bd7Lb_ClkEPbjCRvzuZguz*+DmxC$mf z5dLYcjLidfPzs&|>%lW11a^W*D|4_O40^%)pdVZW*TEg|h|1U;uo#r-eSx2nW`2y1 z-Gl2Kea!AP7Bmey4?2p>+0b5S4pf8gf(D_xp-s?N Vpq0@345eHc2uwKj)#0I(zXA3DCHVjV diff --git a/DEBUGGER.TXT b/DEBUGGER.TXT index b7dee8a..cb789c5 100644 --- a/DEBUGGER.TXT +++ b/DEBUGGER.TXT @@ -1,11 +1,11 @@ Debugger in Emu48/Tools/Debugger... ----------------------------------- -This is a short description of the internal assembly debugger of Emu48. The debugger is not perfect and/or complete. I provide it as it is. Please don't send me any suggestions or changes at the moment. If you have a better idea make it for yourself. Of course you're free to publish them or send me your changes. +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 PC position. The emulation will continue after closing the debugger window. Please remember that the clock now shows the wrong time. +After starting the debugger the emulation will stop at the current program counter position. The emulation will continue after closing the debugger window. Please remember that the clock now shows the wrong time. 1.) Menu Debug @@ -16,7 +16,7 @@ Continue calculator emulation under debugger control. The emulation will stop at - Run to Cursor F6 -Execute program until address at cursor position is reached. Code and memory breakpoints are still active and may stop execution before. +Execute program until address at cursor position is reached. Breakpoints are still active and may stop execution before. - Step Into F7 @@ -28,9 +28,9 @@ Execute a GOSUB, GOSUBL or GOSBVL as one instruction. Normally the instruction c But this makes trouble in the following code part: - GOSUB + - NIBASC /Hello world/ -+ C=RSTK + 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. @@ -40,44 +40,44 @@ Continue the program until a RTI, RTN, RTNC, RTNCC, RTNNC, RTNSC, RTNSXN, RTNYES At some code constructions (mostly used to save space on the hardware stack) like - C=RSTK - PC=C + C=RSTK + PC=C and - C=RSTK - RSTK=C - RTN + 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 + 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 -Stop the emulation at the current program counter position. +Stops the emulation at the current program counter position. 2.) Menu Breakpoints - Set Breakpoint F2 -Toggle a code breakpoint at the cursor position in the code window. +Toggle a code breakpoint at the cursor position in the Code window. - Edit Breakpoints... -You get a sorted list of the current code and memory 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. +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 @@ -85,11 +85,11 @@ You get a sorted list of the current code and memory breakpoints. When the break - "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. +With a left mouse button double click on a breakpoint or using the space key on a selected breakpoint you can toggle the check box inside. - Clear All Breakpoints -Clear all breakpoints except the NOP3 ones. +Clear all address specific breakpoints. - NOP3 Code Breakpoints @@ -105,31 +105,31 @@ and 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: +A short example how to use a NOP3 Code breakpoint: ASSEMBLE NIBASC /HPHP48-E/ -BREAK MACRO - CON(3) #024 NOP3 - ENDM +BREAK MACRO + CON(3) #024 NOP3 + ENDM RPL CODE - BREAK code breakpoint + BREAK code breakpoint - GOSBVL =SAVPTR save register + GOSBVL =SAVPTR save register - GOSUB + problem for step over - NIBASC /Hello world/ -+ C=RSTK + GOSUB + problem for step over + NIBASC /Hello world/ ++ C=RSTK - GOVLNG =GETPTRLOOP + 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 the static CODE breakpoints instead please. +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 @@ -140,7 +140,7 @@ If this item is checked, the debugger stops program execution on every instructi - 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. Code and memory breakpoints are still active. +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. @@ -151,10 +151,14 @@ You can also use this option if you want to quit the interrupt handler. Just che 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. +- Write Only Registers... + +Some of the display registers have a different meaning on reading and writing. This dialog shows the data written to the write only I/O registers. + 5.) Code window -This windows shows you the disassembled code. The line with the current PC is marked with a "->" between the address and the disassembly. +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. @@ -170,7 +174,7 @@ 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. +Toggle a code breakpoint at the cursor position in the Code window. - Set PC to selection @@ -186,9 +190,9 @@ With the left mouse button you change the content of the register. On bit regist 7.) Memory window -This windows shows the memory content. +This windows shows the memory content in the selected context. -You can use the arrow, PAGE UP and PAGE DOWN keys to move the cursor to a memory position. With a double click on the left mouse button you change the content of the two addresses. When the memory position is read only (ROM) the content wouldn't change. +You can use the arrow, PAGE UP and PAGE DOWN keys to move the cursor to a memory position. With a double click on the left mouse button (only in Map mode) you can change the content of the two addresses. When the memory position is read only (ROM or write protected RAM) the content wouldn't change. Context menu pressing the right mouse button: @@ -214,26 +218,69 @@ Sets the cursor to the return address placed in the top level of the stack. - Find... F -Calls the "Find" dialog box, allowing you to search for a data sequence in hexadecimal or ASCII mode. The search area is restricted to the mapped CPU address area, so it isn't possible to access hidden memory. When you close the "Find" dialog box, you will loose all saved strings in the data combo box. +Calls the "Find" dialog box, allowing you to search for a data sequence in hexadecimal or ASCII mode. The search area is selected by the memory view Mapping mode described in the following section. When you close the "Find" dialog box, you will loose all saved strings in the data combo box. + +- Mapping + +Mapping is a Pop-up menu to select the memory view of the Memory window. Normally the CPU see only 512KB of the total memory, the rest is banked or covered by other modules. The following menu entries select the memory chip connected with the chosen Chip Select signal of the MMU. The connections are calculator model dependent. + +- Mapping Map + +This is the default mode. Here the Memory window shows what the CPU see. In this mode you can also change the memory content of writeable memory. + +- Mapping NCE1/NCE2/CE1/CE2/NCE3 + +Here the Memory window shows the content of the selected Chip Select signal. The content is showed in a linear address model and it's content can't be changed in this mode. + +Here's a comparison of the mapping of the emulated calculator models: + +Abbreviations: ROM = Read Only Memory + RAM = Random Access Memory + Flash = electrical reprogramming ROM + Slt = Memory Card Slot + BS = Bank Switcher (no memory) + nc. = not connected + + | HP38G | HP39/40G | HP48S/SX | HP48G/G+/GX | HP49G +----------------------------------------------------------------------------- +NCE1 | ROM 512KB | ROM 1024KB | ROM 256KB | ROM 512KB | Flash 2048KB +NCE2 | RAM 32KB | RAM 128KB | RAM 32KB | RAM 32/128KB | RAM 256KB +CE1 | nc. | BS | Slt1 32/128KB | BS | BS +CE2 | nc. | nc. | Slt2 32/128KB | Slt1 32/128KB | RAM 128KB +NCE3 | nc. | RAM 128KB | nc. | Slt2 32KB-4MB | RAM 128KB 8.) Stack window -The content of the hardware stack is viewed here. In "1:" is the current return address. A double click on an item shows the address content in the code window. +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 return address at topmost element on the stack. + +- Pop + +Pop the topmost element form the stack. + +- Modify + +Modifies the stack content of the current selection. 9.) MMU window -The configuration of the memory controllers is viewed here. The viewed addresses are the first configured addresses of the modules and may differ from the given address in the CONFIG command. +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 + 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 address #80000. +will config a 128KB module at address #80000 and not at the given address. So the MMU viewer will show you the address #80000. 10.) Miscellaneous window @@ -243,4 +290,4 @@ The Miscellaneous window show you the internal state of the interrupt flag, the You can change the values by pressing the left mouse button over the old content. -05/15/01 (c) by Christoph Gießelink, cgiess@swol.de +02/21/02 (c) by Christoph Gießelink, cgiess@swol.de diff --git a/EMU48.EXE b/EMU48.EXE index a15d4cf3447bd82ddb63b2623d0f8aa5f484215a..1b9027d6aa4c0e89f012f08408e15c3c7922c267 100644 GIT binary patch literal 241664 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 literal 233472 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 diff --git a/EMU48.TXT b/EMU48.TXT index c1c444a..52106ee 100644 --- a/EMU48.TXT +++ b/EMU48.TXT @@ -2,31 +2,10 @@ Emu48 - A freeware HP38/39/40/48/49 Emulator - for Windows 9x, ME, NT and 2000 + for Windows 9x, ME, NT, 2000 and XP - **************** - * INSTALLATION * - **************** - -Emu48 is distributed in 1 archive: -- Emu48-1_25.zip All files and sources - -To install Emu48, just unzip Emu48-1_25.zip into an empty directory. When you -first run Emu48, it will detect the directory in which you installed it, and -will write its configuration to a file named Emu48.ini in your Windows -directory. If you move the Emu48 directory to another place you have to change -the directory path inside the Emu48.ini file manually or have to delete the -Emu48.ini file. - -You can also update your current version with the Unofficial Service Packs: -- E48BP2x.ZIP New EXE-File -- E48SP2x.ZIP Sources of the Service Pack - -Replace the original EXE file please. - - ******************** * OPERATING SYSTEM * ******************** @@ -35,6 +14,27 @@ This version of Emu48 should work with all Intel Win32 platforms. You may recompile the sources to run Emu48 with Windows NT on a DEC Alpha. + **************** + * INSTALLATION * + **************** + +Emu48 is distributed in 1 archive: +- Emu48-1_30.zip All files and sources + +To install Emu48, just unzip Emu48-1_30.zip into an empty directory. When you +first run Emu48, it will detect the directory in which you installed it, and +will write its configuration to a file named Emu48.ini in your Windows +directory. If you move the Emu48 directory to another place you have to change +the directory path inside the Emu48.ini file manually or have to delete the +Emu48.ini file. + +You can also update your current version with the Unofficial Service Packs: +- E48BP3x.ZIP New EXE-File +- E48SP3x.ZIP Sources of the Service Pack + +Replace the original EXE file please. + + ************************ * YOU NEED A ROM IMAGE * ************************ @@ -198,23 +198,18 @@ before doing this. * SHARED RAM CARD * ******************* -You can add a SHARED (I'll explain later) ram card of up to 4MB to a HP48. By +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. -The syntax is: - MkShared +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! -For example, you can create a 2MB RAM card name SHARED.BIN (in Emu48's -directory) with the following command: - - MkShared SHARED.BIN 2048 - -Valid sizes are 32, 128, 256, 512, 1024, 2048 and 4096 KB. 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! +If you use RAM cards greater than 128 KB in a HP48SX, you can only see the first +128 KB of the card. Please remember, the firmware of all HP48GX versions has a +bug when using a 4MB RAM card. You always get the message "Warning: Invalid Card +Data" at startup and Port 33 is unaccessible. This is not a bug of the emulator! When you have created this file, run Emu48, and use the Close menu item to close the calculator state. Now select File/Settings. In the "Port 2" text area, type @@ -223,17 +218,18 @@ searched for in Emu48's directory). You can also tick the check box "Port 2 Is Shared". When the box is cleared, only the first instance of Emu48 will allow you to use the RAM card in Port 2. -When this box is ticked, the first instance of Emu48 will give you both read and -write access to this RAM card. If then you start Emu48 again, 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. +When this box is checked, the first instance of Emu48 will give you both read +and write access to this RAM card. If you start Emu48 in another instance, the +RAM card in Port 2 will be write-protected. Thus you can transfer files very +easily between two calculators. This RAM card is used by both S/SX and G/GX +types. *********************** * FLASH ROM EMULATION * *********************** -The HP49G save the operation system a reprogramable memory, a so called flash +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 @@ -259,11 +255,11 @@ versa. ********** Emu48 includes a backup feature (in the Edit menu). It save the complete state -of the calculator 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. +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. ************ @@ -286,8 +282,8 @@ button. 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 Port 0 are rewritten with -the actual time information. +emulated calculators the address area #00058-#00076 in System RAM are rewritten +with the actual time information. ************* @@ -297,7 +293,8 @@ the actual time information. 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. +again. If you move the Emu48 directory to another place, you have to adjust the +variable 'Emu48Directory' in the [Files] section. ************************ @@ -378,7 +375,7 @@ Technical data: Servername: Emu48 Topicname: Stack -Item: Dummy (ignored, must be a nonzero string) +Item: - (ignored, must be a nonzero string) Clipboardformat: "CF_HPOBJ" (user defined) The DDE commands CONNECT, POKE and REQUEST are supported. @@ -405,9 +402,8 @@ to get more information please. * SUPPORT * *********** -We cannot provide individual support for Emu48, but we will read all the mails -that you send. All informations about Emu48 will be on the Emu48 Official -Homepage on the Web: +We cannot provide any individual support for Emu48. All informations about Emu48 +will be on the Emu48 Official Homepage on the Web: http://www.epita.fr/~sebc/Emu48/index.html @@ -425,7 +421,7 @@ Other graphics and scripts are available at Casey's Emu48 Graphics Page: *************** Emu48 - An HP38/39/40/48/49 Emulator -Copyright (C) 2001 Sebastien Carlier & Christoph Gießelink +Copyright (C) 2002 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 diff --git a/Emu48asc/EMU48ASC.BIN b/Emu48asc/EMU48ASC.BIN index 8f0837ac58c501a49ad0311a24db134536254c39..c89e38f6bbe42ac71e26ff1178255953b88de413 100644 GIT binary patch delta 76 zcmV-S0JHy-0i^+uU`0ZhVTdu12nCmJz)2%6JHVo013F+d~(P4hw|17h!@o-~LtkO&2rZg7J* zE;}HpG#fE9yfrH^a1b+{E#T}WPxHc}0uKOCQi24flQ2rnQ^TSH3jnawprH0O%~P`y H11m8Ab0I0= n6MY@64-5>dsvZGW=aaA4nCfV4U|_K3<;se`Y-6fz2viCHEan+} diff --git a/Emu48asc/EMU49ASC.S b/Emu48asc/EMU49ASC.S new file mode 100644 index 0000000..3055f7b --- /dev/null +++ b/Emu48asc/EMU49ASC.S @@ -0,0 +1,50 @@ +TITLE Emu49Asc ACSII<->Bin Converter + +* Author: Christoph Giesselink + +ASSEMBLE + NIBASC /HPHP49-B/ + +* Please don't blame me if this program destroy your memory content. The +* unsupported entry points are picked up from the beta ROM v1.19-6. +* These entry points may change in future and very important, they have +* changed in past! Use it on your own risk. Adjust the lines below to +* your used ROM version. + +*=SetModes EQU #4649A (ROM 1.18, 1.19-4/5) +=SetModes EQU #4A3D4 (ROM 1.19-6) +*=RadixAngle$ EQU #463CF (ROM 1.18, 1.19-4/5) +=RadixAngle$ EQU #4A309 (ROM 1.19-6) + +RPL +:: + CK1NoBlame + CK&DISPATCH1 + THREE ( String ) + :: + DUP ELEVEN 1_#1-SUB$ ( get first ten characters ) + $ "%%HP:" ONE POS$ ( and look for the HP string) + DUP #0=?SKIP ( if found ) + :: + #5+ LAST$ ( remove it from original ) + DUP ";" ONE POS$ ( search for end of modes ) + 2DUP #1+ LAST$ ( get the rest after modes ) + UNROT 1_#1-SUB$ ( get the modes string ) + SetModes ( set the current mode ) + ; + DROP ( drop position or flag ) + KINVISLF DROP ( translate string ) + palparse ( decode string ) + NOTcase2DROP ( on fail -> DROP2 and exit ) + DUP CARCOMP ' xSILENT' EQ ( heading xSILENT' object ? ) + NOT?SEMI TWO NTHCOMPDROP ( yes, remove it ) + ; + ZERO ( Any Object ) + :: + EDITDECOMP$ ( make string ) + KVISLF ( translate string ) + $ "%%HP:" RadixAngle$ !append$ ( build file header with modes ) + $ ";\0D\0A" !append$SWAP ( and CR+LF ) + !append$ ( append program ) + ; +; diff --git a/Emu48asc/EMU49ASC.SRC b/Emu48asc/EMU49ASC.SRC deleted file mode 100644 index 0355907..0000000 --- a/Emu48asc/EMU49ASC.SRC +++ /dev/null @@ -1,60 +0,0 @@ -Here are the MASD sources for the HP49G v1.19-4/5 version of - -EMU48ASC - -!NO CODE -!RPL -:: - CK1NoBlame - CK&DISPATCH1 - BINT3 - :: - DUP - BINT11 - 1_#1-SUB$ - "%%HP:" - BINT1 - POS$ - DUP - #0=?SKIP - :: - #5+ - LAST$ - DUP - ";" - BINT1 - POS$ - 2DUP - #1+ - LAST$ - UNROT - 1_#1-SUB$ - PTR 4649A - ; - DROP - KINVISLF - DROP - palparse - ?SEMI - 2DROP - ; - BINT0 - :: - EDITDECOMP$ - KVISLF - "%%HP:" - PTR 463CF - !append$ - ";\0D\0A" - !append$SWAP - !append$ - ; -; -@ - -Please don't blame me if this program destroy your memory content. The -unsupported entry points are picked up from the beta ROM v1.19-4. These -entry points may change in future and very important, they have changed -in past! Use it on your own risk. - -Christoph diff --git a/JEMAC.KML b/JEMAC.KML index e803c1f..895b81f 100644 --- a/JEMAC.KML +++ b/JEMAC.KML @@ -9,8 +9,8 @@ Global End Background - Offset 0 0 - Size 324 703 + Offset 4 4 + Size 322 701 End Lcd diff --git a/MkShared.exe b/MkShared.exe index 57a9cf5cb9b878033e903d5af72433f9cd0b89a4..dfadc89cb8f96cddfb3a14afaa60a9ea5d879460 100644 GIT binary patch delta 10571 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 R)r(&%aBQyeDdi>2{{qz+(uM#4 delta 12572 zcmc(Fe{@q-w*O6X+ulM-0<=(|r4dps6txh_57S~QO({z8mePcbP|CBdAy#C{y@K;? zN$@sK(`ytb&YK_4^|aqQYnXA~8fW1sGo#oVrY#Qh1a!1dTw|HX=&cu>GBPDd;eJ2+ zCPno7=8x}R-*oMsbN1Q$oPG8=XP>=y5*jgtMhpkb3`0+suMOUH?cM)Mf9|*4lkj=y zSKS$ChozUhr=V;K{j57p@Bdx*Vw9hUUg}H@z*F0GbFu*hOKp56OD2Q{5#nQt-D+5v*0q^oiele3Zf3#z z($hE1n{k`ayiY*?ckaYLqIJbSFw9?E#i0tCujWBL^>Kmdwx%7z4z#=G$L==f8~P4^ z+xS#gY`0-`P_DGI9w}iG=AmYmxmy3hMi-;B85u@8#vL($sUa>4=<;xz6kbQOa>u71z(b-R?FFphQEYFm}UrGU){m$pSpU_(GhfeJ<=aE zr4sAX1beQmC=1iD#DvEiFaS}cWMcKwv0Ue%XxFmNfnlGuhxi7VDOws;)G4086y=W? zz~^(E@zg6}s>*TS_J(cu#W9Un4z}B~rSc8UF2=CmVtFMa!12ljOyl#Z)RePa_q&*G zwn7@=!hM2ioqF2x%F1=pwOi%Sg*53}vB*-}ab|K1bAa_IFC_<<+LL77Esd~X&Wf`C z7TsEPk{HytrK`8fanTsOG^`7Q1+hx2rfI%--Q?<(d$2=tB3fzB-mMsgZ= zVmpQooYffUKa#Z-&Hl5T>8(iC7OE=N-pwcjj52i=R1xkIxj>%sV~pmLnkN|L?rdyH zPBwC|70!_Ig$4ioSq#&&8@OHp|9Gv`k3uVLBC01RG)+2Yj=0b9 zLFFM3%I@P*T!zTvh_W5sQu!*d8HF?@GFv|ktnwRF{2Cdc5pluPsRynxJ5awrVA!Ii zRbtAPOI2pGLEI#jh0D-n>b%ip?RRupL|dH_&eaEsWQa4PRoMY!q>78AZ;4dpMygmf zOTIJGkQ>?0Mb<qu&Bo~%d;5J2ak|e^yPXX(9$^8Qm+`7BtFXwUOo3#fmaNaE zmRv@og+cfS1MFy`Zm=Y-LdYwlW&2p7lZm=GD%+mP<)N#mV39D2ZgjrGx z%N(*=I?tdjZ{+OPBGV!ut};`H)NE$#e=`S-Ek83GfEE`dH6d^*EljOb<|h{?W_Xn7 zJWNXJ;0#`&Nv`7LHN}Z&wIMQ1-k7^StVK4m`>kHSPI(pb>w9Zf9nuv|m?g*MKlCTk zq_YJw8*Y5a#YEX5HSNe4uH3kb4cde(GFNrVUojaNF?4qg*Tt#X0YlWyZKpAtu8+C8 z!@ONcmo}Phszr8lg>(W`&TPX1q#>>0zHOwIS{!(}!)*CwhZQuDjtPk8=H!pW3`gJS zA4wTvc5-J0E+}KG+}UczpgWuPRNiPlaF)aE5*ERj;?^#34$D{CjB>)OiJZ1V8J^29 zi}tgIHITLVBSQzLRynt2w^fCY3sXJHy*P0=SK^~mM>Zp5$#KW&OJcTsrT9zLC|}Ws z!0G#ba;Y%N!q#;LH&;CD9Bf-7cTA{@Ah5@Bl;M?|uE7?7!YACOLHMfNdmv`GFGzwy zJSKd`66RqqWVaPI;CP&LBD*Ir&0w)Ut6BXd7QCfB*b~AP^#s7%AZ8q)hf7U&4s>M5 z1HIG}V0k5;ht2KXx67r=eiKYsq?G!UpMR}`3(Qp*TwR~z_>n$b8aanpBJ6aj&nES= zbKo>;BpBrT>HBXab+S0=f1p) zZkdoa`b$i#Zzc4BQ+D7ogEj9pC(W_tRklLiBoBGyQkt^=GG}?^)Oe-G5NZ&ng_^H1 zT{RP3?yCW#ni*;iUS+!66S#HJnkfMTy^FPc)IGrm3`#F1k&fj-9^>)2Lil z_=)5PH;$7x*sW8C@ZT(ElAmzt?3-4m?$TL#n0hZY*|SZ_+F8>(_n`}C@4)$LXgGNQ z9pl{v>w=wUBI3n}cs_y?*?AELT&@{&oK`s$vUt*QdUgGI%M%|0AG%^|pBcKcFF4I| zG~OM076wZiv8YBzpXF%hz-wssCs-+w5pkcieD}v-(Cpzq6@aK_OpDZ?h|~{9>PI5= z=OXnJk@^ou_kjWf1<5;cN{UIiRZ=*ipt@R^ftDrgL%&or!Hx3C3Ol+kSuu>RK_yf( z5gIK`VvM6p2ybi4milmW$KYtqTQfu7;rc(N^?C>Q5$55cFet zB|ctRVF(FxqVDrN?lyGAD?w0o7vO5I1J8{eEi}V1^JpPTP)dmhEKlWOr=>)*CHk+p zk5Xcb8!y)_eG*j z4is@GMn8di^rapK6+OZZxtfLfTr%#y$_cYAZ!o76tP)eD7CQQCQfTvsw0884_M+L- zNV;$*_|YdZxM&Vtc|e3|9HXgV5;gIJ)W=0o35sKKbs|!8M0QO?+^^9tr`8m8AAx83 ze2WY3!Gv@y`@qK`FbmmI#~33{OA%=~zW{-chMy50zXAP%j@A`>tC)qoG(=zj4O9gk zdzIN=!b*-#Ko}{g?6h=}o+TyC()sVGKvSJm2(@$%JFkMN8n|ESG`N_@!8YiA_ZGC( zvf}V@L|(Wi8_F%E3t&OkBlo(K&F-52q!F~YWP z_qzk`PfOU^LH9W*5LQuDt926hMPd@1ScFk6)wgCIb`gCxpOD!i*PL&OaB3RL2$$OV z(S@`nI9k}@u?NWE!;Hj4&wOHc`_?Q)M0s}??-B(@|s?MwNL#! z=#m7S@>892M0t)%$7$*j=7moOXSd_c*fagouymS{$1Z(9%9g)WS$RzU5@#la=j0QH zcC8~bbplite(EK8VCtY4MD-lb+p9P%vU^Oc8N(x*PF;dic3=F_Cf@6bxcegY$CTgW zS=Pj3@~{*y(ZoKlM_Ee`^_A5$(SDO@+q<$mq17b1a@AjBz+yDq2?$`sQ5ELMHHpyb z5|N&_ED*WS9x#+dpns`jn6ZQ}LQV3viSf8gxOsfgAf#hAZdJ3#5VnmHaf3R8i# zVTZ7pIF=*D;<;k+;&|LF&PZ7!Pr#noQ}Rp1bG))Fi5-vQ5{<{%6h~0x zJb@DBuNV(3QpbmkP$6|(WcJhQ_s<-G2*)XjA< zi{`kPO29$DAmD8P3;YiNcu(aTv$meyH%*@2?dH0Sn!)l49CeaR`O!sOGIEh{-^DbE z)3(JmW?LM-U%TC^i@N_NSqLI-qTmUTt^5LJ7FJAJqX`*0Mex73H^l4YtqAYq_jtVF zzWq0YI}Z-I)p9i5TLrE>UJ+AlcM@Ct9vq;*P~1f>fyA&f1Jjafa0p{65_0Wio0W^0 z7V7R1bni+qYA5~Kjfm7Z{8RoD8uGK$WEx`!avWs`;s1!!F)OF#*zw}SK-tI-Xt$dg z5;Hqe{sc(6^2t#W%X)SvM_nLiweE_I<;*T4#GcO@{5Bdim*!ZEIeb2U5h3h^fHMt5 z_{@Exl_rXm)1+EX9v@pf>s!HR(1_H)J26MHs1DmK!NCd-flJOBfz_3B&f&l5l*jp2 zM^1G4`r=d3<-gty6oo4T=DWp9&`9ZdAj)Bz`$Y3#8Z{&gpzba|cmwJHp|+JjeFG{; zrcu4SJaz*vOsJLR(Hl^Y5z1cv)D5V|2{pa^dxSztgr=lam8MG);Iwyq5xwebP&uiQ zV`_ncciBseMC_nuYNA=o2^K-!2qWapc2>?(Fv#j0KmL1;(0_r}i`7zH>N7hB_OPq1 z;ydjatl~x#pe#) zSuQN7S2!>^b@@ZN40H5{AojDcGoCsvX9+abL-9X?g`KgYIfXO)ct_+hO38cy z5jdsvZ?Wxj7GzT7zrCTiX=ki^PL;8uHFkQ=S}JDGJv*c{bo;>gwfNU3`q6mph-Dn4VFKw45@`FDIN=ES;YMGnn>lAolpY^5#`Q zpGwr#x$5e8PhDN4(!N}KUsjx|u&iGB6MPy{QKj7~EUc@mr@V?CRNDJ6Y}3%NDnGz; z>5FWI2?2PtoaHo4T&Dp(gK>3gJsymI!xq56c(t4o zVjCduo$@=BpEzGh87Duv49PH?4$hKgEWwqQ;9BGhjgqGsfz%! z8u5>w^|L(F2f4QBgZEZLAR$L-!pVXXP#xDSZI;wSX1g|}YMPqr^YFARC2 zkYTpLpvr&6z1x5=tAfG^M$f&*o|vC+>|7LkasC5^ouC)3a;;h3WL{m%iD^1Tysq|l z6S#Tm6ulB#Sunnk(nV6jAY?&|#ZvhZm_37dlYauEQuc)Dc|H*1ZFZ-%1vl0zOogvl zQBIy<`vs4Dq5or(^IxgU63&D6ds3sp!38Tu%%TxvzqAzw=?VZbGaBBCaNW;shQ(IY zsbpW1C@&v-kj95f;G&j82LZcE$=-(Jajoxm{@o@qS4t!K-aG zh?XF)+(r%r<#O`Zs&fg=h%?9gjlwiunVISmjZ+gv(8>6t&0)6|U-4;d_-UXVb49acn4k&ZDvIY+od zHo+V)dTSMqG=qeo$uxjOVaU?ik7;GMnONKtkq*xC_$RV>ItdcDpP<{PV-ivKMO=eq%gf6qZFx!Z z{6i;e;V2v23Flz@R3zAn-DaoT+ScuW+XPS;63oy+Xw_Ux z=O1BMl9M5k#YDgK^ev6;Dk?H8i~XRe5dU8L98ZYf`AoNui2KC1nRk{ zxB%T%riG`v)|oc;j~efVzuWf8)h~WBq1nGb;)u;$yvX2)l`mduPmcQxie~{Y0Db{@ z8Son57~p+J?0bvX&qPhu#_&G@;Fe=8%VVD}UKdOnC$Hs~2u7r9Bl4g3X2Hst0)_@@ zz#@G-=FouMyV#kpThOdJ<%6jVo@8HG6N^3YVN(w(nNm3c@HuG?WD zTm4O40FTK3nyH(%qy@pZ8~EPr{(#A$HKa^dNOgjYyiH0D#Fu+@1J6Um2(3xKq$uknO!ZpgLK9Ao<9dIZ2={=TCiirZoExj}b`CN9{orjUqdWPr~60=h`2caqP z#6Dj+%Qzf}rLUS_EMNpAA)RsI{>Zk8Xc(Q*pNW*^Ut@9`gX2kAY zRh&)qAjU<^<~_P4Z0BQ7uF4Hk-08;meWGz2zE+}n8F2>kAm)|>mo;YZ>VTn(+p;Yl zt>L6*1QM{SrSuIVVr6r{!24~`l~y`T;^vSyiuI;T9UQ(7NZwY8$Y4IVCGQc=1is*# z+J~=ZH`udLw+8X0!390XY)1KxS^ZC`t;QB)Z^mCOJ5)%(u6NZ zhtQLK>0{HWwrNh#?d94soS=Qgu+ec^9%!KKbSklZqK-&Y^XUWX(@{eEg8U{j(|k~$ z&x-lXAmzR>-w@@!pT<9HYBDn>n}*4_N4R20KX0==8zqO*eCY!uZ9ZUVcHK1yj|+B> zatEAWe5vRWrtpf7>UiHtheZE{dGJ(5p#N1s00i!Z*YX06UYgfk~h zQ=SBa5|b9rDklV%d`+|trcs^b(F(hCJzV`dvQtM+h?X8YxkMg&Z(+q9q#&pzHIKfO zUYb}J9@B15xd7NlWQ9EVqL)@nx{lS_9}3bElPG#!@nCuAo0qLf0tM*hM(#7&>$%?T z(MpL)S%EKpbs9WU-=y)=*H|gKX`qviZFTfTqvw%!ZIP#szlkqDlUiJ4?S3YWX6ZWU zf>>(a_?t$-n(VVt7Y0}VJM_Hk!B|USL68)0?H&sC+q$}R`T{Ajqiv>?_|D#GlT1UQ zQJXqKEKse>InZ_#KC)C!fj+E6;Ht8OUe&=gLXf)Seixm#>*t${cetnLrv^{ZtxVi~$wtH8oX(bXKKuGSvEke9GBOXw@@{_t z#o;$^R}Dep5NE*YbLzSfGg2xACc;JUV)b#7BV>_Q;kj$hez7E48R7d!j5x3)gLl&W z9K^bugTf9exdr+@rMQXCf2;zV$zi!khUneP4`?~Gr_RAhl~q_AVEv2@kr(|G2~qSD zsU9;Pu1BobXCdw3O`a`nT@(|8gpOUjkBlulHpXJygdNG)p@v4~NeP(|TSG z#LhbAPA5ik;mJ49t?l8AzjBfB4(c(#<8&m8{MRGFK3HrgC)f~^ z-&I}&RQm<#^31k;>9T!qZjuoUwmNVG2^PVGveIt2BJGl}a5HIQoi*1nYW%TQAP!0~b|} z5Bx(l@quxCfka!AA(=}wDDt1gNjGXP?;A1iIAUHyl26}adT!nABg{i#q z*Uu4CwdCPPP=iv78QUL^O)FbED_~T9@D{9A%9Y!VGeoZ3ywd!t5u66G%8TDz-B+hO zaYxCj;W5rRr0=i5Me>P8UEuZBC7n6+y`cA5Iv0R;Fl{6d+h3YzMDc9tyt&jj`e$s2 z{uIB1%IXfA$l=+EohqGg6!_SM(s}Fv-inxY#mZdr(ja^dhS$UoDQKc2b%>8`U$NAP z;(IG@V`mWYu@%dy{pE^<##MZ*VC5Q?PBlW5@)d_RV2}QX7^UwXToZfr2)ZdoDWmcv za1#LA2E-`07!PegkYcCe^`hKD8YsG+c@D)BfKEU=KmfD?ngCljAWW&w)~D2HHH$?OlmKubeUL^<5x*k20Ntyu%R5OZnw5}z{ufgy4{(*|m`Fo#`=T%0j4lA)) z-(GP_GUk;PBqBcZDz%Ab#PlW~qXhq#LcQ+wD%H zPiYCm6S1vm6~85eV^)@wc=-&pZDbf3uF>C!55w$Ux_ci!97vc9hT~9h_@gFM#Oz{3 zrWJpSh=(cKCAPLMVh&$%T`(e2Onyij(+U@pvBDKo*Oo7)cJAFcXaK68KzS1I(&kAH zhWX!655qrw)2#pB$}j$ZP=4%5x94!>y5)x0vUM|J`>O9+`y)ej^N!sWt*sB=&&)Nf zYZf-_*dw^x+8%C$lVGfC7OHl(HuDef+$A&vbIwpBwhBA%dQ@oM{IGXtQ}bH?j<(pg z^@XwDRxgMt)vkpOn=DM!bF|@z=seE`>^KC$`-LY%e!-DPM-OamfEg0QaxaYPh!0WQ!Ve{;0-PvT@ zv-3O6du%(}nr)6{OKeM*ty*GRR=Q$|t#m0hOO`E1yJYz)8w9Z}FKJo50z=gA+3`U0 z9aAz3_t?s4;9WZ&YF=Y2qoKC%+_n9|e_1nynXn~2VG=F`TQXT1aBQ!C$l3kI2R=Sy zz>mJusA9xVwr@9P-hID|Az)&)bu$l_)r}eE(I~XW8%HauMQ*3o75j+)kKi+Ph^El* zN12JT3?;2S7bVSlev^xtj`B5>Gf@5zB^~Zol({IEqeM!TF{8wC?_B@o&qNK!| zfN~$oCX|n&+>G)&C@WFo8;2!~3l)mxN~o;?kPn2rudfKVnlS@tl~mFysiaj>-&DY4-z?OLmr4tO#0C*r*N%VUfYWDz0z!v~C;ZFgCe-%Ii{wIL?-v>~CF4SVii8_=1 Fe*nqox;Ov; diff --git a/PROBLEMS.TXT b/PROBLEMS.TXT index 74faa52..8935538 100644 --- a/PROBLEMS.TXT +++ b/PROBLEMS.TXT @@ -1,4 +1,4 @@ -Known bugs and restrictions of Emu48 V1.25 +Known bugs and restrictions of Emu48 V1.30 ------------------------------------------ - the following I/O bits aren't emulated (incomplete) @@ -9,7 +9,6 @@ Known bugs and restrictions of Emu48 V1.25 CMODE (0x10A) Mode register IOC (0x110) [ERBZ] RCS (0x111) [RX RER RBZ] - TCS (0x112) [BRK TBZ TBF] SRQ1 (0x118) [ISQR VSRQ] SRQ2 (0x119) [LSRQ] IRC (0x11A) [IRI EIRU EIRI IRE] @@ -20,14 +19,19 @@ Known bugs and restrictions of Emu48 V1.25 Windows 95a 1920, 3840, 7680 work, 15360 fail Windows 98 all baudrates fail Windows NT4.0 SP3 all baudrates fail +- problems when receiving a break signal on the serial port + Windows 98, NT4.0 SP4 no retrigger on port open + Windows 98 timing problems setting the RER bit + Windows 2000 SP2 no known problems - System-RPL commands VERYVERYSLOW, VERYSLOW and SLOW depends on PC speed (are realized as simple down counter in ROM) - display updating differs from the real machine - screen VBL counter values may skip after large display operations like turning on or updating the whole display +- executing an opcode over a MMU boundary will fail - read on an unconfigured address (open data bus) will not show the same value like a real calculator -- the G(X) hardware signals BEN and DA19 aren't fully supported, +- the Yorke hardware signals BEN and DA19 aren't fully supported, because the emulator don't use a multiplexed AR18 / NCE3 data line -> all programs that run on a real calculator will run as well, programs with incorrect DA19 / BEN handling may run on the @@ -41,20 +45,14 @@ Known bugs and restrictions of Emu48 V1.25 - beeper emulation, ATTN key doesn't work, Windows 9x: plays only default sound event or standard system beep - no infrared printer support -- memory window of debugger view some addresses in I/O register area - with invalid data -- the 'Offset' command in the 'Background' section of KML scripts - don't work, the offset is always zero - Shell OS: clock isn't synchronized with real time - HP49G: the flash memory is emulated now with some restrictions - - first implementation, at the moment the flash memory is more a - simulation than an emulation - no flash programming times, the flash state machine returns immediately the ready signal - only one write buffer, second not needed because of prior reason - - not fully tested, especially the status byte may sometimes - return incorrect values (error bits) + - not fully tested, especially the status byte may return + incorrect values (error bits) - quitting the emulator while programming the flash isn't allowed, because the content of flash state machine isn't saved so far -05/15/01 (c) by Christoph Gießelink, cgiess@swol.de +03/19/02 (c) by Christoph Gießelink, cgiess@swol.de diff --git a/sources/COPYING.TXT b/sources/COPYING.TXT deleted file mode 100644 index 60549be..0000000 --- a/sources/COPYING.TXT +++ /dev/null @@ -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. - - - Copyright (C) 19yy - - 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) 19yy name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. diff --git a/sources/Cardcopy/CARDCOPY.C b/sources/Cardcopy/CARDCOPY.C index 4d01877..23546bf 100644 --- a/sources/Cardcopy/CARDCOPY.C +++ b/sources/Cardcopy/CARDCOPY.C @@ -24,13 +24,13 @@ #include #include "types.h" -#define VERSION "2.0" +#define VERSION "2.1" #define FT_ERR 0 // illegal format #define FT_NEW 1 // empty file #define FT_SXGX 2 // Emu48 HP48SX/GX state file -#define _KB(n) (n*1024*2) // KB in state file +#define _KB(n) ((n)*1024*2) // KB in state file #define HP48SIG "Emu48 Document\xFE" // HP48 state file signature @@ -63,9 +63,9 @@ UINT CheckType(char *lpszFileName) BOOL SeekData(HANDLE hFile,UINT *nPortSize) { BYTE byBuffer[16]; - CHIPSET Chipset; + CHIPSET *pChipset; UINT i; - DWORD lBytes; + DWORD lBytes; SetFilePointer(hFile,0,NULL,FILE_BEGIN); @@ -83,16 +83,20 @@ BOOL SeekData(HANDLE hFile,UINT *nPortSize) // read CHIPSET structure length ReadFile(hFile,&i,sizeof(i),&lBytes,NULL); if (lBytes != sizeof(i)) return TRUE; - + // read CHIPSET structure - ReadFile(hFile,&Chipset,sizeof(Chipset),&lBytes,NULL); - if (lBytes != sizeof(Chipset)) return TRUE; + if ((pChipset = LocalAlloc(LMEM_FIXED,i)) == NULL) + return TRUE; + + ReadFile(hFile,pChipset,i,&lBytes,NULL); + if (lBytes != i) { LocalFree(pChipset); return TRUE; } // skip port0 - SetFilePointer(hFile,_KB(Chipset.Port0Size),NULL,FILE_CURRENT); + SetFilePointer(hFile,_KB(pChipset->Port0Size),NULL,FILE_CURRENT); - *nPortSize = _KB(Chipset.Port1Size); // expected filesize + *nPortSize = _KB(pChipset->Port1Size); // expected filesize + LocalFree(pChipset); return FALSE; } @@ -154,7 +158,7 @@ UINT main(int argc, char *argv[]) if (nSourceType == FT_SXGX) bFormatErr |= SeekData(hFileSource,&nSourceType); if (nDestType == FT_SXGX) bFormatErr |= SeekData(hFileDest,&nDestType); - + if (!bFormatErr && (nSourceType == nDestType || nDestType == FT_NEW)) { assert(nSourceType > FT_SXGX); diff --git a/sources/EMU48.TXT b/sources/EMU48.TXT deleted file mode 100644 index 7bc0307..0000000 --- a/sources/EMU48.TXT +++ /dev/null @@ -1,449 +0,0 @@ - - - - Emu48 - A freeware HP38/48/49 Emulator for Windows 9x, NT and 2000 - - - - **************** - * INSTALLATION * - **************** - -Emu48 is distributed in 1 archive: -- Emu48-1_20.zip All files and sources - -To install Emu48, just unzip Emu48-1_20.zip into an empty directory. When you -first run Emu48, it will detect the directory in which you installed it, and -will write its configuration to a file named Emu48.ini in your Windows -directory. - -You can also update your current version with the Unofficial Service Packs: -- E48BP2x.ZIP New EXE-File -- E48SP2x.ZIP Sources of the Service Pack - -Replace the original EXE file please. - - - ******************** - * OPERATING SYSTEM * - ******************** - -This version of Emu48 should work with all Intel Win32 platforms. You may -recompile the sources to run Emu48 with Windows NT on a DEC Alpha. - - - ************************ - * YOU NEED A ROM IMAGE * - ************************ - -Emu48 needs an image of the ROM of YOUR calculator to be able to run. Since the -ROM is copyrighted by HP, I won't give you mine, and you should not give yours -or make it freely available. To use the ROM software of a calculator, you must -be the owner of this calculator. - -- HP38: -To upload the ROM of your HP38G, you will need a special aplet called "ROM -UPLOAD", available at http://www.epita.fr/~avenar_j/hp. Once you've uploaded the -ROM, you have to convert it using the Convert utility. - -To do that, start a Command Prompt while running Windows, and type: - Convert ROM.38G - -Where is the path to your ROM image. This will create a file named -ROM.38G. This tool will also check its validity. - -- HP48: -If you have already used another HP48 emulator, you can convert the ROM using -the Convert utility. - -To do that, start a Command Prompt while running Windows, and type: - Convert ROM.48G -or Convert ROM.48S - -Where is the path to your old ROM image. This will create a file -named ROM.48G or ROM.48S, depending on the version you own. This tool should be -able to read any style of ROM image, and will also check its validity. Note that -if you run it with only one parameter, no file will be written, but it will -still check the validity of the ROM. - -If you have never used an HP48 emulator, and don't have a ROM dump, you can -either use Jean-Yves Avenard's ROMUPL.BIN or the ROMDump Wizard V1.x, which will -almost automatically get the ROM from your HP48. Don't use the ROMDump Wizard -versions V0.x any more please! After the download you may have to convert your -dump with the CONVERT utility into the Emu48 format. - -You can find the latest version of the ROM dump programs on: -ROMUPL.BIN http://www.epita.fr/~avenar_j/hp/calcen.html -ROMDump Wizard http://privat.swol.de/ChristophGiesselink/index.htm - -Once you have your ROM dump(s), SAVE THEM TO A FLOPPY DISK! It will save you a -lot of trouble if you were lose them. - -- HP49: -There's no ROM download program available so far. But you can find a HP49G ROM -for emulators in the YorkeM emulator package or in the HP49G SDK on -http://www.hpcalc.org in the HP49 section. - - - **************** - * HOW TO START * - **************** - -When Emu48 is installed and you have put the ROM image(s) in 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. - -Choose a KML script in the list box. If you have put a G/GX ROM dump in Emu48's -directory, choose a script for G/GX. If you have put a S/SX ROM dump in Emu48's -directory, choose a script for S/SX. - -Several scripts are included in the common 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 Casey's Emu48 homepage: - http://www.gulftel.com/~pattersc/emu48/ - -And if you are interested in writing new scripts, get the KML 2.0 documentation -from Christoph's page at http://privat.swol.de/ChristophGiesselink/emu48.htm - -Once you have selected a script, press OK to start the emulator. While it's -running, you can use the View/Change KML Script... command to change the visual -aspect of Emu48. - - - *************** - * KML SCRIPTS * - *************** - -In some cases you have to fix Color 0 in your KML script file, because the -colors red and blue has been swapped in the "Lcd" section (bugfix in a previous -version). 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. - - - **************** - * 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 (I'll explain later) 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. - -The syntax is: - MkShared - -For example, you can create a 4MB RAM card name SHARED.BIN (in Emu48's -directory) with the following command: - - MkShared SHARED.BIN 4096 - -Valid sizes are 32, 128, 256, 512, 1024, 2048 and 4096 KB. If you use RAM cards -greater than 128 KB in a HP48SX, you can only see the first 128 KB of the card. - -When you have created this file, run Emu48, and use the Close menu item to close -the calculator state. Now select File/Settings. In the "Port 2" text area, type -the name of the file you created (if you don't include a path, it will be -searched for in Emu48's directory). - -You can also tick the check box "Port 2 Is Shared". When the box is cleared, -only the first instance of Emu48 will allow you to use the RAM card in Port 2. -When this box is ticked, the first instance of Emu48 will give you both read and -write access to this RAM card. If then you start Emu48 again, the RAM card in -Port 2 will be write-protected. Thus you can transfer files very easily between -two calculators. This RAM card is used by both S/SX and G/GX types. - - - *********************** - * FLASH ROM EMULATION * - *********************** - -The HP49G save the operation system 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.E49). As default -setting the ROM file is writeable in the first instance of Emu48. When you open -another instance of a HP49G emulation the Port 2 area is READ ONLY, that mean -all changes in Port 2 are lost when you exit this instance. If you don't want to -save data in Port 2 and want to protect the operating systems from overwriting, -you're able protect the ROM file. To do this, close all Emu48 instances and set -the variable 'Writeable' defined in the Emu48.ini file, section [ROM] to zero. - - - *********************** - * COPY / PASTE STRING * - *********************** - -With the menu items "Copy String" and "Paste String" in the "Edit" menu you're -able to copy HP string objects from the stack to the PC clipboard and vice -versa. - - - ********** - * BACKUP * - ********** - -Emu48 includes a backup feature (in the Edit menu). It save the complete state -of the calculator 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 Port 0 are rewritten with -the actual time information. - - - ************* - * EMU48.INI * - ************* - -The section [Timers] in the Emu48.ini file isn't used any more. The variable -values are replaced by useful constants. You may delete this section if you -want. Starting an old version of Emu48 (V1.07 and earlier) will add this section -again. - - - ************************ - * REAL SPEED EMULATION * - ************************ - -As you recognized the speed of the emulated HP is much faster than an original -one. The reason is, the assembler commands are emulated faster than the original -CPU can execute them. On one side this is a big advantage (faster execution of -programs) on the other side this cause many trouble. In Emu48 only the timers -work with the original speed. In result all commands like User-RPL WAIT wait -more or less the correct time. But many programs like shells or editors use an -own key handler to realize an autorepeat implementation. Normally these programs -use the execution time of each assembler command for waiting. On Emu48 this time -is much shorter, so the time between each key read is shorter as well and you -get a very fast key repetition. The editor ED from the JAZZ package hasn't this -problem, because the key input is synchronized with one of the timers. To solve -this problem Emu48 generally slow down emulation if a key is pressed. To solve -some other speed depending problems you are able to slow down the whole -emulation speed. There are two variables 'SXCycles=82' and 'GXCycles=123' -defined in the Emu48.ini file, section [Emulator] which control the "real" speed -and key repetition slow down for each calculator type. Each numeric value is -representing the allowed CPU cycles in a 16384Hz time frame. Because the used -cycle statements (from SASM.DOC) in Emu48 doesn't correspond to the real values -of the CPU, the saved values are estimated by comparing the execution time of a -program to the real calculator. Increasing the value fitting to your ROM will -make the "real speed" HP faster and vice versa. No warranty to the functionality -of Emu48 when you go below the default values. - - - ************************* - * SERIAL PORT EMULATION * - ************************* - -The serial ports are emulated as well now. You may choose the same serial port -for wire and IR. Remember that the IR port only work with 2400 Baud. If you want -to change the serial port settings, but they are disabled, close the serial port -with the command CLOSEIO or power cycle the HP first. - -Now it's possible to make transfers between the real calculator and Emu48. If -you have problems with the connection please try the following. There's a simple -way to check if your serial port is used by another program. First disable the -serial settings in both combo boxes and very important close the settings -dialog. Reopen the settings dialog and choose the COM port in the wire combo box -to the port the HP is connected with. When you open this combo box you only see -valid (unused) serial ports. Don't use the IR combo box, it only works with 2400 -Baud. The next important thing are the serial settings of the real calculator -and Emu48, they must be equal. If this doesn't work then mostly there's a -hardware or a resource problem of the serial port. Check this with connecting -the HP with a transfer program you like on the same serial port. - - - **************** - * DISASSEMBLER * - **************** - -With the internal disassembler you're able to disassemble the Saturn chip -address area. With the default Map setting the disassembler always see the -mapped memory address. If for example you configured the RAM at #00000 you will -see the RAM and not the ROM at this address. With the other module settings you -specify a special module for disassembly. Each module use a linear address mode, -beginning at address #00000 and will not overlapped by other modules. So, for -example, you can access the second port of a HP48 RAM card greater than 128KB at -address #40000 (128 * 1024 * 2). The "Copy Data" button copies the selected -disassembler lines to the PC clipboard. - - - ************** - * DDE SERVER * - ************** - -I implemented a DDE server in Emu48 to transmit data from and to the HP stack -with DDE. You have the same restrictions like with the commands "Load object..." -and "Save Object...", that a running program may corrupt memory. Take care to -transmit data only after the acknowledge of the last DDE transaction. - -Technical data: - -Servername: Emu48 -Topicname: Stack -Item: Dummy (ignored, must be a nonzero string) -Clipboardformat: "CF_HPOBJ" (user defined) - -The DDE commands CONNECT, POKE and REQUEST are supported. - -The structure of the clipboard format "CF_HPOBJ": - -+--------+------------------------------------+ -| 4 Byte | HP object | -+--------+------------------------------------+ - \ \ - \ +--- normal HP object - +----------- length of object (LSB first) - - - ******************** - * TROUBLE SHOOTING * - ******************** - -Visit the Emu48 FAQ site at http://privat.swol.de/ChristophGiesselink/index.htm -to get more information please. - - - *********** - * SUPPORT * - *********** - -We cannot provide individual support for Emu48, but we will read all the mails -that you send. All informations about Emu48 will be on the Emu48 Official -Homepage on the Web: - - http://www.epita.fr/~sebc/Emu48/index.html - -or on the Emu48 FAQ at - - http://privat.swol.de/ChristophGiesselink/index.htm - -Other graphics and scripts are available at Casey's Emu48 Graphics Page: - - http://www.gulftel.com/~pattersc/emu48/ - - - *************** - * LEGAL STUFF * - *************** - -Emu48 - An HP38/48/49 Emulator -Copyright (C) 2000 Sebastien Carlier & Christoph Gießelink - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 2 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -PARTICULAR PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 59 Temple -Place, Suite 330, Boston, MA 02111-1307 USA - - - *************** - * The Authors * - *************** - -Paper Mail: - Sebastien Carlier - 10 Allee des bergeronnettes - 35340 LIFFRE - FRANCE - -E-Mail: - sebc@epita.fr - -Homepage: - http://www.epita.fr/~sebc/Emu48/index.html - - -Paper Mail: - Christoph Giesselink - GERMANY - -E-Mail: - cgiess@swol.de - -Homepage: - http://privat.swol.de/ChristophGiesselink/index.htm diff --git a/sources/Emu48/CHANGES.TXT b/sources/Emu48/CHANGES.TXT index 47c3702..43b66c1 100644 --- a/sources/Emu48/CHANGES.TXT +++ b/sources/Emu48/CHANGES.TXT @@ -1,3 +1,333 @@ +Service Pack 30 for Emu48 Version 1.0 + +DEBUGGER.C +- bugfix in function Debugger(), used wrong debugger window position + when task bar was on top or left side +- added implementation of "Write-Only Register" dialog box + +DDESERV.C +- added io.h in header definition +- removed usage of Chipset.dispon variable + +DISPLAY.C +- added sources of gray scale emulation (experimental) +- replaced the LCD1_ROW by the LCD_ROW definition and the use of the + LCD2_ROW and the LCD3_ROW definitions by a calculation base on + LCD_ROW +- changed macro LINES(), added brackets forcing operator priority +- changed BITMAPINFO color table variable type from DWORD to RGBQUAD +- changed LCD bitmap from 64 palette entries to the number of used + colors in the LCD area, so function UpdateContrast() do not change + the drawing patterns to a new palette entry, it change the palette + itself now +- bugfix in function UpdateContrast(), switch palette to LCD off + background color if display is off +- bugfix in function DestroyLcdBitmap(), contrast palette wasn't + restored to startup colors +- changed function WriteToMainDisplay(), used wrong sizeof argument + to get the 4 byte x-multiplier in the calculation of the memory + position in the LCD bitmap +- bugfix in function StartDisplay(), the VBL counter of a switched + on display starts at the LINECOUNT (0x128,0x129) register content + +EMU48.C +- removed usage of Chipset.dispon variable +- bugfix in function SetCommList(), used wrong NO_SERIAL string + definition for UNICODE mode +- bugfix in function OnDestroy(), call function SwitchToState() only + if CPU emulation thread is running +- bugfix in function OnViewCopy(), clipboard contained wrong data + when left display margin was unequal to zero + +EMU48.H +- changed prototype of function StartDisplay() +- extern declaration of global function + +EMU48.RC +- fixed misspellings "hexdezimal" to "hexadecimal" +- added "Write-Only Register" dialog +- added Windows XP manifest resource +- changed version and copyright + +EMU48.XML +- manifest file for Windows XP + +ENGINE.C +- changed function WorkerThread(), added parameter to StartDisplay() + function call + +FILES.C +- bugfix in function NewDocument(), OpenDocument() and + RestoreBackup(), used wrong window restore position when task bar + was on top or left side +- bugfix in function SaveBackup(), get workspace instead of screen + coordinates of main window +- changed function LoadObject(), changed text output of "load error" + message boxes + +KML.C +- changed function InitAnnunciator() and DrawAnnunciator(), + determine the number of valid entries now from the annunciator + array size + +MOPS.C +- removed usage of Chipset.dispon variable +- bugfix in function Npeek(), fixed most of the wrong read values in + the I/O register area +- changed function Nwrite(), added conditional gray scale compiling + and added parameter to StartDisplay() function call +- bugfix in function WriteIO(), after changing the DON bit in + BITOFFSET (0x100) the display contrast must be updated as well + +RESOURCE.H +- added some definitions + +SERIAL.C +- redesign of character transmit part, on some PC systems corrupted + characters were sent + +TIMER.C +- workaround for Win2k in function CalcT2(), when detecting a + "negative" time use the CPU cycles for new timer2 value + + +Service Pack 29 for Emu48 Version 1.0 + +DEBUGGER.C +- bugfix in function UpdateMiscWnd(), readded trailing spaces in + "On" text +- changed function Debugger(), replaced bDbgEnable with nDbgState + access + +DEBUGGER.H +- added new debugger state defines + +DISPLAY.C +- added varaible and functions from MOPS.C +- new functions StartDisplay() and StopDisplay() to control display + +EMU48.C +- added VOID as parameter in functions with no argument +- added Critical Section initialization for gray scale emulation +- changed function UpdateWindowStatus(), OnDropFiles(), + OnStackPaste() and OnObjectLoad(), replaced bDbgEnable with + nDbgState access +- changed function OnDropFiles(), OnFileClose(), OnStackPaste() and + OnObjectLoad(), added debugger control + +EMU48.H +- added defines for conditional gray scale compiling +- removed declaration of bDbgEnable +- extern declaration of global variables and functions + +EMU48.RC +- changed version and copyright + +ENGINE.C +- removed global variable bDbgEnable, replaced by a state in the + nDbgState variable +- added new functions SuspendDebugger() and ResumeDebugger() for + debugger control +- changed functions Debugger(), WaitForSleepState(), SwitchToState() + and WorkerThread(), added debugger control instead of general + debugger switching off +- changed function CheckSerial(), removed interrupt handling and + made function public +- changed function SwitchToState() and WorkerThread(), replaced + bDbgEnable with nDbgState access +- changed function WorkerThread(), main loop don't check status of + serial port any more, serial port must be initialized before + entering the RUN state now, added start/stop VBL counter at + entering/leaving the RUN state + +FILES.C +- changed function WriteStack(), minor optimization + +MOPS.C +- removed variable ioc_acc +- moved variable byVblRef and the functions F4096Hz() and + GetLineCounter() to DISPLAY.C +- changed function CpuReset(), must close serial port here +- changed function WriteIO(), control of the BITOFFSET (0x100) + register calls now the functions StartDisplay() and StopDisplay() + to modify the VBL counter reference +- changed function WriteIO(), changing the IOC (0x110) register + must also update the serial port status, because this isn't + handled in the CPU main loop any more + +TIMER.C +- changed function RescheduleT2(), calculate now ceil of time delay + to reduce checking of timer2 value without timer2 overflow + + +Service Pack 28 for Emu48 Version 1.0 + +DEBUGGER.C +- bugfix in table MemMap[], NCE3 reference in HP39/40 part was wrong +- bugfix in function SetMappingMenu(), used wrong variable type for + UNICODE mode +- changed function UpdateMiscWnd(), removed trailing spaces in text +- new functions OnStackPush(), OnStackPop() and OnStackModify() and + other changes for implementation of the stack context menu +- changed function OnFindOK(), minor optimizations +- changed function OnNewValue(), now returns exit condition + +DISPLAY.C +- removed usage of Chipset.dispon variable + +EMU48.C +- bugfix in function SetCommList(), detection of COM ports larger + than COM9 failed +- moved shutdown of CPU emulation thread from the WinMain() to the + OnDestroy() function to avoid trouble with closed window DC + +EMU48.DSP +- added configuration "Release Unicode" +- added configuration "Debug Unicode" + +EMU48.RC +- added "Stack" menu entries +- added accelerator key in "Find" dialog +- changed version and copyright + +I28F160.C +- bugfix in table byQueryTab[], content of not specified offset 0x03 + is 0x02; offset 0x1B and 0x1D contain 0x30 in the real chip +- changed function WrStateE8N(), WrStateE8D() and WrState60D(), use + device specific data from query table instead of program constants +- bugfix in function WrStateE8C(), don't set the BWSLBS bit in the + status register on trying to set a 0 bit +- changed function WrState40D(), removed useless increment +- bugfix in function WrState30C(), don't set the ECLBS bit in the + status register when trying to erase a lock bit with WP# = low +- changed function WrState20C() and WrState30C(), use device + dimensions from query table now and optimized data filling part +- bugfix in function WrState60D(), the BWSLBS or the ECLBS bit in + the status register is set when trying to modify lock bits with + WP# = low +- bugfix in function RdStateId() and RdStateQuery(), Flash commands + "Read Identifier Codes" and "Read Query" now behave on illegal + address access like the original calculator + +RESOURCE.H +- added definitions + +SERIAL.C +- bugfix in function SerialThread(), added implementation of the RER + bit in the RCS (0x111) register for receiver BREAK condition +- bugfix in function CommOpen(), opening COM ports larger than COM9 + failed +- bugfix in function CommTxBRK(), added LPB (Loop Back) emulation + + +Service Pack 27 for Emu48 Version 1.0 + +DEBUGGER.C +- various changes in many functions for implementation of the module + memory view +- changed function CreateToolbar(), replaced InitCommonControlsEx() + with InitCommonControls() function call to be compatible with the + project version 0x0200 + +EMU48.C +- changed function Disasm(), can use value of external port2 size + directly now + +EMU48.RC +- added "Mapping" menu entries +- changed version + +FILES.C +- changed function MapPort2(), don't limit port2 to 128KB here + +I28F160.C +- bugfix in function WrStateE8N(), a "Write to Buffer" command + failed when requesting for the buffer length + +KML.C +- added VOID as parameter in function prototypes +- replaced structure names changed in KML.H into the new ones + +KML.H +- changed structure names Token to KmlToken, Line to KmlLine, Block + to KmlBlock, Button to KmlButton and Annunciator to KmlAnnunciator + because the Line and Button definition clash with the MacOS API + +MOPS.C +- changed function MapP2(), limit port2 mapping size to 128KB here + +OPCODES.C +- changed function o138(), o139(), o13A(), o13B(), o13C(), o13D(), + o13E(), o13F(), o19d2(), o1Ad4(), o1Dd2() and o1Ed4(), added big + endian machine support (use define BIGENDIAN) + +PCH.H +- added _WIN32_IE definition for project version + +RESOURCE.H +- added definitions + + +Service Pack 26 for Emu48 Version 1.0 + +DEBUGGER.C +- bugfix in function ViewMemWnd(), wrong ASCII view of characters in + text area, strings are saved in LSB, MSB order +- changed function Debugger(), save and restore debugger window + position in session now +- bugfix in function Debugger(), missing initialization of menu + entry CODE Object Breakpoints +- bugfix in function OnFindOK(), search for ASCII character in + LSB, MSB order + +DISPLAY.C +- bugfix in function ResizeWindow(), 'Offset' command in KML script + section 'Background' hasn't worked + +EMU48.C +- added Critical Section initialization +- bugfix in function OnPaint(), 'Offset' command in KML script + section 'Background' hasn't worked + +EMU48.H +- extern declaration of global variable and function + +EMU48.RC +- changed version + +FILES.C +- changed functions ResetDocument(), ResetBackup() and + InitializeOFN(), replaced function FillMemory() with ZeroMemory() +- changed function OpenDocument(), can handle various Chipset sizes + now + +KML.C +- bugfix in function DrawButton() and ClipButton, 'Offset' command + in KML script section 'Background' hasn't worked + +MOPS.C +- bugfix function WriteIO(), when writing to the IOC (0x110) + register a possible USRQ interrupt wasn't generated +- changed function WriteIO(), writing the RER bit of the RCS (0x111) + register and writing the TBF bit of the TBR (0x116,0x117) register + is thread save now +- bugfix function WriteIO(), added implementation of the BRK bit in + the TCS (0x112) register +- changed function WriteIO(), removed NINT2 line update at writing + TBR (0x116,0x117) register, is doing in function CommTransmit() + +PCH.H +- added include ctype.h + +SERIAL.C +- minimized size of PC receive buffer +- bugfix in function SerialThread(), CommOpen(), CommClose() and + CommTransmit(), added implementation of the RER bit (only for + framing errors) in the RCS (0x111) register and of the TBF and TBZ + bit in the TCS (0x112) register +- new function CommTxBRK(), handle BREAK condition on Tx line + + Service Pack 25 for Emu48 Version 1.0 DDESERV.C diff --git a/sources/Emu48/DDESERV.C b/sources/Emu48/DDESERV.C index c376972..7d36a58 100644 --- a/sources/Emu48/DDESERV.C +++ b/sources/Emu48/DDESERV.C @@ -8,6 +8,7 @@ */ #include "pch.h" #include "Emu48.h" +#include "io.h" HDDEDATA CALLBACK DdeCallback(UINT iType,UINT iFmt,HCONV hConv, HSZ hsz1,HSZ hsz2,HDDEDATA hData, @@ -40,7 +41,7 @@ HDDEDATA CALLBACK DdeCallback(UINT iType,UINT iFmt,HCONV hConv, if ((lpData = (LPBYTE) LocalAlloc(LMEM_FIXED,dwSize * 2)) == NULL) return (HDDEDATA) DDE_FNOTPROCESSED; - if (!Chipset.dispon) // HP off + if (!(Chipset.IORam[BITOFFSET]&DON)) // HP off { // turn on HP KeyboardEvent(TRUE,0,0x8000); diff --git a/sources/Emu48/DEBUGGER.C b/sources/Emu48/DEBUGGER.C index 9fd54e9..0c6edc4 100644 --- a/sources/Emu48/DEBUGGER.C +++ b/sources/Emu48/DEBUGGER.C @@ -43,7 +43,29 @@ typedef struct CToolBarData WORD aItems[1]; } CToolBarData; -static CONST int nCol[] = +typedef struct // type of breakpoint table +{ + BOOL bEnable; // breakpoint enabled + UINT nType; // breakpoint type + DWORD dwAddr; // breakpoint address +} BP_T; + +typedef struct // type of model memory mapping +{ + CONST BYTE byType; // calculator type + CONST LPBYTE *ppbyNCE1; // NCE1 data + CONST DWORD *pdwNCE1Size; // NCE1 size + CONST LPBYTE *ppbyNCE2; // NCE2 data + CONST DWORD *pdwNCE2Size; // NCE2 size + CONST LPBYTE *ppbyCE1; // CE1 data + CONST DWORD *pdwCE1Size; // CE1 size + CONST LPBYTE *ppbyCE2; // CE2 data + CONST DWORD *pdwCE2Size; // CE2 size + CONST LPBYTE *ppbyNCE3; // NCE3 data + CONST DWORD *pdwNCE3Size; // NCE3 size +} MODEL_MAP_T; + +static CONST int nCol[] = { IDC_DEBUG_MEM_COL0, IDC_DEBUG_MEM_COL1, IDC_DEBUG_MEM_COL2, IDC_DEBUG_MEM_COL3, IDC_DEBUG_MEM_COL4, IDC_DEBUG_MEM_COL5, IDC_DEBUG_MEM_COL6, IDC_DEBUG_MEM_COL7 @@ -54,37 +76,102 @@ static CONST TCHAR cHex[] = { _T('0'),_T('1'),_T('2'),_T('3'), _T('8'),_T('9'),_T('A'),_T('B'), _T('C'),_T('D'),_T('E'),_T('F') }; -typedef struct // type of breakpoint table +static CONST LPBYTE pbyNoMEM = NULL; // no memory module + +static CONST MODEL_MAP_T MemMap[] = { - BOOL bEnable; // breakpoint enabled - UINT nType; // breakpoint type - DWORD dwAddr; // breakpoint address -} BP_T; + { + 0, // default + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL // nc. + }, + { + '6', // HP38G (64K) + &pbyRom, &dwRomSize, // ROM + &Chipset.Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL // nc. + }, + { + 'A', // HP38G + &pbyRom, &dwRomSize, // ROM + &Chipset.Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL, // nc. + &pbyNoMEM, NULL // nc. + }, + { + 'E', // HP39/40G + &pbyRom, &dwRomSize, // ROM + &Chipset.Port0, &Chipset.Port0Size, // RAM part 1 + &pbyNoMEM, NULL, // BS + &pbyNoMEM, NULL, // nc. + &Chipset.Port2, &Chipset.Port2Size // RAM part 2 + }, + { + 'G', // HP48GX + &pbyRom, &dwRomSize, // ROM + &Chipset.Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // BS + &Chipset.Port1, &Chipset.Port1Size, // Card slot 1 + &pbyPort2, &dwPort2Size // Card slot 2 + }, + { + 'S', // HP48SX + &pbyRom, &dwRomSize, // ROM + &Chipset.Port0, &Chipset.Port0Size, // RAM + &Chipset.Port1, &Chipset.Port1Size, // Card slot 1 + &pbyPort2, &dwPort2Size, // Card slot 2 + &pbyNoMEM, NULL // nc. + }, + { + 'X', // HP49G + &pbyRom, &dwRomSize, // Flash + &Chipset.Port0, &Chipset.Port0Size, // RAM + &pbyNoMEM, NULL, // BS + &Chipset.Port1, &Chipset.Port1Size, // Port 1 part 1 + &Chipset.Port2, &Chipset.Port2Size // Port 1 part 2 + } +}; -static WORD wBreakpointCount = 0; // number of breakpoints -static BP_T sBreakpoint[MAXBREAKPOINTS]; // breakpoint table +static INT nDbgPosX = 0; // position of debugger window +static INT nDbgPosY = 0; -static BOOL bRplBreak; // RPL breakpoint +static WORD wBreakpointCount = 0; // number of breakpoints +static BP_T sBreakpoint[MAXBREAKPOINTS]; // breakpoint table -static DWORD dwAdrLine[MAXCODELINES]; // addresses of disassember lines in code window -static DWORD dwAdrMem = 0; // start address of memory window +static BOOL bRplBreak; // RPL breakpoint -static LONG lCharWidth; // width of a character (is a fix font) +static DWORD dwAdrLine[MAXCODELINES]; // addresses of disassember lines in code window +static DWORD dwAdrMem = 0; // start address of memory window -static HMENU hMenuCode,hMenuMem; // handle of context menues -static HWND hWndToolbar; // toolbar handle +static UINT uIDMap = ID_DEBUG_MEM_MAP; // current memory view mode +static LPBYTE lbyMapData; // data +static DWORD dwMapDataSize; // data size -static CHIPSET OldChipset; // old chipset content -static BOOL bRegUpdate[REG_SIZE]; // register update table +static LONG lCharWidth; // width of a character (is a fix font) + +static HMENU hMenuCode,hMenuMem,hMenuStack;// handle of context menues +static HWND hWndToolbar; // toolbar handle + +static MODEL_MAP_T CONST *pMapping; // model specific memory mapping + +static CHIPSET OldChipset; // old chipset content +static BOOL bRegUpdate[REG_SIZE]; // register update table static HBITMAP hBmpCheckBox; // checked and unchecked bitmap // function prototypes static BOOL OnMemFind(HWND hDlg); -static VOID OnNewValue(LPTSTR lpszValue); +static INT OnNewValue(LPTSTR lpszValue); static VOID OnEnterAddress(HWND hDlg, DWORD *dwValue); static BOOL OnEditBreakpoint(HWND hDlg); static BOOL OnInfoIntr(HWND hDlg); +static BOOL OnInfoWoRegister(HWND hDlg); //################ //# @@ -105,6 +192,7 @@ static VOID DisableMenuKeys(HWND hDlg) EnableMenuItem(hMenu,ID_DEBUG_STEPOVER,MF_GRAYED); EnableMenuItem(hMenu,ID_DEBUG_STEPOUT,MF_GRAYED); EnableMenuItem(hMenu,ID_INFO_LASTINSTRUCTIONS,MF_GRAYED); + EnableMenuItem(hMenu,ID_INFO_WRITEONLYREG,MF_GRAYED); SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_RUN,MAKELONG((FALSE),0)); SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_BREAK,MAKELONG((TRUE),0)); @@ -115,6 +203,61 @@ static VOID DisableMenuKeys(HWND hDlg) return; } +// +// set mapping menu +// +static VOID SetMappingMenu(HWND hDlg,UINT uID) +{ + LPTSTR szCaption; + + CheckMenuItem(hMenuMem,uIDMap,MF_UNCHECKED); + + switch (uID) + { + case ID_DEBUG_MEM_MAP: + szCaption = _T("Memory"); + lbyMapData = NULL; // data + dwMapDataSize = 512; // data size + break; + case ID_DEBUG_MEM_NCE1: + szCaption = _T("Memory (NCE1)"); + lbyMapData = *pMapping->ppbyNCE1; + dwMapDataSize = *pMapping->pdwNCE1Size / 2048; // ROM size is always in nibbles + break; + case ID_DEBUG_MEM_NCE2: + szCaption = _T("Memory (NCE2)"); + lbyMapData = *pMapping->ppbyNCE2; + dwMapDataSize = *pMapping->pdwNCE2Size; + break; + case ID_DEBUG_MEM_CE1: + szCaption = _T("Memory (CE1)"); + lbyMapData = *pMapping->ppbyCE1; + dwMapDataSize = *pMapping->pdwCE1Size; + break; + case ID_DEBUG_MEM_CE2: + szCaption = _T("Memory (CE2)"); + lbyMapData = *pMapping->ppbyCE2; + dwMapDataSize = *pMapping->pdwCE2Size; + break; + case ID_DEBUG_MEM_NCE3: + szCaption = _T("Memory (NCE3)"); + lbyMapData = *pMapping->ppbyNCE3; + dwMapDataSize = *pMapping->pdwNCE3Size; + break; + default: _ASSERT(0); + } + + dwMapDataSize *= 2048; // size in nibble + + if (uIDMap != uID) dwAdrMem = 0; // view from address 0 + + uIDMap = uID; + CheckMenuItem(hMenuMem,uIDMap,MF_CHECKED); + + SetDlgItemText(hDlg,IDC_STATIC_MEMORY,szCaption); + return; +}; + // // set/reset breakpoint // @@ -155,6 +298,45 @@ static __inline VOID ToggleBreakpoint(DWORD dwAddr) return; } +// +// init memory mapping table and mapping menu entry +// +static __inline VOID InitMemMap(HWND hDlg) +{ + BOOL bActive; + INT i; + + pMapping = MemMap; // init default mapping + + // scan for all table entries + for (i = 0; i < ARRAYSIZEOF(MemMap); ++i) + { + if (MemMap[i].byType == cCurrentRomType) + { + pMapping = &MemMap[i]; // found entry + break; + } + } + + _ASSERT(hMenuMem); + + // enable menu mappings + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_NCE1,(*pMapping->ppbyNCE1) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_NCE2,(*pMapping->ppbyNCE2) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_CE1, (*pMapping->ppbyCE1) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_CE2, (*pMapping->ppbyCE2) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenuMem,ID_DEBUG_MEM_NCE3,(*pMapping->ppbyNCE3) ? MF_ENABLED : MF_GRAYED); + + bActive = (ID_DEBUG_MEM_NCE1 == uIDMap && *pMapping->ppbyNCE1 != NULL) + || (ID_DEBUG_MEM_NCE2 == uIDMap && *pMapping->ppbyNCE2 != NULL) + || (ID_DEBUG_MEM_CE1 == uIDMap && *pMapping->ppbyCE1 != NULL) + || (ID_DEBUG_MEM_CE2 == uIDMap && *pMapping->ppbyCE2 != NULL) + || (ID_DEBUG_MEM_NCE3 == uIDMap && *pMapping->ppbyNCE3 != NULL); + + SetMappingMenu(hDlg,bActive ? uIDMap : ID_DEBUG_MEM_MAP); + return; +} + // // init bank switcher area // @@ -171,7 +353,7 @@ static __inline VOID InitBsArea(HWND hDlg) // // convert nibble register to string -// +// static LPTSTR RegToStr(BYTE *pReg, WORD wNib) { static TCHAR szBuffer[32]; @@ -187,11 +369,11 @@ static LPTSTR RegToStr(BYTE *pReg, WORD wNib) // // convert string to nibble register -// +// static VOID StrToReg(BYTE *pReg, WORD wNib, LPTSTR lpszValue) { int i,nValuelen; - + nValuelen = lstrlen(lpszValue); for (i = wNib - 1;i >= 0;--i) { @@ -202,7 +384,7 @@ static VOID StrToReg(BYTE *pReg, WORD wNib, LPTSTR lpszValue) else { // convert to number - pReg[i] = _totupper(*lpszValue) - _T('0'); + pReg[i] = _totupper(*lpszValue) - _T('0'); if (pReg[i] > 9) pReg[i] -= _T('A') - _T('9') - 1; ++lpszValue; } @@ -243,7 +425,7 @@ static VOID ViewMemWnd(HWND hDlg, DWORD dwAddress) INT i,j,k; TCHAR szBuffer[16],szItem[4]; - BYTE cChar = 0; + BYTE cChar; szItem[2] = 0; // end of string dwAdrMem = dwAddress; // save start address of memory window @@ -258,17 +440,35 @@ static VOID ViewMemWnd(HWND hDlg, DWORD dwAddress) { BYTE byLineData[MAXMEMITEMS]; - // fetch data line - Npeek(byLineData, dwAddress, MAXMEMITEMS); + if (ID_DEBUG_MEM_MAP == uIDMap) // mapped memory content + { + // fetch mapping data line + Npeek(byLineData, dwAddress, MAXMEMITEMS); - wsprintf(szBuffer,_T("%05lX"),dwAddress); + wsprintf(szBuffer,_T("%05lX"),dwAddress); + } + else // module memory content + { + INT i; + + _ASSERT(lbyMapData); // valid module + + // fetch modul data line + for (i = 0; i < MAXMEMITEMS; ++i) + { + byLineData[i] = lbyMapData[(dwAddress + i) & (dwMapDataSize - 1)]; + } + + wsprintf(szBuffer,_T("%06lX"),dwAddress); + } SendDlgItemMessage(hDlg,IDC_DEBUG_MEM_ADDR,LB_ADDSTRING,0,(LPARAM) szBuffer); for (k = 0, j = 0; j < MAXMEMITEMS; ++j) { // read from fetched data line szItem[j&0x1] = cHex[byLineData[j]]; - cChar = (cChar << 4) | byLineData[j]; + // characters are saved in LBS, MSB order + cChar = (cChar >> 4) | (byLineData[j] << 4); if ((j&0x1) != 0) { @@ -282,7 +482,8 @@ static VOID ViewMemWnd(HWND hDlg, DWORD dwAddress) } szBuffer[j/2] = 0; // end of text string SendDlgItemMessage(hDlg,IDC_DEBUG_MEM_TEXT,LB_ADDSTRING,0,(LPARAM) szBuffer); - dwAddress = (dwAddress + MAXMEMITEMS) & 0xFFFFF; + + dwAddress = (dwAddress + MAXMEMITEMS) & (dwMapDataSize - 1); } return; #undef TEXTOFF @@ -327,7 +528,7 @@ static VOID UpdateCodeWnd(HWND hDlg) i = 10; // set cursor to actual pc } ViewCodeWnd(hWnd,dwAddress); // init code area - SendMessage(hWnd,LB_SETCURSEL,i,0); // set + SendMessage(hWnd,LB_SETCURSEL,i,0); // set return; } @@ -337,7 +538,7 @@ static VOID UpdateCodeWnd(HWND hDlg) static VOID UpdateRegisterWnd(HWND hDlg) { TCHAR szBuffer[64]; - + _ASSERTREG(IDC_REG_A); bRegUpdate[IDC_REG_A-REG_START] = memcmp(Chipset.A, OldChipset.A, sizeof(Chipset.A)) != 0; wsprintf(szBuffer,_T("A= %s"),RegToStr(Chipset.A,16)); @@ -563,6 +764,7 @@ VOID OnUpdate(HWND hDlg) EnableMenuItem(GetMenu(hDlg),ID_DEBUG_STEPOVER,MF_ENABLED); EnableMenuItem(GetMenu(hDlg),ID_DEBUG_STEPOUT,MF_ENABLED); EnableMenuItem(GetMenu(hDlg),ID_INFO_LASTINSTRUCTIONS,MF_ENABLED); + EnableMenuItem(GetMenu(hDlg),ID_INFO_WRITEONLYREG,MF_ENABLED); // enable toolbar buttons SendMessage(hWndToolbar,TB_ENABLEBUTTON,ID_DEBUG_RUN,MAKELONG((TRUE),0)); @@ -741,12 +943,12 @@ static BOOL OnKeyF11(HWND hDlg) static BOOL OnCodeGoAdr(HWND hDlg) { DWORD dwAddress = -1; // no address given - + OnEnterAddress(hDlg, &dwAddress); if (dwAddress != -1) { HWND hWnd = GetDlgItem(hDlg,IDC_DEBUG_CODE); - ViewCodeWnd(hWnd,dwAddress); + ViewCodeWnd(hWnd,dwAddress & 0xFFFFF); SendMessage(hWnd,LB_SETCURSEL,0,0); } return -1; // call windows default handler @@ -758,7 +960,7 @@ static BOOL OnCodeGoAdr(HWND hDlg) static BOOL OnCodeGoPC(HWND hDlg) { UpdateCodeWnd(hDlg); - return TRUE; + return 0; } // @@ -789,10 +991,10 @@ static BOOL OnMemGoDx(HWND hDlg, DWORD dwAddress) static BOOL OnMemGoAdr(HWND hDlg) { DWORD dwAddress = -1; // no address given - + OnEnterAddress(hDlg, &dwAddress); if (dwAddress != -1) // not Cancel key - OnMemGoDx(hDlg,dwAddress); + OnMemGoDx(hDlg,dwAddress & (dwMapDataSize - 1)); return -1; // call windows default handler } @@ -804,7 +1006,7 @@ static BOOL OnClearAll(HWND hDlg) wBreakpointCount = 0; // redraw code window InvalidateRect(GetDlgItem(hDlg,IDC_DEBUG_CODE),NULL,TRUE); - return TRUE; + return 0; } // @@ -814,7 +1016,85 @@ static BOOL OnToggleMenuItem(HWND hDlg,UINT uIDCheckItem,BOOL *bCheck) { *bCheck = !*bCheck; // toggle flag CheckMenuItem(GetMenu(hDlg),uIDCheckItem,*bCheck ? MF_CHECKED : MF_UNCHECKED); - return TRUE; + return 0; +} + +// +// change memory window mapping style +// +static BOOL OnMemMapping(HWND hDlg,UINT uID) +{ + if (uID == uIDMap) return -1; // same view, call windows default handler + + SetMappingMenu(hDlg,uID); // update menu settings + UpdateMemoryWnd(hDlg); // update memory window + return 0; +} + +// +// push value on hardware stack +// +static BOOL OnStackPush(HWND hDlg) +{ + TCHAR szBuffer[] = _T("00000"); + DWORD dwAddr; + + HWND hWnd = GetDlgItem(hDlg,IDC_DEBUG_STACK); + + if (nDbgState != DBG_STEPINTO) // not in single step mode + return TRUE; + + if (IDOK != OnNewValue(szBuffer)) // canceled function + return TRUE; + _stscanf(szBuffer,_T("%5X"),&dwAddr); + rstkpush(dwAddr); // push data + + UpdateStackWnd(hDlg); // update stack window + SendMessage(hWnd,LB_SETTOPINDEX,0,0); // top of listbox + SendMessage(hWnd,LB_SETCURSEL,0,0); // top selection + return 0; +} + +// +// pop value from hardware stack +// +static BOOL OnStackPop(HWND hDlg) +{ + HWND hWnd = GetDlgItem(hDlg,IDC_DEBUG_STACK); + + if (nDbgState != DBG_STEPINTO) // not in single step mode + return TRUE; + + rstkpop(); // pop data + + UpdateStackWnd(hDlg); // update stack window + SendMessage(hWnd,LB_SETTOPINDEX,0,0); // top of listbox + SendMessage(hWnd,LB_SETCURSEL,0,0); // top selection + return 0; +} + +// modify value on hardware stack +static BOOL OnStackModify(HWND hDlg) +{ + TCHAR szBuffer[16]; + HWND hWnd; + INT i; + + if (nDbgState != DBG_STEPINTO) // not in single step mode + return TRUE; + + hWnd = GetDlgItem(hDlg,IDC_DEBUG_STACK); + + i = SendMessage(hWnd,LB_GETCURSEL,0,0); + if (LB_ERR == i) return TRUE; // no selection + + SendMessage(hWnd,LB_GETTEXT,i,(LPARAM) szBuffer); + OnNewValue(&szBuffer[3]); + _stscanf(&szBuffer[3],_T("%5X"),&Chipset.rstk[(Chipset.rstkp-i-1)&7]); + + UpdateStackWnd(hDlg); // update stack window + SendMessage(hWnd,LB_SETCURSEL,i,0); // restore cursor postion + return 0; } // @@ -970,7 +1250,9 @@ static BOOL OnDblClick(HWND hWnd, WORD wId) if (nCol[i] == wId) // found ID break; - if (i == MEMWNDMAX) return FALSE; // not IDC_DEBUG_MEM window, default handler + // not IDC_DEBUG_MEM window or module mode -> default handler + if (i == MEMWNDMAX || ID_DEBUG_MEM_MAP != uIDMap) + return FALSE; // calculate address of byte dwAddress = i * 2; @@ -983,7 +1265,7 @@ static BOOL OnDblClick(HWND hWnd, WORD wId) _stscanf(szBuffer,_T("%2X"), &byData); byData = (byData >> 4) | (byData << 4); // change nibbles for writing - Write2(dwAddress, byData); // write data + Write2(dwAddress, byData); // write data UpdateMemoryWnd(GetParent(hWnd)); // update memory window SendMessage(hWnd,LB_SETCURSEL,i,0); return FALSE; @@ -1016,7 +1298,11 @@ static VOID OnContextMenu(HWND hDlg, LPARAM lParam, WPARAM wParam) case IDC_DEBUG_MEM_COL7: // handle memory window TrackPopupMenu(hMenuMem,0,pt.x,pt.y,0,hDlg,NULL); break; - } + + case IDC_DEBUG_STACK: // handle code window + TrackPopupMenu(hMenuStack,0,pt.x,pt.y,0,hDlg,NULL); + break; + } return; } @@ -1034,7 +1320,7 @@ static __inline BOOL OnKeyRightLeft(HWND hWnd, WPARAM wParam) HWND hWndNew; WORD wX, wY; INT nId; - + nId = GetDlgCtrlID(hWnd); // control ID of window for (wX = 0; wX < MEMWNDMAX; ++wX) // scan all Id's @@ -1052,7 +1338,7 @@ static __inline BOOL OnKeyRightLeft(HWND hWnd, WPARAM wParam) wX %= MEMWNDMAX; // set new focus - hWndNew = GetDlgItem(GetParent(hWnd),nCol[wX]); + hWndNew = GetDlgItem(GetParent(hWnd),nCol[wX]); SendMessage(hWndNew,LB_SETCURSEL,wY,0); SetFocus(hWndNew); return -2; @@ -1062,10 +1348,10 @@ static __inline BOOL OnKeyRightLeft(HWND hWnd, WPARAM wParam) // handle (page) up/down keys in memory window // static __inline BOOL OnKeyUpDown(HWND hWnd, WPARAM wParam) -{ +{ INT wX, wY; INT nId; - + nId = GetDlgCtrlID(hWnd); // control ID of window for (wX = 0; wX < MEMWNDMAX; ++wX) // scan all Id's @@ -1079,13 +1365,13 @@ static __inline BOOL OnKeyUpDown(HWND hWnd, WPARAM wParam) switch(LOWORD(wParam)) { case VK_NEXT: - dwAdrMem = (dwAdrMem + MAXMEMITEMS * MAXMEMLINES) & 0xFFFFF; + dwAdrMem = (dwAdrMem + MAXMEMITEMS * MAXMEMLINES) & (dwMapDataSize - 1); UpdateMemoryWnd(GetParent(hWnd)); SendMessage(hWnd,LB_SETCURSEL,wY,0); return -2; case VK_PRIOR: - dwAdrMem = (dwAdrMem - MAXMEMITEMS * MAXMEMLINES) & 0xFFFFF; + dwAdrMem = (dwAdrMem - MAXMEMITEMS * MAXMEMLINES) & (dwMapDataSize - 1); UpdateMemoryWnd(GetParent(hWnd)); SendMessage(hWnd,LB_SETCURSEL,wY,0); return -2; @@ -1093,17 +1379,17 @@ static __inline BOOL OnKeyUpDown(HWND hWnd, WPARAM wParam) case VK_DOWN: if (wY+1 >= MAXMEMLINES) { - dwAdrMem = (dwAdrMem + MAXMEMITEMS) & 0xFFFFF; + dwAdrMem = (dwAdrMem + MAXMEMITEMS) & (dwMapDataSize - 1); UpdateMemoryWnd(GetParent(hWnd)); SendMessage(hWnd,LB_SETCURSEL,wY,0); return -2; } break; - case VK_UP: + case VK_UP: if (wY == 0) { - dwAdrMem = (dwAdrMem - MAXMEMITEMS) & 0xFFFFF; + dwAdrMem = (dwAdrMem - MAXMEMITEMS) & (dwMapDataSize - 1); UpdateMemoryWnd(GetParent(hWnd)); SendMessage(hWnd,LB_SETCURSEL,wY,0); return -2; @@ -1218,7 +1504,7 @@ static __inline BOOL OnCtlColorStatic(HWND hWnd) int nId = GetDlgCtrlID(hWnd); if (nId >= REG_START && nId <= REG_STOP) // in register area bError = bRegUpdate[nId-REG_START]; // register changed? - return bError; + return bError; } @@ -1279,8 +1565,6 @@ VOID DisableDebugger(VOID) // static __inline HWND CreateToolbar(HWND hWnd) { - INITCOMMONCONTROLSEX icex; - HRSRC hRes; HGLOBAL hGlobal; CToolBarData *pData; @@ -1288,11 +1572,8 @@ static __inline HWND CreateToolbar(HWND hWnd) INT i,j; HWND hWndToolbar = NULL; // toolbar window - - // init common control DLL - icex.dwSize = sizeof(INITCOMMONCONTROLSEX); - icex.dwICC = ICC_BAR_CLASSES; - InitCommonControlsEx(&icex); + + InitCommonControls(); // ensure that common control DLL is loaded if ((hRes = FindResource(hApp,MAKEINTRESOURCE(IDR_DEBUG_TOOLBAR),RT_TOOLBAR)) == NULL) goto quit; @@ -1334,7 +1615,7 @@ static __inline HWND CreateToolbar(HWND hWnd) sizeof(TBBUTTON)); LocalFree(ptbb); - + unlock: FreeResource(hGlobal); quit: @@ -1343,8 +1624,9 @@ quit: static BOOL CALLBACK Debugger(HWND hDlg, UINT message, DWORD wParam, LONG lParam) { - static HMENU hMenuMainCode,hMenuMainMem; + static HMENU hMenuMainCode,hMenuMainMem,hMenuMainStack; + WINDOWPLACEMENT wndpl; TEXTMETRIC tm; HDC hDC; HFONT hFont; @@ -1353,12 +1635,13 @@ static BOOL CALLBACK Debugger(HWND hDlg, UINT message, DWORD wParam, LONG lParam switch (message) { case WM_INITDIALOG: - _ASSERT(hWnd); + SetWindowLocation(hDlg,nDbgPosX,nDbgPosY); SetClassLong(hDlg,GCL_HICON,(LONG) LoadIcon(hApp,MAKEINTRESOURCE(IDI_EMU48))); hWndToolbar = CreateToolbar(hDlg); // add toolbar - CheckMenuItem(GetMenu(hDlg),ID_BREAKPOINTS_NOP3,bDbgNOP3 ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(GetMenu(hDlg),ID_BREAKPOINTS_RPL, bDbgRPL ? MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(GetMenu(hDlg),ID_INTR_STEPOVERINT,bDbgSkipInt ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(GetMenu(hDlg),ID_BREAKPOINTS_NOP3, bDbgNOP3 ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(GetMenu(hDlg),ID_BREAKPOINTS_DOCODE,bDbgCode ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(GetMenu(hDlg),ID_BREAKPOINTS_RPL, bDbgRPL ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(GetMenu(hDlg),ID_INTR_STEPOVERINT, bDbgSkipInt ? MF_CHECKED : MF_UNCHECKED); hDlgDebug = hDlg; // handle for debugger dialog hEventDebug = CreateEvent(NULL,FALSE,FALSE,NULL); if (hEventDebug == NULL) @@ -1374,6 +1657,10 @@ static BOOL CALLBACK Debugger(HWND hDlg, UINT message, DWORD wParam, LONG lParam _ASSERT(hMenuMainMem); hMenuMem = GetSubMenu(hMenuMainMem, 0); _ASSERT(hMenuMem); + hMenuMainStack = LoadMenu(hApp,MAKEINTRESOURCE(IDR_DEBUG_STACK)); + _ASSERT(hMenuMainStack); + hMenuStack = GetSubMenu(hMenuMainStack, 0); + _ASSERT(hMenuStack); // font settings SendDlgItemMessage(hDlg,IDC_STATIC_CODE, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT),MAKELPARAM(FALSE,0)); @@ -1388,6 +1675,7 @@ static BOOL CALLBACK Debugger(HWND hDlg, UINT message, DWORD wParam, LONG lParam wInstrSize = INSTRSIZE; // size of last instruction array wInstrWp = wInstrRp = 0; // write/read pointer + InitMemMap(hDlg); // init memory mapping table InitBsArea(hDlg); // init bank switcher list box disassembler_map = MEM_MAP; // disassemble with mapped modules @@ -1395,7 +1683,6 @@ static BOOL CALLBACK Debugger(HWND hDlg, UINT message, DWORD wParam, LONG lParam dwDbgStopPC = -1; // no stop address for goto cursor dwDbgRplPC = -1; // no stop address for RPL breakpoint - bDbgEnable = TRUE; // debugger active nDbgState = DBG_STEPINTO; // state "step into" if (Chipset.Shutdn) // cpu thread stopped SetEvent(hEventShutdn); // goto debug session @@ -1406,8 +1693,7 @@ static BOOL CALLBACK Debugger(HWND hDlg, UINT message, DWORD wParam, LONG lParam case WM_DESTROY: // SetHP48Time(); // update time & date - bDbgEnable = FALSE; // debugger inactive - nDbgState = DBG_RUN; // state of debugger + nDbgState = DBG_OFF; // debugger inactive bInterrupt = TRUE; // exit opcode loop SetEvent(hEventDebug); if (pdwInstrArray) // free last instruction circular buffer @@ -1416,11 +1702,17 @@ static BOOL CALLBACK Debugger(HWND hDlg, UINT message, DWORD wParam, LONG lParam pdwInstrArray = NULL; } CloseHandle(hEventDebug); + wndpl.length = sizeof(wndpl); // save debugger window position + GetWindowPlacement(hDlg, &wndpl); + nDbgPosX = wndpl.rcNormalPosition.left; + nDbgPosY = wndpl.rcNormalPosition.top; DestroyMenu(hMenuMainCode); DestroyMenu(hMenuMainMem); + DestroyMenu(hMenuMainStack); + DestroyMenu(hMenuStack); _ASSERT(hWnd); UpdateWindowStatus(); // enable application menu items - hDlgDebug = NULL; // debugger windows closed + hDlgDebug = NULL; // debugger windows closed break; case WM_CLOSE: @@ -1465,6 +1757,7 @@ static BOOL CALLBACK Debugger(HWND hDlg, UINT message, DWORD wParam, LONG lParam case ID_BREAKPOINTS_DOCODE: return OnToggleMenuItem(hDlg,LOWORD(wParam),&bDbgCode); case ID_BREAKPOINTS_RPL: return OnToggleMenuItem(hDlg,LOWORD(wParam),&bDbgRPL); case ID_INFO_LASTINSTRUCTIONS: return OnInfoIntr(hDlg); + case ID_INFO_WRITEONLYREG: return OnInfoWoRegister(hDlg); case ID_INTR_STEPOVERINT: return OnToggleMenuItem(hDlg,LOWORD(wParam),&bDbgSkipInt); case ID_DEBUG_MEM_GOADR: return OnMemGoAdr(hDlg); case ID_DEBUG_MEM_GOPC: return OnMemGoDx(hDlg,Chipset.pc); @@ -1472,6 +1765,15 @@ static BOOL CALLBACK Debugger(HWND hDlg, UINT message, DWORD wParam, LONG lParam case ID_DEBUG_MEM_GOD1: return OnMemGoDx(hDlg,Chipset.d1); case ID_DEBUG_MEM_GOSTACK: return OnMemGoDx(hDlg,Chipset.rstk[(Chipset.rstkp-1)&7]); case ID_DEBUG_MEM_FIND: return OnMemFind(hDlg); + case ID_DEBUG_MEM_MAP: + case ID_DEBUG_MEM_NCE1: + case ID_DEBUG_MEM_NCE2: + case ID_DEBUG_MEM_CE1: + case ID_DEBUG_MEM_CE2: + case ID_DEBUG_MEM_NCE3: return OnMemMapping(hDlg,LOWORD(wParam)); + case ID_DEBUG_STACK_PUSH: return OnStackPush(hDlg); + case ID_DEBUG_STACK_POP: return OnStackPop(hDlg); + case ID_DEBUG_STACK_MODIFY: return OnStackModify(hDlg); case ID_DEBUG_CANCEL: DestroyWindow(hDlg); return TRUE; } @@ -1508,7 +1810,7 @@ static BOOL CALLBACK Debugger(HWND hDlg, UINT message, DWORD wParam, LONG lParam { case _T('G'): return OnMemGoAdr(GetParent((HWND) lParam)); case _T('F'): return OnMemFind(GetParent((HWND) lParam)); - case VK_RIGHT: + case VK_RIGHT: case VK_LEFT: return OnKeyRightLeft((HWND) lParam, wParam); case VK_NEXT: case VK_PRIOR: @@ -1540,7 +1842,7 @@ static BOOL CALLBACK Debugger(HWND hDlg, UINT message, DWORD wParam, LONG lParam // tooltip for toolbar if(((LPNMHDR) lParam)->code == TTN_GETDISPINFO) { - ((LPTOOLTIPTEXT) lParam)->hinst = hApp; + ((LPTOOLTIPTEXT) lParam)->hinst = hApp; ((LPTOOLTIPTEXT) lParam)->lpszText = MAKEINTRESOURCE(((LPTOOLTIPTEXT) lParam)->hdr.idFrom); break; } @@ -1595,16 +1897,14 @@ static __inline BOOL OnFindOK(HWND hDlg,BOOL bASCII,DWORD *pdwAddrLast) HWND hWnd = GetDlgItem(hDlg,IDC_FIND_DATA); - i = GetWindowTextLength(hWnd); // text length - - j = bASCII ? 2 : 1; // buffer width - if (j < sizeof(*(LPTSTR)0)) j = sizeof(*(LPTSTR)0); + i = GetWindowTextLength(hWnd) + 1; // text length incl. EOS + j = bASCII ? 2 : sizeof(*(LPTSTR)0); // buffer width // allocate search buffer - if ((lpbySearch = LocalAlloc(GMEM_FIXED,(i+1) * j)) != NULL) + if ((lpbySearch = LocalAlloc(GMEM_FIXED,i * j)) != NULL) { // get search text and real length - i = GetWindowText(hWnd,(LPTSTR) lpbySearch,(i+1)); + i = GetWindowText(hWnd,(LPTSTR) lpbySearch,i); // add string to combo box if (SendMessage(hWnd,CB_FINDSTRINGEXACT,0,(LPARAM) lpbySearch) == CB_ERR) @@ -1630,9 +1930,9 @@ static __inline BOOL OnFindOK(HWND hDlg,BOOL bASCII,DWORD *pdwAddrLast) // convert ASCII to number for (j = i - 1; j >= 0; --j) { - // order MSB, LSB - lpbySearch[j * 2 + 1] = lpbySearch[j] & 0xF; - lpbySearch[j * 2] = lpbySearch[j] >> 4; + // order LSB, MSB + lpbySearch[j * 2 + 1] = lpbySearch[j] >> 4; + lpbySearch[j * 2] = lpbySearch[j] & 0xF; } i *= 2; // no. of nibbles } @@ -1663,19 +1963,26 @@ static __inline BOOL OnFindOK(HWND hDlg,BOOL bASCII,DWORD *pdwAddrLast) if (*pdwAddrLast == dwAddr) ++dwAddr; - // scan 1M Nibble until match - for (; i && !bMatch && dwAddr <= dwAdrMem + 0x100000; ++dwAddr) + // scan mapping/module until match + for (; i && !bMatch && dwAddr <= dwAdrMem + dwMapDataSize; ++dwAddr) { BYTE byC; // i = no. of nibbles that have to match - for (bMatch = TRUE, j = 0;bMatch && j < i; ++j) + for (bMatch = TRUE, j = 0;bMatch && j < i; ++j) { - Npeek(&byC,(dwAddr + j) & 0xFFFFF,1); + if (ID_DEBUG_MEM_MAP == uIDMap) // mapped memory content + { + Npeek(&byC,(dwAddr + j) & 0xFFFFF,1); + } + else // module memory content + { + byC = lbyMapData[(dwAddr + j) & (dwMapDataSize - 1)]; + } bMatch = (byC == lpbySearch[j]); } } - dwAddr = (dwAddr - 1) & 0xFFFFF; // possible matching address + dwAddr = (dwAddr - 1) & (dwMapDataSize - 1); // possible matching address LocalFree(lpbySearch); // check match result @@ -1740,7 +2047,6 @@ static BOOL OnMemFind(HWND hDlg) return -1; // call windows default handler } - //################ //# //# New Value dialog box @@ -1795,10 +2101,18 @@ static BOOL CALLBACK NewValue(HWND hDlg, UINT message, DWORD wParam, LONG lParam UNREFERENCED_PARAMETER(wParam); } -static VOID OnNewValue(LPTSTR lpszValue) +static INT OnNewValue(LPTSTR lpszValue) { - if (DialogBoxParam(hApp, MAKEINTRESOURCE(IDD_NEWVALUE), hDlgDebug, (DLGPROC)NewValue, (LPARAM)lpszValue) == -1) + INT nResult; + + if ((nResult = DialogBoxParam(hApp, + MAKEINTRESOURCE(IDD_NEWVALUE), + hDlgDebug, + (DLGPROC)NewValue, + (LPARAM)lpszValue) + ) == -1) AbortMessage(_T("Input Dialog Box Creation Error !")); + return nResult; } @@ -1818,7 +2132,7 @@ static BOOL CALLBACK EnterAddr(HWND hDlg, UINT message, DWORD wParam, LONG lPara HWND hWnd; TCHAR szBuffer[8]; LONG i; - + switch (message) { case WM_INITDIALOG: @@ -1840,7 +2154,7 @@ static BOOL CALLBACK EnterAddr(HWND hDlg, UINT message, DWORD wParam, LONG lPara return FALSE; } } - if (*szBuffer) _stscanf(szBuffer,_T("%5X"),dwAddress); + if (*szBuffer) _stscanf(szBuffer,_T("%6X"),dwAddress); // no break case IDCANCEL: EndDialog(hDlg,wParam); @@ -2209,7 +2523,7 @@ static BOOL OnEditBreakpoint(HWND hDlg) AbortMessage(_T("Edit Breakpoint Dialog Box Creation Error !")); // update code window - InvalidateRect(GetDlgItem(hDlg,IDC_DEBUG_CODE),NULL,TRUE); + InvalidateRect(GetDlgItem(hDlg,IDC_DEBUG_CODE),NULL,TRUE); return -1; } @@ -2269,7 +2583,6 @@ static BOOL CALLBACK InfoIntr(HWND hDlg, UINT message, DWORD wParam, LONG lParam } } return FALSE; - UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); } @@ -2277,7 +2590,44 @@ static BOOL OnInfoIntr(HWND hDlg) { if (DialogBox(hApp, MAKEINTRESOURCE(IDD_INSTRUCTIONS), hDlg, (DLGPROC)InfoIntr) == -1) AbortMessage(_T("Last Instructions Dialog Box Creation Error !")); - return TRUE; + return 0; +} + +// +// view write only I/O registers +// +static BOOL CALLBACK InfoWoRegister(HWND hDlg, UINT message, DWORD wParam, LONG lParam) +{ + TCHAR szBuffer[8]; + + switch (message) + { + case WM_INITDIALOG: + wsprintf(szBuffer,_T("%05X"),Chipset.start1); + SetDlgItemText(hDlg,IDC_ADDR20_24,szBuffer); + wsprintf(szBuffer,_T("%03X"),Chipset.loffset); + SetDlgItemText(hDlg,IDC_ADDR25_27,szBuffer); + wsprintf(szBuffer,_T("%02X"),Chipset.lcounter); + SetDlgItemText(hDlg,IDC_ADDR28_29,szBuffer); + wsprintf(szBuffer,_T("%05X"),Chipset.start2); + SetDlgItemText(hDlg,IDC_ADDR30_34,szBuffer); + return TRUE; + case WM_COMMAND: + if ((wParam==IDOK)) + { + EndDialog(hDlg,wParam); + return TRUE; + } + } + return FALSE; + UNREFERENCED_PARAMETER(lParam); +} + +static BOOL OnInfoWoRegister(HWND hDlg) +{ + if (DialogBox(hApp, MAKEINTRESOURCE(IDD_WRITEONLYREG), hDlg, (DLGPROC)InfoWoRegister) == -1) + AbortMessage(_T("Write-Only Register Dialog Box Creation Error !")); + return 0; } diff --git a/sources/Emu48/DEBUGGER.H b/sources/Emu48/DEBUGGER.H index a38b6c9..fc1a90b 100644 --- a/sources/Emu48/DEBUGGER.H +++ b/sources/Emu48/DEBUGGER.H @@ -15,10 +15,12 @@ #define BP_ACCESS (BP_READ|BP_WRITE) // read/write memory breakpoint // debugger state definitions -#define DBG_RUN 0 -#define DBG_STEPINTO 1 -#define DBG_STEPOVER 2 -#define DBG_STEPOUT 3 +#define DBG_SUSPEND -1 +#define DBG_OFF 0 +#define DBG_RUN 1 +#define DBG_STEPINTO 2 +#define DBG_STEPOVER 3 +#define DBG_STEPOUT 4 // debugger.c extern BOOL CheckBreakpoint(DWORD dwAddr, DWORD wRange, UINT nType); diff --git a/sources/Emu48/DISPLAY.C b/sources/Emu48/DISPLAY.C index d5f39f8..f0de973 100644 --- a/sources/Emu48/DISPLAY.C +++ b/sources/Emu48/DISPLAY.C @@ -4,6 +4,7 @@ * This file is part of Emu48 * * Copyright (C) 1995 Sebastien Carlier + * Copyright (C) 2002 Christoph Gießelink * */ #include "pch.h" @@ -14,12 +15,27 @@ // #define DEBUG_DISPLAY // switch for DISPLAY debug purpose -#define LCD1_ROW 144 -#define LCD2_ROW 288 -#define LCD3_ROW 576 +#if defined GRAYSCALE + #define NOCOLORS 8 +#else + #define NOCOLORS 2 +#endif + +#define B 0x00000000 // black +#define W 0x00FFFFFF // white +#define I 0xFFFFFFFF // ignore + +#define LCD_ROW (36*4) // max. pixel per line // main display lines, handle zero lines exception -#define LINES(n) ((n == 0) ? 64 : (n+1)) +#define LINES(n) (((n) == 0) ? 64 : ((n)+1)) + +#define GRAYMASK (((((NOCOLORS)-1)>>1)<<24) \ + |((((NOCOLORS)-1)>>1)<<16) \ + |((((NOCOLORS)-1)>>1)<<8) \ + |((((NOCOLORS)-1)>>1))) + +#define DIBPIXEL(d,p) *(((DWORD*)(d))++) = ((*((DWORD*)(d)) & GRAYMASK) << 1) | (p) UINT nBackgroundX = 0; UINT nBackgroundY = 0; @@ -31,40 +47,33 @@ UINT nLcdDoubled = 1; LPBYTE pbyLcd; HDC hLcdDC = NULL; HDC hMainDC = NULL; + static HBITMAP hLcdBitmap; static HBITMAP hMainBitmap; static HBITMAP hOldLcdBitmap; static HBITMAP hOldMainBitmap; -#define B 0x00000000 -#define W 0x00FFFFFF -#define I 0xFFFFFFFF +static DWORD Pattern[16]; + +static DWORD dwKMLColor[64] = // color table loaded by KML script +{ + W,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B, + B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B, + I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, + I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I +}; + static struct { BITMAPINFOHEADER Lcd_bmih; - DWORD dwColor[64]; + RGBQUAD bmiColors[NOCOLORS]; } bmiLcd = { - {0x28,0/*x*/,0/*y*/,1,8,BI_RGB,0,0,0,64,0}, - { - W,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B, - B,B,B,B,B,B,B,B,B,B,B,B,B,B,B,B, - I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I, - I,I,I,I,I,I,I,I,I,I,I,I,I,I,I,I - } + {0x28,0/*x*/,0/*y*/,1,8,BI_RGB,0,0,0,NOCOLORS,0} }; -#undef B -#undef W -#undef I -static DWORD Pattern[16]; - -VOID UpdateContrast(BYTE byContrast) +static __inline VOID BuildPattern(VOID) { - DWORD c = byContrast; - DWORD b = byContrast + 0x20; - if (bmiLcd.dwColor[b] == 0xFFFFFFFF) b = 0; - _ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4); if (nLcdDoubled == 1) @@ -75,38 +84,65 @@ VOID UpdateContrast(BYTE byContrast) Pattern[i] = 0; for (j=8; j>0; j>>=1) { - Pattern[i] = (Pattern[i] << 8) | ((i&j) ? c : b); + Pattern[i] = (Pattern[i] << 8) | ((i&j) != 0); } } return; } - - c = (c<<8) | c; - b = (b<<8) | b; - if (nLcdDoubled == 2) { - Pattern[0] = (b<<16)|b; - Pattern[1] = (b<<16)|c; - Pattern[2] = (c<<16)|b; - Pattern[3] = (c<<16)|c; + Pattern[0] = 0x00000000; + Pattern[1] = 0x00000101; + Pattern[2] = 0x01010000; + Pattern[3] = 0x01010101; return; } - - c = (c<<16) | c; - b = (b<<16) | b; - if (nLcdDoubled == 4) { - Pattern[0] = b; - Pattern[1] = c; + Pattern[0] = 0x00000000; + Pattern[1] = 0x01010101; } return; } +VOID UpdateContrast(BYTE byContrast) +{ +#if defined GRAYSCALE + INT i; + + RGBQUAD c = *(RGBQUAD*)&dwKMLColor[byContrast]; // pixel on color + RGBQUAD b = *(RGBQUAD*)&dwKMLColor[byContrast+32]; // pixel off color + if ((Chipset.IORam[BITOFFSET] & DON) == 0 || I == *(DWORD*)&b) + b = *(RGBQUAD*)&dwKMLColor[0]; + + // fill color palette of bitmap + for (i = 0; i < ARRAYSIZEOF(bmiLcd.bmiColors); ++i) + { + const INT nCAdj[] = { 0, 1, 1, 2, 1, 2, 2, 3 }; + + _ASSERT(i < ARRAYSIZEOF(bmiLcd.bmiColors)); + _ASSERT(i < ARRAYSIZEOF(nCAdj)); + bmiLcd.bmiColors[i] = b; + bmiLcd.bmiColors[i].rgbRed += (((INT) c.rgbRed - (INT) b.rgbRed) * nCAdj[i] / 3); + bmiLcd.bmiColors[i].rgbGreen += (((INT) c.rgbGreen - (INT) b.rgbGreen) * nCAdj[i] / 3); + bmiLcd.bmiColors[i].rgbBlue += (((INT) c.rgbBlue - (INT) b.rgbBlue) * nCAdj[i] / 3); + } +#else + bmiLcd.bmiColors[1] = *(RGBQUAD*)&dwKMLColor[byContrast]; // pixel on color + bmiLcd.bmiColors[0] = *(RGBQUAD*)&dwKMLColor[byContrast+32]; // pixel off color + if ((Chipset.IORam[BITOFFSET] & DON) == 0 || I == *(DWORD*)&bmiLcd.bmiColors[0]) + bmiLcd.bmiColors[0] = *(RGBQUAD*)&dwKMLColor[0]; +#endif + + // update palette information + _ASSERT(hLcdDC); + SetDIBColorTable(hLcdDC,0,ARRAYSIZEOF(bmiLcd.bmiColors),bmiLcd.bmiColors); + return; +} + VOID SetLcdColor(UINT nId, UINT nRed, UINT nGreen, UINT nBlue) { - bmiLcd.dwColor[nId&0x3F] = ((nRed&0xFF)<<16)|((nGreen&0xFF)<<8)|(nBlue&0xFF); + dwKMLColor[nId&0x3F] = ((nRed&0xFF)<<16)|((nGreen&0xFF)<<8)|(nBlue&0xFF); return; } @@ -114,8 +150,9 @@ VOID CreateLcdBitmap(VOID) { // create LCD bitmap _ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4); - bmiLcd.Lcd_bmih.biWidth = LCD1_ROW * nLcdDoubled; + bmiLcd.Lcd_bmih.biWidth = LCD_ROW * nLcdDoubled; bmiLcd.Lcd_bmih.biHeight = -64 * nLcdDoubled; + _ASSERT(hLcdDC == NULL); hLcdDC = CreateCompatibleDC(hWindowDC); _ASSERT(hLcdDC != NULL); hLcdBitmap = CreateDIBSection(hLcdDC, (BITMAPINFO*)&bmiLcd,DIB_RGB_COLORS, (LPVOID*)&pbyLcd, NULL, 0); @@ -124,14 +161,17 @@ VOID CreateLcdBitmap(VOID) _ASSERT(hPalette != NULL); SelectPalette(hLcdDC, hPalette, FALSE); // set palette for LCD DC RealizePalette(hLcdDC); // realize palette + BuildPattern(); // build Nibble -> DIB mask pattern UpdateContrast(Chipset.contrast); + return; } VOID DestroyLcdBitmap(VOID) { - WORD i; - // clear background colors - for (i=32; i< 64; ++i) bmiLcd.dwColor[i] = 0xFFFFFFFF; + // set contrast palette to startup colors + WORD i = 0; dwKMLColor[i++] = W; + while(i < 32) dwKMLColor[i++] = B; + while(i < 64) dwKMLColor[i++] = I; if (hLcdDC != NULL) { @@ -191,31 +231,36 @@ VOID DestroyMainBitmap(VOID) VOID UpdateDisplayPointers(VOID) { - UINT nLines = LINES(Chipset.lcounter); + GRAYON(EnterCriticalSection(&csLcdLock)); + { + UINT nLines = LINES(Chipset.lcounter); - #if defined DEBUG_DISPLAY - { - TCHAR buffer[256]; - wsprintf(buffer,_T("%.5lx: Update Display Pointer\n"),Chipset.pc); - OutputDebugString(buffer); - } - #endif + #if defined DEBUG_DISPLAY + { + TCHAR buffer[256]; + wsprintf(buffer,_T("%.5lx: Update Display Pointer\n"),Chipset.pc); + OutputDebugString(buffer); + } + #endif - // calculate display width - Chipset.width = (34 + Chipset.loffset + (Chipset.boffset / 4) * 2) & 0xFFFFFFFE; - Chipset.end1 = Chipset.start1 + nLines * Chipset.width; - if (Chipset.end1 < Chipset.start1) - { - // calculate first address of main display - Chipset.start12 = Chipset.end1 - Chipset.width; - // calculate last address of main display - Chipset.end1 = Chipset.start1 - Chipset.width; + // calculate display width + Chipset.width = (34 + Chipset.loffset + (Chipset.boffset / 4) * 2) & 0xFFFFFFFE; + Chipset.end1 = Chipset.start1 + nLines * Chipset.width; + if (Chipset.end1 < Chipset.start1) + { + // calculate first address of main display + Chipset.start12 = Chipset.end1 - Chipset.width; + // calculate last address of main display + Chipset.end1 = Chipset.start1 - Chipset.width; + } + else + { + Chipset.start12 = Chipset.start1; + } + Chipset.end2 = Chipset.start2 + (64 - nLines) * 34; } - else - { - Chipset.start12 = Chipset.start1; - } - Chipset.end2 = Chipset.start2 + (64 - nLines) * 34; + GRAYON(LeaveCriticalSection(&csLcdLock)); + return; } static BYTE Buf[36]; @@ -235,10 +280,10 @@ VOID UpdateMainDisplay(VOID) #endif _ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4); - if (!Chipset.dispon) + if (!(Chipset.IORam[BITOFFSET]&DON)) { nLines = 64; - ZeroMemory(pbyLcd, LCD1_ROW * nLcdDoubled * nLines * nLcdDoubled); + ZeroMemory(pbyLcd, LCD_ROW * nLcdDoubled * nLines * nLcdDoubled); } else { @@ -250,15 +295,15 @@ VOID UpdateMainDisplay(VOID) Npeek(Buf,d,36); for (x=0; x<36; x++) { - *(((DWORD*)p)++)=Pattern[Buf[x]&1]; - *(((DWORD*)p)++)=Pattern[(Buf[x]>>1) & 1]; - *(((DWORD*)p)++)=Pattern[(Buf[x]>>2) & 1]; - *(((DWORD*)p)++)=Pattern[(Buf[x]>>3) & 1]; + DIBPIXEL(p,Pattern[Buf[x]&1]); + DIBPIXEL(p,Pattern[(Buf[x]>>1) & 1]); + DIBPIXEL(p,Pattern[(Buf[x]>>2) & 1]); + DIBPIXEL(p,Pattern[(Buf[x]>>3) & 1]); } - CopyMemory(p, p-LCD3_ROW, LCD3_ROW); - p+=LCD3_ROW; - CopyMemory(p, p-LCD3_ROW*2, LCD3_ROW*2); - p+=LCD3_ROW*2; + CopyMemory(p, p-LCD_ROW*4, LCD_ROW*4); + p+=LCD_ROW*4; + CopyMemory(p, p-LCD_ROW*8, LCD_ROW*8); + p+=LCD_ROW*8; d+=Chipset.width; } } @@ -269,11 +314,11 @@ VOID UpdateMainDisplay(VOID) Npeek(Buf,d,36); for (x=0; x<36; x++) { - *(((DWORD*)p)++)=Pattern[Buf[x]&3]; - *(((DWORD*)p)++)=Pattern[Buf[x]>>2]; + DIBPIXEL(p,Pattern[Buf[x]&3]); + DIBPIXEL(p,Pattern[Buf[x]>>2]); } - CopyMemory(p, p-LCD2_ROW, LCD2_ROW); - p+=LCD2_ROW; + CopyMemory(p, p-LCD_ROW*2, LCD_ROW*2); + p+=LCD_ROW*2; d+=Chipset.width; } } @@ -282,7 +327,10 @@ VOID UpdateMainDisplay(VOID) for (y = 0; y < nLines; ++y) { Npeek(Buf,d,36); - for (x=0; x<36; x++) *(((DWORD*)p)++)=Pattern[Buf[x]]; + for (x=0; x<36; x++) // every 4 pixel + { + DIBPIXEL(p,Pattern[Buf[x]]); + } d+=Chipset.width; } } @@ -313,12 +361,12 @@ VOID UpdateMenuDisplay(VOID) nLines = LINES(Chipset.lcounter); - if (!Chipset.dispon) return; + if (!(Chipset.IORam[BITOFFSET]&DON)) return; if (nLines == 64) return; // menu disabled _ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4); // calculate bitmap offset - p = pbyLcd + (nLines*nLcdDoubled*LCD1_ROW*nLcdDoubled); + p = pbyLcd + (nLines*nLcdDoubled*LCD_ROW*nLcdDoubled); if (nLcdDoubled == 4) { for (y = nLines; y < 64; ++y) @@ -326,15 +374,15 @@ VOID UpdateMenuDisplay(VOID) Npeek(Buf,d,34); // 34 nibbles are viewed for (x = 0; x < 36; ++x) { - *(((DWORD*)p)++)=Pattern[Buf[x]&1]; - *(((DWORD*)p)++)=Pattern[(Buf[x]>>1) & 1]; - *(((DWORD*)p)++)=Pattern[(Buf[x]>>2) & 1]; - *(((DWORD*)p)++)=Pattern[(Buf[x]>>3) & 1]; + DIBPIXEL(p,Pattern[Buf[x]&1]); + DIBPIXEL(p,Pattern[(Buf[x]>>1) & 1]); + DIBPIXEL(p,Pattern[(Buf[x]>>2) & 1]); + DIBPIXEL(p,Pattern[(Buf[x]>>3) & 1]); } - CopyMemory(p, p-LCD3_ROW, LCD3_ROW); - p+=LCD3_ROW; - CopyMemory(p, p-LCD3_ROW*2, LCD3_ROW*2); - p+=LCD3_ROW*2; + CopyMemory(p, p-LCD_ROW*4, LCD_ROW*4); + p+=LCD_ROW*4; + CopyMemory(p, p-LCD_ROW*8, LCD_ROW*8); + p+=LCD_ROW*8; d+=34; } } @@ -345,11 +393,11 @@ VOID UpdateMenuDisplay(VOID) Npeek(Buf,d,34); // 34 nibbles are viewed for (x = 0; x < 36; ++x) { - *(((DWORD*)p)++)=Pattern[Buf[x]&3]; - *(((DWORD*)p)++)=Pattern[Buf[x]>>2]; + DIBPIXEL(p,Pattern[Buf[x]&3]); + DIBPIXEL(p,Pattern[Buf[x]>>2]); } - CopyMemory(p, p-LCD2_ROW, LCD2_ROW); - p+=LCD2_ROW; + CopyMemory(p, p-LCD_ROW*2, LCD_ROW*2); + p+=LCD_ROW*2; d+=34; } } @@ -358,7 +406,10 @@ VOID UpdateMenuDisplay(VOID) for (y = nLines; y < 64; ++y) { Npeek(Buf,d,34); // 34 nibbles are viewed - for (x = 0; x < 36; ++x) *(((DWORD*)p)++)=Pattern[Buf[x]]; + for (x=0; x<36; x++) // every 4 pixel + { + DIBPIXEL(p,Pattern[Buf[x]]); + } d+=34; } } @@ -375,6 +426,7 @@ VOID UpdateMenuDisplay(VOID) VOID WriteToMainDisplay(LPBYTE a, DWORD d, UINT s) { +#if !defined GRAYSCALE INT x0, x; INT y0, y; DWORD *p; @@ -403,8 +455,8 @@ VOID WriteToMainDisplay(LPBYTE a, DWORD d, UINT s) _ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4); // calculate memory position in LCD bitmap - p = (DWORD*) (pbyLcd + y0*LCD1_ROW*nLcdDoubled*nLcdDoubled - + x0*sizeof(bmiLcd.dwColor[0])*nLcdDoubled); + p = (DWORD*) (pbyLcd + y0*LCD_ROW*nLcdDoubled*nLcdDoubled + + x0*sizeof(*p)*nLcdDoubled); while (s--) // loop for nibbles to write { @@ -437,9 +489,9 @@ VOID WriteToMainDisplay(LPBYTE a, DWORD d, UINT s) x = 0; // first coloumn ++y; // next row // recalculate bitmap memory position of new line - p = (DWORD*) (pbyLcd+y*LCD1_ROW*nLcdDoubled*nLcdDoubled); + p = (DWORD*) (pbyLcd+y*LCD_ROW*nLcdDoubled*nLcdDoubled); } - else + else p += nLcdDoubled; // next x position in bitmap } @@ -454,12 +506,12 @@ VOID WriteToMainDisplay(LPBYTE a, DWORD d, UINT s) else { x0 <<= 2; x <<= 2; // x-position in pixel - _ASSERT(x >= x0); // can't draw negative number of pixel + _ASSERT(x >= x0); // can't draw negative number of pixel x -= x0; // number of pixels to update x0 -= Chipset.boffset; // adjust x-position with left margin if (x0 < 0) x0 = 0; - + if (x0 > 131) x0 = 131; // cut right borders if (x+x0 > 131) x = 131 - x0; @@ -477,11 +529,13 @@ VOID WriteToMainDisplay(LPBYTE a, DWORD d, UINT s) GdiFlush(); } LeaveCriticalSection(&csGDILock); +#endif return; } VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s) { +#if !defined GRAYSCALE UINT x0, x; UINT y0, y; DWORD *p; @@ -504,7 +558,7 @@ VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s) _ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4); if (nLcdDoubled == 4) { - p = (DWORD*)(pbyLcd + y0*LCD3_ROW*4 + x0*16); + p = (DWORD*)(pbyLcd + y0*LCD_ROW*16 + x0*16); while (s--) { if (x<34) @@ -521,7 +575,7 @@ VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s) x=0; y++; if (y==64) break; - p=(DWORD*)(pbyLcd+y*LCD3_ROW*4); + p=(DWORD*)(pbyLcd+y*LCD_ROW*16); } else p+=4; } EnterCriticalSection(&csGDILock); // solving NT GDI problems @@ -544,7 +598,7 @@ VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s) } if (nLcdDoubled == 2) { - p = (DWORD*)(pbyLcd + y0*LCD2_ROW*2 + x0*8); + p = (DWORD*)(pbyLcd + y0*LCD_ROW*4 + x0*8); while (s--) { if (x<34) @@ -559,7 +613,7 @@ VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s) x=0; y++; if (y==64) break; - p=(DWORD*)(pbyLcd+y*LCD2_ROW*2); + p=(DWORD*)(pbyLcd+y*LCD_ROW*4); } else p+=2; } EnterCriticalSection(&csGDILock); // solving NT GDI problems @@ -582,7 +636,7 @@ VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s) } if (nLcdDoubled == 1) { - p = (DWORD*)(pbyLcd + y0*LCD1_ROW + x0*4); + p = (DWORD*)(pbyLcd + y0*LCD_ROW + x0*4); while (s--) { if (x<34) *p = Pattern[*a]; @@ -593,7 +647,7 @@ VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s) x=0; y++; if (y==64) break; - p=(DWORD*)(pbyLcd+y*LCD1_ROW); + p=(DWORD*)(pbyLcd+y*LCD_ROW); } else p++; } EnterCriticalSection(&csGDILock); // solving NT GDI problems @@ -612,13 +666,14 @@ VOID WriteToMenuDisplay(LPBYTE a, DWORD d, UINT s) } LeaveCriticalSection(&csGDILock); } +#endif return; } VOID UpdateAnnunciators(VOID) { BYTE c; - + c = (BYTE)(Chipset.IORam[ANNCTRL] | (Chipset.IORam[ANNCTRL+1]<<4)); // switch annunciators off if timer stopped if ((c & AON) == 0 || (Chipset.IORam[TIMER2_CTRL] & RUN) == 0) @@ -659,6 +714,131 @@ VOID ResizeWindow(VOID) rectWindow.bottom - rectWindow.top, SWP_NOMOVE | SWP_NOZORDER); } + + _ASSERT(hWindowDC); // move destination window + SetWindowOrgEx(hWindowDC, nBackgroundX, nBackgroundY, NULL); InvalidateRect(hWnd,NULL,TRUE); return; -} \ No newline at end of file +} + + +#if defined GRAYSCALE + +#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 VOID CALLBACK LcdProc(UINT uEventId, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2) +{ + EnterCriticalSection(&csLcdLock); + { + UpdateMainDisplay(); // update display + UpdateMenuDisplay(); + } + LeaveCriticalSection(&csLcdLock); + + QueryPerformanceCounter(&lLcdRef); // actual time + + return; + UNREFERENCED_PARAMETER(uEventId); + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(dwUser); + UNREFERENCED_PARAMETER(dw1); + UNREFERENCED_PARAMETER(dw2); +} + +// LCD line counter calculation +BYTE GetLineCounter(VOID) +{ + LARGE_INTEGER lLC; + BYTE byTime; + + if (uLcdTimerId == 0) // display off + return ((Chipset.IORam[LINECOUNT+1] & (LC5|LC4)) << 4) | Chipset.IORam[LINECOUNT]; + + QueryPerformanceCounter(&lLC); // get elapsed time since display update + + // elapsed ticks so far + byTime = (BYTE) (((lLC.QuadPart - lLcdRef.QuadPart) << 12) / lFreq.QuadPart); + + if (byTime > 0x3F) byTime = 0x3F; // all counts made + + return (0x7E - byTime) & 0x3F; // update display between VBL counter 0x3F-0x3E +} + +VOID StartDisplay(BYTE byInitial) +{ + if (uLcdTimerId) // LCD update timer running + return; // -> quit + + if (Chipset.IORam[BITOFFSET]&DON) // display on? + { + QueryPerformanceCounter(&lLcdRef); // actual time of top line + + // adjust startup counter to get the right VBL value + lLcdRef.QuadPart -= ((LONGLONG) ((0x7E - byInitial) & 0x3F) * lFreq.QuadPart) >> 12; + + uLcdTimerId = timeSetEvent(DISPLAY_FREQ,0,(LPTIMECALLBACK)&LcdProc,0,TIME_PERIODIC); + _ASSERT(uLcdTimerId); // test if display update timer started + } + return; +} + +VOID StopDisplay(VOID) +{ + BYTE a[2]; + ReadIO(a,LINECOUNT,2); // update VBL at display off time + + if (uLcdTimerId == 0) // timer stopped + return; // -> quit + + timeKillEvent(uLcdTimerId); // stop display update + uLcdTimerId = 0; // set flag display update stopped + + EnterCriticalSection(&csLcdLock); // update to last condition + { + UpdateMainDisplay(); // update display + UpdateMenuDisplay(); + } + LeaveCriticalSection(&csLcdLock); + return; +} + +#else + +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; +} + +BYTE GetLineCounter(VOID) // get line counter value +{ + _ASSERT(byVblRef < 0x40); + return (0x40 + F4096Hz() - byVblRef) & 0x3F; +} + +VOID StartDisplay(BYTE byInitial) +{ + // get positive VBL difference between now and stop time + byVblRef = (0x40 + F4096Hz() - byInitial) & 0x3F; + return; +} + +VOID StopDisplay(VOID) +{ + BYTE a[2]; + ReadIO(a,LINECOUNT,2); // update VBL at display off time + return; +} + +#endif diff --git a/sources/Emu48/EMU48.C b/sources/Emu48/EMU48.C index da600be..fdb6359 100644 --- a/sources/Emu48/EMU48.C +++ b/sources/Emu48/EMU48.C @@ -13,7 +13,7 @@ #include "kml.h" #include "debugger.h" -#define VERSION "1.25" +#define VERSION "1.30" #define CF_HPOBJ "CF_HPOBJ" // clipboard format for DDE #define MAXPORTS 16 // number of COM ports @@ -46,10 +46,12 @@ static const TCHAR szLicence[] = CRITICAL_SECTION csGDILock; // critical section for hWindowDC +GRAYON(CRITICAL_SECTION csLcdLock); // critical section for display update CRITICAL_SECTION csKeyLock; // critical section for key scan CRITICAL_SECTION csIOLock; // critical section for I/O access CRITICAL_SECTION csT1Lock; // critical section for timer1 access CRITICAL_SECTION csT2Lock; // critical section for timer2 access +CRITICAL_SECTION csTxdLock; // critical section for transmit byte CRITICAL_SECTION csRecvLock; // critical section for receive byte INT nArgc; // no. of command line arguments LPCTSTR *ppArgv; // command line arguments @@ -97,7 +99,7 @@ VOID SetWindowTitle(LPTSTR szString) return; } -VOID UpdateWindowStatus() +VOID UpdateWindowStatus(VOID) { if (hWnd) // window open { @@ -127,7 +129,7 @@ VOID UpdateWindowStatus() EnableMenuItem(hMenu,ID_BACKUP_DELETE,uBackup); EnableMenuItem(hMenu,ID_VIEW_SCRIPT,uRun); EnableMenuItem(hMenu,ID_TOOL_DISASM,uRun); - EnableMenuItem(hMenu,ID_TOOL_DEBUG,(bRun && !bDbgEnable) ? MF_ENABLED : MF_GRAYED); + EnableMenuItem(hMenu,ID_TOOL_DEBUG,(bRun && nDbgState == DBG_OFF) ? MF_ENABLED : MF_GRAYED); } return; } @@ -144,7 +146,7 @@ VOID CopyItemsToClipboard(HWND hWnd) // save selected Listbox Items to Clipboar { LONG i; LPINT lpnCount; - + // get number of selections if ((i = SendMessage(hWnd,LB_GETSELCOUNT,0,0)) == 0) return; // no items selected @@ -213,12 +215,12 @@ static VOID SetCommList(HWND hDlg,int nIDDlgItem,LPCTSTR szSetting) TCHAR szBuffer[16]; WPARAM wSelect = 0; // set select to disabled - SendDlgItemMessage(hDlg,nIDDlgItem,CB_ADDSTRING,0,(LPARAM) NO_SERIAL); + SendDlgItemMessage(hDlg,nIDDlgItem,CB_ADDSTRING,0,(LPARAM) _T(NO_SERIAL)); for (wCount = 1;wCount <= MAXPORTS;++wCount) { - wsprintf(szBuffer,_T("COM%u"),wCount); - if ((bAdd = (lstrcmp(szBuffer,szSetting) == 0))) + wsprintf(szBuffer,_T("\\\\.\\COM%u"),wCount); + if ((bAdd = (lstrcmp(&szBuffer[4],szSetting) == 0))) wSelect = wIndex; // test if COM port is valid @@ -232,7 +234,7 @@ static VOID SetCommList(HWND hDlg,int nIDDlgItem,LPCTSTR szSetting) if (bAdd) // add item to combobox { - SendDlgItemMessage(hDlg,nIDDlgItem,CB_ADDSTRING,0,(LPARAM) szBuffer); + SendDlgItemMessage(hDlg,nIDDlgItem,CB_ADDSTRING,0,(LPARAM) &szBuffer[4]); ++wIndex; } } @@ -254,7 +256,7 @@ static BOOL CALLBACK SettingsProc(HWND hDlg, UINT message, DWORD wParam, LONG lP CheckDlgButton(hDlg,(disassembler_mode == HP_MNEMONICS) ? IDC_DISASM_HP : IDC_DISASM_CLASS,BST_CHECKED); // set combobox parameter - SetCommList(hDlg,IDC_WIRE,szSerialWire); + SetCommList(hDlg,IDC_WIRE,szSerialWire); SetCommList(hDlg,IDC_IR,szSerialIr); if (bCommInit) // disable when port open { @@ -378,16 +380,16 @@ static BOOL CALLBACK SettingsProc(HWND hDlg, UINT message, DWORD wParam, LONG lP static UINT SaveChanges(BOOL bAuto) { UINT uReply; - + if (pbyRom == NULL) return IDNO; - + if (bAuto) uReply = IDYES; else uReply = YesNoCancelMessage(_T("Do you want to save changes ?")); - + if (uReply != IDYES) return uReply; - + if (szCurrentFilename[0]==0) { // Save As... uReply = GetSaveAsFilename(); @@ -396,7 +398,7 @@ static UINT SaveChanges(BOOL bAuto) WriteLastDocument(szCurrentFilename); return IDYES; } - + SaveDocument(); return IDYES; } @@ -415,10 +417,12 @@ static UINT SaveChanges(BOOL bAuto) static LRESULT OnCreate(HWND hWindow) { InitializeCriticalSection(&csGDILock); + GRAYON(InitializeCriticalSection(&csLcdLock)); InitializeCriticalSection(&csKeyLock); InitializeCriticalSection(&csIOLock); InitializeCriticalSection(&csT1Lock); InitializeCriticalSection(&csT2Lock); + InitializeCriticalSection(&csTxdLock); InitializeCriticalSection(&csRecvLock); hWnd = hWindow; @@ -433,16 +437,19 @@ static LRESULT OnCreate(HWND hWindow) static LRESULT OnDestroy(HWND hWindow) { DragAcceptFiles(hWnd,FALSE); // no WM_DROPFILES message any more - ReleaseDC(hWnd, hWindowDC); + if (hThread) SwitchToState(SM_RETURN); // exit emulation thread SetWindowTitle(NULL); // free memory of title + ReleaseDC(hWnd, hWindowDC); hWindowDC = NULL; // hWindowDC isn't valid any more hWnd = NULL; DeleteCriticalSection(&csGDILock); + GRAYON(DeleteCriticalSection(&csLcdLock)); DeleteCriticalSection(&csKeyLock); DeleteCriticalSection(&csIOLock); DeleteCriticalSection(&csT1Lock); DeleteCriticalSection(&csT2Lock); + DeleteCriticalSection(&csTxdLock); DeleteCriticalSection(&csRecvLock); #if defined _USRDLL // DLL version @@ -461,10 +468,16 @@ static LRESULT OnPaint(HWND hWindow) { PAINTSTRUCT Paint; HDC hPaintDC; - + hPaintDC = BeginPaint(hWindow, &Paint); if (hMainDC != NULL) { + RECT rcMainPaint = Paint.rcPaint; + rcMainPaint.left += nBackgroundX; // coordinates in source bitmap + rcMainPaint.top += nBackgroundY; + rcMainPaint.right += nBackgroundX; + rcMainPaint.bottom += nBackgroundY; + EnterCriticalSection(&csGDILock); // solving NT GDI problems { UINT nLines = (Chipset.lcounter == 0) ? 64 : (Chipset.lcounter + 1); @@ -472,7 +485,7 @@ static LRESULT OnPaint(HWND hWindow) // redraw background bitmap BitBlt(hPaintDC, Paint.rcPaint.left, Paint.rcPaint.top, Paint.rcPaint.right-Paint.rcPaint.left, Paint.rcPaint.bottom-Paint.rcPaint.top, - hMainDC, Paint.rcPaint.left, Paint.rcPaint.top, SRCCOPY); + hMainDC, rcMainPaint.left, rcMainPaint.top, SRCCOPY); // redraw main display area BitBlt(hWindowDC, nLcdX, nLcdY, 131*nLcdDoubled, nLines*nLcdDoubled, @@ -485,7 +498,7 @@ static LRESULT OnPaint(HWND hWindow) } LeaveCriticalSection(&csGDILock); UpdateAnnunciators(); - RefreshButtons(&Paint.rcPaint); + RefreshButtons(&rcMainPaint); } EndPaint(hWindow, &Paint); return 0; @@ -506,12 +519,17 @@ static LRESULT OnDropFiles(HANDLE hFilesInfo) // get number of files dropped wNumFiles = DragQueryFile (hFilesInfo,(UINT)-1,NULL,0); - if (!Chipset.dispon && !bDbgEnable) // calculator off, turn on + SuspendDebugger(); // suspend debugger + bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control + + // calculator off, turn on + if (!(Chipset.IORam[BITOFFSET]&DON)) { // turn on HP KeyboardEvent(TRUE,0,0x8000); Sleep(200); KeyboardEvent(FALSE,0,0x8000); + Sleep(200); } _ASSERT(nState == SM_RUN); // emulator must be in RUN state @@ -519,7 +537,7 @@ static LRESULT OnDropFiles(HANDLE hFilesInfo) { DragFinish (hFilesInfo); InfoMessage(_T("The emulator is busy.")); - return 0; + goto cancel; } _ASSERT(nState == SM_SLEEP); @@ -540,20 +558,24 @@ static LRESULT OnDropFiles(HANDLE hFilesInfo) _ASSERT(nState == SM_RUN); if (bSuccess == FALSE) // data not copied - return 0; + goto cancel; KeyboardEvent(TRUE,0,0x8000); Sleep(200); KeyboardEvent(FALSE,0,0x8000); // wait for sleep mode while(Chipset.Shutdn == FALSE) Sleep(0); + +cancel: + bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control + ResumeDebugger(); return 0; } // // ID_FILE_NEW // -static LRESULT OnFileNew() +static LRESULT OnFileNew(VOID) { SaveBackup(); if (pbyRom) @@ -572,7 +594,7 @@ cancel: // // ID_FILE_OPEN // -static LRESULT OnFileOpen() +static LRESULT OnFileOpen(VOID) { if (pbyRom) { @@ -592,7 +614,7 @@ cancel: // // ID_FILE_SAVE // -static LRESULT OnFileSave() +static LRESULT OnFileSave(VOID) { if (pbyRom == NULL) return 0; SwitchToState(SM_INVALID); @@ -604,13 +626,13 @@ static LRESULT OnFileSave() // // ID_FILE_SAVEAS // -static LRESULT OnFileSaveAs() +static LRESULT OnFileSaveAs(VOID) { UINT uReply; - + if (pbyRom == NULL) return 0; SwitchToState(SM_INVALID); - + uReply = GetSaveAsFilename(); if (uReply != IDOK) { @@ -623,7 +645,7 @@ static LRESULT OnFileSaveAs() return 0; } WriteLastDocument(szCurrentFilename); - + SwitchToState(SM_RUN); return 0; } @@ -631,12 +653,13 @@ static LRESULT OnFileSaveAs() // // ID_FILE_CLOSE // -static LRESULT OnFileClose() +static LRESULT OnFileClose(VOID) { if (pbyRom == NULL) return 0; SwitchToState(SM_INVALID); if (SaveChanges(bAutoSave)!=IDCANCEL) { + DisableDebugger(); KillKML(); ResetDocument(); SetWindowTitle(NULL); @@ -653,7 +676,7 @@ static LRESULT OnFileClose() // // WM_SYS_CLOSE // -static LRESULT OnFileExit() +static LRESULT OnFileExit(VOID) { SwitchToState(SM_INVALID); // hold emulation thread if (SaveChanges(bAutoSaveOnExit) == IDCANCEL) @@ -668,7 +691,7 @@ static LRESULT OnFileExit() // // ID_STACK_COPY // -static LRESULT OnStackCopy() // copy data from stack +static LRESULT OnStackCopy(VOID) // copy data from stack { HANDLE hClipObj; LPBYTE lpData; @@ -733,18 +756,23 @@ error: // // ID_STACK_PASTE // -static LRESULT OnStackPaste() // paste data to stack +static LRESULT OnStackPaste(VOID) // paste data to stack { HANDLE hClipObj; LPBYTE lpClipdata,lpData; BOOL bSuccess = FALSE; - if (!Chipset.dispon && !bDbgEnable) // calculator off, turn on + SuspendDebugger(); // suspend debugger + bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control + + // calculator off, turn on + if (!(Chipset.IORam[BITOFFSET]&DON)) { KeyboardEvent(TRUE,0,0x8000); Sleep(200); KeyboardEvent(FALSE,0,0x8000); + Sleep(200); // wait for sleep mode while(Chipset.Shutdn == FALSE) Sleep(0); } @@ -753,7 +781,7 @@ static LRESULT OnStackPaste() // paste data to stack if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state { InfoMessage(_T("The emulator is busy.")); - return 0; + goto cancel; } _ASSERT(nState == SM_SLEEP); @@ -790,20 +818,25 @@ static LRESULT OnStackPaste() // paste data to stack _ASSERT(nState == SM_RUN); if (bSuccess == FALSE) // data not copied - return 0; + goto cancel; KeyboardEvent(TRUE,0,0x8000); Sleep(200); KeyboardEvent(FALSE,0,0x8000); + // wait for sleep mode while(Chipset.Shutdn == FALSE) Sleep(0); + +cancel: + bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control + ResumeDebugger(); return 0; } // // ID_VIEW_COPY // -static LRESULT OnViewCopy() +static LRESULT OnViewCopy(VOID) { if (OpenClipboard(hWnd)) { @@ -827,21 +860,21 @@ static LRESULT OnViewCopy() hBmp = CreateCompatibleBitmap(hLcdDC,131*nLcdDoubled,64*nLcdDoubled); hBmpDC = CreateCompatibleDC(hLcdDC); hBmp = SelectObject(hBmpDC,hBmp); - BitBlt(hBmpDC, 0, 0, 131*nLcdDoubled, 64*nLcdDoubled, hLcdDC, 0, 0, SRCCOPY); + BitBlt(hBmpDC,0,0,131*nLcdDoubled,64*nLcdDoubled,hLcdDC,Chipset.boffset*nLcdDoubled,0,SRCCOPY); hBmp = SelectObject(hBmpDC,hBmp); // fill BITMAP structure for size information GetObject(hBmp, sizeof(bm), &bm); wBits = bm.bmPlanes * bm.bmBitsPixel; - // make sure bits per pixel is valid + // make sure bits per pixel is valid if (wBits <= 1) wBits = 1; else if (wBits <= 4) wBits = 4; else if (wBits <= 8) wBits = 8; - else // if greater than 8-bit, force to 24-bit + else // if greater than 8-bit, force to 24-bit wBits = 24; dwSizeImage = WIDTHBYTES((DWORD)bm.bmWidth * wBits) * bm.bmHeight; @@ -874,9 +907,9 @@ static LRESULT OnViewCopy() GetDIBits(hBmpDC, hBmp, 0, lpbi->biHeight, (LPBYTE)lpbi + dwLen - dwSizeImage, (LPBITMAPINFO)lpbi, DIB_RGB_COLORS); GlobalUnlock(hClipObj); - SetClipboardData(CF_DIB, hClipObj); + SetClipboardData(CF_DIB, hClipObj); - // get number of entries in the logical palette + // get number of entries in the logical palette GetObject(hPalette,sizeof(WORD),&wBits); // memory allocation for temporary palette data @@ -885,7 +918,7 @@ static LRESULT OnViewCopy() ppal->palVersion = PALVERSION; ppal->palNumEntries = wBits; GetPaletteEntries(hPalette, 0, wBits, ppal->palPalEntry); - SetClipboardData(CF_PALETTE, CreatePalette(ppal)); + SetClipboardData(CF_PALETTE, CreatePalette(ppal)); LocalFree(ppal); } } @@ -897,11 +930,12 @@ static LRESULT OnViewCopy() HBITMAP hOldBmp, hBmp; HDC hBmpDC; + // don't work with background index <> 0 _ASSERT(nLcdDoubled == 1 || nLcdDoubled == 2 || nLcdDoubled == 4); hBmp = CreateBitmap(131*nLcdDoubled,64*nLcdDoubled,1,1,NULL); hBmpDC = CreateCompatibleDC(NULL); hOldBmp = (HBITMAP)SelectObject(hBmpDC,hBmp); - BitBlt(hBmpDC, 0, 0, 131*nLcdDoubled, 64*nLcdDoubled, hLcdDC, 0, 0, SRCCOPY); + BitBlt(hBmpDC,0,0,131*nLcdDoubled,64*nLcdDoubled,hLcdDC,Chipset.boffset*nLcdDoubled,0,SRCCOPY); SetClipboardData(CF_BITMAP,hBmp); SelectObject(hBmpDC,hOldBmp); DeleteDC(hBmpDC); @@ -915,7 +949,7 @@ static LRESULT OnViewCopy() // // ID_VIEW_RESET // -static LRESULT OnViewReset() +static LRESULT OnViewReset(VOID) { if (nState != SM_RUN) return 0; if (YesNoMessage(_T("Are you sure you want to press the Reset Button ?"))==IDYES) @@ -930,7 +964,7 @@ static LRESULT OnViewReset() // // ID_VIEW_SETTINGS // -static LRESULT OnViewSettings() +static LRESULT OnViewSettings(VOID) { ReadSettings(); @@ -947,7 +981,7 @@ static LRESULT OnViewSettings() // // ID_VIEW_SCRIPT // -static LRESULT OnViewScript() +static LRESULT OnViewScript(VOID) { BYTE cType = cCurrentRomType; if (nState != SM_RUN) @@ -979,7 +1013,7 @@ ok: // // ID_BACKUP_SAVE // -static LRESULT OnBackupSave() +static LRESULT OnBackupSave(VOID) { UINT nOldState; if (pbyRom == NULL) return 0; @@ -992,7 +1026,7 @@ static LRESULT OnBackupSave() // // ID_BACKUP_RESTORE // -static LRESULT OnBackupRestore() +static LRESULT OnBackupRestore(VOID) { SwitchToState(SM_INVALID); RestoreBackup(); @@ -1003,7 +1037,7 @@ static LRESULT OnBackupRestore() // // ID_BACKUP_DELETE // -static LRESULT OnBackupDelete() +static LRESULT OnBackupDelete(VOID) { ResetBackup(); return 0; @@ -1012,32 +1046,38 @@ static LRESULT OnBackupDelete() // // ID_OBJECT_LOAD // -static LRESULT OnObjectLoad() +static LRESULT OnObjectLoad(VOID) { static BOOL bWarning = TRUE; - if (!Chipset.dispon && !bDbgEnable) // calculator off, turn on + SuspendDebugger(); // suspend debugger + bDbgAutoStateCtrl = FALSE; // disable automatic debugger state control + + // calculator off, turn on + if (!(Chipset.IORam[BITOFFSET]&DON)) { - // turn on HP KeyboardEvent(TRUE,0,0x8000); Sleep(200); KeyboardEvent(FALSE,0,0x8000); + Sleep(200); + // wait for sleep mode + while(Chipset.Shutdn == FALSE) Sleep(0); } if (nState != SM_RUN) { InfoMessage(_T("The emulator must be running to load an object.")); - return 0; + goto cancel; } - + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state { InfoMessage(_T("The emulator is busy.")); - return 0; + goto cancel; } _ASSERT(nState == SM_SLEEP); - + if (bWarning) { UINT uReply = YesNoCancelMessage( @@ -1054,20 +1094,20 @@ static LRESULT OnObjectLoad() break; case IDCANCEL: SwitchToState(SM_RUN); - return 0; + goto cancel; } } - + if (!GetLoadObjectFilename()) { SwitchToState(SM_RUN); - return 0; + goto cancel; } - + if (!LoadObject(szBufferFilename)) { SwitchToState(SM_RUN); - return 0; + goto cancel; } SwitchToState(SM_RUN); // run state @@ -1077,20 +1117,24 @@ static LRESULT OnObjectLoad() Sleep(200); KeyboardEvent(FALSE,0,0x8000); while(Chipset.Shutdn == FALSE) Sleep(0); + +cancel: + bDbgAutoStateCtrl = TRUE; // enable automatic debugger state control + ResumeDebugger(); return 0; } // // ID_OBJECT_SAVE // -static LRESULT OnObjectSave() +static LRESULT OnObjectSave(VOID) { if (nState != SM_RUN) { InfoMessage(_T("The emulator must be running to save an object.")); return 0; } - + if (WaitForSleepState()) // wait for cpu SHUTDN then sleep state { InfoMessage(_T("The emulator is busy.")); @@ -1104,11 +1148,11 @@ static LRESULT OnObjectSave() SwitchToState(SM_RUN); return 0; } - + SaveObject(szBufferFilename); - + SwitchToState(SM_RUN); - + return 0; } @@ -1165,9 +1209,10 @@ static BOOL CALLBACK Disasm(HWND hDlg, UINT message, DWORD wParam, LONG lParam) return TRUE; case IDC_DISASM_PORT2: disassembler_map = MEM_PORT2; - dwAddressMax = (cCurrentRomType=='E' || cCurrentRomType=='X') - ? (Chipset.Port2Size * 2048) - : ((dwPort2Mask != 0) ? ((dwPort2Mask + 1) << 18) : (dwPort2Size * 2048)); + dwAddressMax = ((cCurrentRomType=='E' || cCurrentRomType=='X') + ? Chipset.Port2Size + : dwPort2Size) + * 2048; return TRUE; case IDOK: SendDlgItemMessage(hDlg,IDC_DISASM_ADR,EM_SETSEL,0,-1); @@ -1209,7 +1254,6 @@ static BOOL CALLBACK Disasm(HWND hDlg, UINT message, DWORD wParam, LONG lParam) // static BOOL CALLBACK About(HWND hDlg, UINT message, DWORD wParam, LONG lParam) { - switch (message) { case WM_INITDIALOG: @@ -1228,7 +1272,7 @@ static BOOL CALLBACK About(HWND hDlg, UINT message, DWORD wParam, LONG lParam) UNREFERENCED_PARAMETER(lParam); } -static LRESULT OnToolDisasm() // disasm dialogbox call +static LRESULT OnToolDisasm(VOID) // disasm dialogbox call { if (pbyRom) SwitchToState(SM_SLEEP); if (DialogBox(hApp, MAKEINTRESOURCE(IDD_DISASM), hWnd, (DLGPROC)Disasm) == -1) @@ -1237,7 +1281,7 @@ static LRESULT OnToolDisasm() // disasm dialogbox call return 0; } -static LRESULT OnAbout() +static LRESULT OnAbout(VOID) { if (DialogBox(hApp, MAKEINTRESOURCE(IDD_ABOUT), hWnd, (DLGPROC)About) == -1) AbortMessage(_T("About Dialog Box Creation Error !")); @@ -1250,7 +1294,7 @@ static LRESULT OnLButtonDown(UINT nFlags, WORD x, WORD y) return 0; } -static LRESULT OnLButtonUp(UINT nFlags, WORD x, WORD y) +static LRESULT OnLButtonUp(UINT nFlags, WORD x, WORD y) { if (nState == SM_RUN) MouseButtonUpAt(nFlags, x,y); return 0; @@ -1381,7 +1425,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC wc.hbrBackground = NULL; wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU); wc.lpszClassName = _T("CEmu48"); - + if (!RegisterClass(&wc)) { AbortMessage( @@ -1389,14 +1433,14 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC _T("This application will now terminate.")); return FALSE; } - + // Create window rectWindow.left = 0; rectWindow.top = 0; rectWindow.right = 256; rectWindow.bottom = 0; AdjustWindowRect(&rectWindow, WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, TRUE); - + hWnd = CreateWindow(_T("CEmu48"), _T("Emu48"), WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT, @@ -1404,21 +1448,21 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC rectWindow.bottom - rectWindow.top, NULL,NULL,hApp,NULL ); - + if (hWnd == NULL) { AbortMessage(_T("Window creation failed.")); return FALSE; } - + // initialization QueryPerformanceFrequency(&lFreq); // init high resolution counter QueryPerformanceCounter(&lAppStart); GetCurrentDirectory(ARRAYSIZEOF(szCurrentDirectory), szCurrentDirectory); - + ReadSettings(); - + UpdateWindowStatus(); // create auto event handle @@ -1445,7 +1489,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC _ASSERT(dwAffMask != 0); ResumeThread(hThread); // start thread while (nState!=nNextState) Sleep(0); // wait for thread initialized - + idDdeInst = 0; // initialize DDE server if (DdeInitialize(&idDdeInst,(PFNCALLBACK) &DdeCallback, APPCLASS_STANDARD | @@ -1487,9 +1531,9 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC SetWindowTitle(_T("Untitled")); goto start; } - + ResetDocument(); - + start: // init clipboard format and name service uCF_HpObj = RegisterClipboardFormat(_T(CF_HPOBJ)); @@ -1500,7 +1544,7 @@ start: ShowWindow(hWnd, nCmdShow); if (pbyRom) SwitchToState(SM_RUN); - + while (GetMessage(&msg, NULL, 0, 0)) { if( (hDlgDebug == NULL || !IsDialogMessage(hDlgDebug, &msg)) @@ -1511,8 +1555,6 @@ start: } } - SwitchToState(SM_RETURN); // exit emulation thread - // clean up DDE server DdeNameService(idDdeInst, hszService, NULL, DNS_UNREGISTER); DdeFreeStringHandle(idDdeInst, hszService); @@ -1520,7 +1562,7 @@ start: DdeUninitialize(idDdeInst); WriteSettings(); // save emulation settings - + CloseHandle(hThread); // close thread handle CloseHandle(hEventShutdn); // close event handle _ASSERT(nState == SM_RETURN); // emulation thread down? @@ -1530,7 +1572,7 @@ start: _ASSERT(pKml == NULL); // KML script not closed _ASSERT(szTitle == NULL); // freed allocated memory _ASSERT(hPalette == NULL); // freed resource memory - + return msg.wParam; UNREFERENCED_PARAMETER(lpCmdLine); UNREFERENCED_PARAMETER(hPrevInst); diff --git a/sources/Emu48/EMU48.DSP b/sources/Emu48/EMU48.DSP index 76e6fd1..05745f1 100644 --- a/sources/Emu48/EMU48.DSP +++ b/sources/Emu48/EMU48.DSP @@ -7,19 +7,21 @@ CFG=Emu48 - Win32 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run -!MESSAGE +!MESSAGE !MESSAGE NMAKE /f "Emu48.mak". -!MESSAGE +!MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE +!MESSAGE !MESSAGE NMAKE /f "Emu48.mak" CFG="Emu48 - Win32 Release" -!MESSAGE +!MESSAGE !MESSAGE Possible choices for configuration are: -!MESSAGE +!MESSAGE !MESSAGE "Emu48 - Win32 Release" (based on "Win32 (x86) Application") !MESSAGE "Emu48 - Win32 Debug" (based on "Win32 (x86) Application") -!MESSAGE +!MESSAGE "Emu48 - Win32 Release Unicode" (based on "Win32 (x86) Application") +!MESSAGE "Emu48 - Win32 Debug Unicode" (based on "Win32 (x86) Application") +!MESSAGE # Begin Project # PROP Scc_ProjName "" @@ -80,12 +82,68 @@ LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /debug /machine:I386 -!ENDIF +!ELSEIF "$(CFG)" == "Emu48 - Win32 Release Unicode" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Emu48___" +# PROP BASE Intermediate_Dir "Emu48___" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\ReleaseUnicode" +# PROP Intermediate_Dir ".\ReleaseUnicode" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /Gr /MT /W3 /GX /O2 /Ob2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /Yu"pch.h" /FD /c +# ADD CPP /nologo /Gr /MT /W3 /GX /O2 /Ob2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /D "_UNICODE" /D "UNICODE" /Yu"pch.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "Emu48 - Win32 Debug Unicode" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Emu48__0" +# PROP BASE Intermediate_Dir "Emu48__0" +# PROP BASE Ignore_Export_Lib 0 +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\DebugUnicode" +# PROP Intermediate_Dir ".\DebugUnicode" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /FR /Yu"pch.h" /FD /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "STRICT" /D "_UNICODE" /D "UNICODE" /FR /Yu"pch.h" /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib shell32.lib winmm.lib comctl32.lib advapi32.lib /nologo /subsystem:windows /debug /machine:I386 + +!ENDIF # Begin Target # Name "Emu48 - Win32 Release" # Name "Emu48 - Win32 Debug" +# Name "Emu48 - Win32 Release Unicode" +# Name "Emu48 - Win32 Debug Unicode" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90" @@ -112,13 +170,6 @@ SOURCE=.\Emu48.c # Begin Source File SOURCE=.\Emu48.rc - -!IF "$(CFG)" == "Emu48 - Win32 Release" - -!ELSEIF "$(CFG)" == "Emu48 - Win32 Debug" - -!ENDIF - # End Source File # Begin Source File diff --git a/sources/Emu48/EMU48.H b/sources/Emu48/EMU48.H index c5eedc9..a8aa6aa 100644 --- a/sources/Emu48/EMU48.H +++ b/sources/Emu48/EMU48.H @@ -6,6 +6,14 @@ * Copyright (C) 1995 Sebastien Carlier * */ +#if defined GRAYSCALE // grayscale defines + #define GRAYON(a) a + #define GRAYOFF(a) +#else + #define GRAYON(a) + #define GRAYOFF(a) a +#endif + #include "types.h" #define ARRAYSIZEOF(a) (sizeof(a) / sizeof(a[0])) @@ -39,7 +47,7 @@ #define MEM_PORT1 3 #define MEM_PORT2 4 -#define VIEW_SHORT FALSE // view of disassembler output +#define VIEW_SHORT FALSE // view of disassembler output #define VIEW_LONG TRUE #define DISP_POINTER 0x01 // defines for display area @@ -58,10 +66,12 @@ extern LPTSTR szAppName; extern LPTSTR szTopic; extern LPTSTR szTitle; extern CRITICAL_SECTION csGDILock; +extern CRITICAL_SECTION csLcdLock; extern CRITICAL_SECTION csKeyLock; extern CRITICAL_SECTION csIOLock; extern CRITICAL_SECTION csT1Lock; extern CRITICAL_SECTION csT2Lock; +extern CRITICAL_SECTION csTxdLock; extern CRITICAL_SECTION csRecvLock; extern INT nArgc; extern LPCTSTR *ppArgv; @@ -88,7 +98,7 @@ extern VOID UpdateWindowStatus(VOID); extern VOID ReadSettings(VOID); extern VOID WriteSettings(VOID); extern VOID ReadLastDocument(LPTSTR szFileName, DWORD nSize); -extern VOID WriteLastDocument(LPCTSTR szFilename); +extern VOID WriteLastDocument(LPCTSTR szFilename); // Display.c extern UINT nBackgroundX; @@ -114,6 +124,9 @@ 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; @@ -128,7 +141,7 @@ extern TCHAR szSerialIr[16]; extern DWORD dwSXCycles; extern DWORD dwGXCycles; extern HANDLE hEventDebug; -extern BOOL bDbgEnable; +extern BOOL bDbgAutoStateCtrl; extern INT nDbgState; extern BOOL bDbgNOP3; extern BOOL bDbgCode; @@ -142,6 +155,9 @@ extern DWORD *pdwInstrArray; extern WORD wInstrSize; extern WORD wInstrWp; extern WORD wInstrRp; +extern VOID SuspendDebugger(VOID); +extern VOID ResumeDebugger(VOID); +extern VOID CheckSerial(VOID); extern VOID AdjKeySpeed(VOID); extern VOID SetSpeed(BOOL bAdjust); extern VOID UpdateKdnBit(VOID); @@ -173,12 +189,13 @@ extern DWORD dwPort2Size; extern DWORD dwPort2Mask; extern BOOL bBackup; extern WORD WriteStack(LPBYTE,DWORD); +extern VOID SetWindowLocation(HWND hWnd,INT nPosX,INT nPosY); +extern VOID UpdatePatches(BOOL bPatch); +extern BOOL PatchRom(LPCTSTR szFilename); extern BOOL MapRom(LPCTSTR szFilename); extern VOID UnmapRom(VOID); extern BOOL MapPort2(LPCTSTR szFilename); extern VOID UnmapPort2(VOID); -extern VOID UpdatePatches(BOOL bPatch); -extern BOOL PatchRom(LPCTSTR szFilename); extern VOID ResetDocument(VOID); extern BOOL NewDocument(VOID); extern BOOL OpenDocument(LPCTSTR szFilename); @@ -205,7 +222,6 @@ extern BYTE ReadT1(VOID); extern VOID SetT1(BYTE byValue); // MOps.c -extern BOOL ioc_acc; extern BOOL bFlashRomArray; extern BYTE disp; extern LPBYTE RMap[256]; @@ -218,7 +234,6 @@ extern VOID Reset(VOID); extern VOID C_Eq_Id(VOID); extern enum MMUMAP MapData(DWORD d); extern VOID CpuReset(VOID); -extern BYTE GetLineCounter(VOID); extern VOID Npeek(BYTE *a, DWORD d, UINT s); extern VOID Nread(BYTE *a, DWORD d, UINT s); extern VOID Nwrite(BYTE *a, DWORD d, UINT s); @@ -258,6 +273,7 @@ extern BOOL CommOpen(LPTSTR strWirePort,LPTSTR strIrPort); extern VOID CommClose(VOID); extern VOID CommSetBaud(VOID); extern BOOL UpdateUSRQ(VOID); +extern VOID CommTxBRK(VOID); extern VOID CommTransmit(VOID); extern VOID CommReceive(VOID); diff --git a/sources/Emu48/EMU48.RC b/sources/Emu48/EMU48.RC index 406375b..268beb3 100644 --- a/sources/Emu48/EMU48.RC +++ b/sources/Emu48/EMU48.RC @@ -27,8 +27,16 @@ LANGUAGE LANG_FRENCH, SUBLANG_FRENCH // #ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO DISCARDABLE +GUIDELINES DESIGNINFO DISCARDABLE BEGIN + IDD_WRITEONLYREG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 173 + TOPMARGIN, 7 + BOTTOMMARGIN, 75 + END + IDD_FIND, DIALOG BEGIN LEFTMARGIN, 6 @@ -133,18 +141,35 @@ END // Dialog // -IDD_FIND DIALOGEX 0, 0, 197, 47 +IDD_WRITEONLYREG DIALOG DISCARDABLE 0, 0, 180, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Write Only Registers" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "20-24 (Display Start Address)",IDC_STATIC,11,11,131,8 + RTEXT "00000",IDC_ADDR20_24,144,11,25,8 + LTEXT "25-27 (Display Line Offset)",IDC_STATIC,11,21,131,8 + RTEXT "000",IDC_ADDR25_27,144,21,25,8 + LTEXT "28-29 (Display Line Counter)",IDC_STATIC,11,31,131,8 + RTEXT "00",IDC_ADDR28_29,144,31,25,8 + LTEXT "30-34 (Display Secondary Start Address)",IDC_STATIC,11, + 41,131,8 + RTEXT "00000",IDC_ADDR30_34,144,41,25,8 + DEFPUSHBUTTON "OK",IDOK,65,61,50,14 +END + +IDD_FIND DIALOG DISCARDABLE 0, 0, 197, 47 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_TOOLWINDOW CAPTION "Find" FONT 8, "MS Sans Serif" BEGIN LTEXT "Find &what:",IDC_STATIC,7,9,34,8 - COMBOBOX IDC_FIND_DATA,46,7,88,41,CBS_DROPDOWN | CBS_AUTOHSCROLL | + COMBOBOX IDC_FIND_DATA,46,7,88,41,CBS_DROPDOWN | CBS_AUTOHSCROLL | WS_VSCROLL | WS_TABSTOP - CONTROL "Find &ASCII",IDC_FIND_ASCII,"Button",BS_AUTOCHECKBOX | + CONTROL "Find &ASCII",IDC_FIND_ASCII,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,46,30,49,10 - DEFPUSHBUTTON "Find Next",IDOK,140,7,50,14 + DEFPUSHBUTTON "&Find Next",IDOK,140,7,50,14 PUSHBUTTON "Cancel",IDCANCEL,140,26,50,14 END @@ -155,15 +180,15 @@ FONT 8, "Courier New" BEGIN DEFPUSHBUTTON "OK",IDCANCEL,83,81,30,14 LTEXT "Current breakpoints:",IDC_STATIC_BREAKPOINT,5,5,82,8 - LISTBOX IDC_BREAKEDIT_WND,5,17,108,58,LBS_SORT | - LBS_OWNERDRAWFIXED | LBS_HASSTRINGS | - LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | + LISTBOX IDC_BREAKEDIT_WND,5,17,108,58,LBS_SORT | + LBS_OWNERDRAWFIXED | LBS_HASSTRINGS | + LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "&Add...",IDC_BREAKEDIT_ADD,5,81,30,14 PUSHBUTTON "&Delete",IDC_BREAKEDIT_DELETE,44,81,30,14 END -IDD_ABOUT DIALOGEX 0, 0, 261, 160 +IDD_ABOUT DIALOG DISCARDABLE 0, 0, 261, 160 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About Emu48" FONT 8, "MS Sans Serif" @@ -171,14 +196,14 @@ BEGIN ICON IDI_EMU48,IDC_STATIC,7,6,20,20,SS_REALSIZEIMAGE, WS_EX_TRANSPARENT LTEXT "",IDC_VERSION,29,6,151,8,NOT WS_GROUP - LTEXT "Copyright © 2001 Sébastien Carlier && Christoph Gießelink", + LTEXT "Copyright © 2002 Sébastien Carlier && Christoph Gießelink", IDC_STATIC,29,18,181,8 DEFPUSHBUTTON "OK",IDOK,215,12,39,14 - EDITTEXT IDC_LICENSE,7,33,247,112,ES_MULTILINE | ES_AUTOHSCROLL | + EDITTEXT IDC_LICENSE,7,33,247,112,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY END -IDD_SETTINGS DIALOGEX 0, 0, 167, 209 +IDD_SETTINGS DIALOG DISCARDABLE 0, 0, 167, 209 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Settings" FONT 8, "MS Sans Serif" @@ -193,12 +218,12 @@ BEGIN IDC_ALWAYSDISPLOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP, 13,52,136,10 GROUPBOX "General",IDC_STATIC,7,4,153,63 - CONTROL "HP Mnemonics",IDC_DISASM_HP,"Button",BS_AUTORADIOBUTTON | + CONTROL "HP Mnemonics",IDC_DISASM_HP,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,13,81,65,11 CONTROL "Class Mnemonics",IDC_DISASM_CLASS,"Button", BS_AUTORADIOBUTTON,84,81,70,11 GROUPBOX "Disassembler",IDC_STATIC,7,70,153,28 - CONTROL "Port 1 is Plugged",IDC_PORT1EN,"Button",BS_AUTOCHECKBOX | + CONTROL "Port 1 is Plugged",IDC_PORT1EN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,110,67,10 CONTROL "Port 1 is Writeable",IDC_PORT1WR,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,84,110,69,10 @@ -208,10 +233,10 @@ BEGIN EDITTEXT IDC_PORT2,51,134,104,12,ES_AUTOHSCROLL GROUPBOX "Memory Cards",IDC_STATIC,7,101,153,50 LTEXT "Wire:",IDC_STATIC,13,166,17,8 - COMBOBOX IDC_WIRE,31,164,48,42,CBS_DROPDOWNLIST | WS_VSCROLL | + COMBOBOX IDC_WIRE,31,164,48,42,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP,WS_EX_LEFTSCROLLBAR LTEXT "IR:",IDC_STATIC,89,166,9,8 - COMBOBOX IDC_IR,107,164,48,43,CBS_DROPDOWNLIST | WS_VSCROLL | + COMBOBOX IDC_IR,107,164,48,43,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP GROUPBOX "Serial Ports",IDC_STATIC,7,154,153,27 DEFPUSHBUTTON "&Ok",IDOK,9,188,50,14 @@ -225,7 +250,7 @@ FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,129,7,50,14 PUSHBUTTON "Cancel",IDCANCEL,129,27,50,14 - COMBOBOX IDC_KMLSCRIPT,7,47,172,120,CBS_DROPDOWNLIST | + COMBOBOX IDC_KMLSCRIPT,7,47,172,120,CBS_DROPDOWNLIST | CBS_OEMCONVERT | CBS_SORT | WS_VSCROLL | WS_TABSTOP EDITTEXT IDC_EMU48DIR,7,17,107,14,ES_AUTOHSCROLL LTEXT "Emu48 Directory :",IDC_STATIC,7,7,115,8 @@ -233,25 +258,25 @@ BEGIN PUSHBUTTON "V",IDC_UPDATE,115,17,10,14 END -IDD_KMLLOG DIALOGEX 0, 0, 301, 167 +IDD_KMLLOG DIALOG DISCARDABLE 0, 0, 301, 167 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "KML Script Compilation Result" FONT 8, "MS Sans Serif" BEGIN DEFPUSHBUTTON "OK",IDOK,86,146,50,14 PUSHBUTTON "Cancel",IDCANCEL,164,146,50,14 - CONTROL "Always",IDC_ALWAYSDISPLOG,"Button",BS_AUTOCHECKBOX | + CONTROL "Always",IDC_ALWAYSDISPLOG,"Button",BS_AUTOCHECKBOX | BS_PUSHLIKE | WS_TABSTOP,263,147,31,13,WS_EX_STATICEDGE 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 - EDITTEXT IDC_KMLLOG,7,48,287,92,ES_MULTILINE | ES_AUTOHSCROLL | - ES_OEMCONVERT | ES_READONLY | ES_WANTRETURN | NOT + EDITTEXT IDC_KMLLOG,7,48,287,92,ES_MULTILINE | ES_AUTOHSCROLL | + ES_OEMCONVERT | ES_READONLY | ES_WANTRETURN | NOT WS_BORDER | WS_VSCROLL | NOT WS_TABSTOP,WS_EX_CLIENTEDGE END -IDD_DISASM DIALOGEX 0, 0, 255, 165 +IDD_DISASM DIALOG DISCARDABLE 0, 0, 255, 165 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Disassembler" FONT 8, "Courier New" @@ -262,7 +287,7 @@ BEGIN PUSHBUTTON "&Copy Data",IDC_DISASM_COPY,150,144,47,14 PUSHBUTTON "Cancel",IDCANCEL,201,144,47,14 GROUPBOX "Module",IDC_DISASM_MODULE,7,5,241,26 - CONTROL "Map",IDC_DISASM_MAP,"Button",BS_AUTORADIOBUTTON | + CONTROL "Map",IDC_DISASM_MAP,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,16,37,10 CONTROL "ROM",IDC_DISASM_ROM,"Button",BS_AUTORADIOBUTTON,61,16, 37,10 @@ -272,20 +297,20 @@ BEGIN 155,16,37,10 CONTROL "Port 2",IDC_DISASM_PORT2,"Button",BS_AUTORADIOBUTTON, 202,16,37,10 - LISTBOX IDC_DISASM_WIN,7,37,241,100,NOT LBS_NOTIFY | - LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | + LISTBOX IDC_DISASM_WIN,7,37,241,100,NOT LBS_NOTIFY | + LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP,WS_EX_NOPARENTNOTIFY END -IDD_DEBUG DIALOGEX 0, 0, 279, 269 +IDD_DEBUG DIALOG DISCARDABLE 0, 0, 279, 269 STYLE WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Debugger" MENU IDR_DEBUG FONT 8, "Courier New" BEGIN - LISTBOX IDC_DEBUG_CODE,11,27,165,122,NOT LBS_NOTIFY | - LBS_OWNERDRAWFIXED | LBS_HASSTRINGS | - LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | + LISTBOX IDC_DEBUG_CODE,11,27,165,122,NOT LBS_NOTIFY | + LBS_OWNERDRAWFIXED | LBS_HASSTRINGS | + LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | WS_TABSTOP GROUPBOX "Code",IDC_STATIC_CODE,5,17,177,138 LTEXT "A= 0000000000000000",IDC_REG_A,192,27,77,8 @@ -313,30 +338,30 @@ BEGIN GROUPBOX "Registers",IDC_STATIC_REGISTERS,187,17,87,138 CONTROL "",IDC_DEBUG_MEM,"Static",SS_WHITERECT | WS_GROUP,11,166, 165,52,WS_EX_CLIENTEDGE - LISTBOX IDC_DEBUG_MEM_ADDR,12,168,25,48,NOT LBS_NOTIFY | - LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_DISABLED | NOT + LISTBOX IDC_DEBUG_MEM_ADDR,12,168,25,48,NOT LBS_NOTIFY | + LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_DISABLED | NOT WS_BORDER - LISTBOX IDC_DEBUG_MEM_COL0,40,168,11,48,LBS_NOINTEGRALHEIGHT | + LISTBOX IDC_DEBUG_MEM_COL0,40,168,11,48,LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | NOT WS_BORDER | WS_TABSTOP - LISTBOX IDC_DEBUG_MEM_COL1,52,168,11,48,LBS_NOINTEGRALHEIGHT | + LISTBOX IDC_DEBUG_MEM_COL1,52,168,11,48,LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | NOT WS_BORDER - LISTBOX IDC_DEBUG_MEM_COL2,64,168,11,48,LBS_NOINTEGRALHEIGHT | + LISTBOX IDC_DEBUG_MEM_COL2,64,168,11,48,LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | NOT WS_BORDER - LISTBOX IDC_DEBUG_MEM_COL3,76,168,11,48,LBS_NOINTEGRALHEIGHT | + LISTBOX IDC_DEBUG_MEM_COL3,76,168,11,48,LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | NOT WS_BORDER - LISTBOX IDC_DEBUG_MEM_COL4,88,168,11,48,LBS_NOINTEGRALHEIGHT | + LISTBOX IDC_DEBUG_MEM_COL4,88,168,11,48,LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | NOT WS_BORDER - LISTBOX IDC_DEBUG_MEM_COL5,100,168,11,48,LBS_NOINTEGRALHEIGHT | + LISTBOX IDC_DEBUG_MEM_COL5,100,168,11,48,LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | NOT WS_BORDER - LISTBOX IDC_DEBUG_MEM_COL6,112,168,11,48,LBS_NOINTEGRALHEIGHT | + LISTBOX IDC_DEBUG_MEM_COL6,112,168,11,48,LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | NOT WS_BORDER - LISTBOX IDC_DEBUG_MEM_COL7,124,168,11,48,LBS_NOINTEGRALHEIGHT | + LISTBOX IDC_DEBUG_MEM_COL7,124,168,11,48,LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | NOT WS_BORDER - LISTBOX IDC_DEBUG_MEM_TEXT,139,168,35,48,NOT LBS_NOTIFY | - LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_DISABLED | NOT + LISTBOX IDC_DEBUG_MEM_TEXT,139,168,35,48,NOT LBS_NOTIFY | + LBS_NOINTEGRALHEIGHT | LBS_NOSEL | WS_DISABLED | NOT WS_BORDER GROUPBOX "Memory",IDC_STATIC_MEMORY,5,156,177,68 - LISTBOX IDC_DEBUG_STACK,192,166,76,52,LBS_NOINTEGRALHEIGHT | + LISTBOX IDC_DEBUG_STACK,192,166,76,52,LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT | WS_VSCROLL | WS_TABSTOP GROUPBOX "Stack",IDC_STATIC_STACK,187,156,87,68 LTEXT "Size Mask",IDC_STATIC,11,243,37,8 @@ -372,8 +397,8 @@ STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "New Value" FONT 8, "MS Sans Serif" BEGIN - LTEXT "New value (hexdezimal):",IDC_STATIC,8,9,81,8 - EDITTEXT IDC_NEWVALUE,91,7,77,12,ES_AUTOHSCROLL + LTEXT "New value (hexadecimal):",IDC_STATIC,8,9,81,8 + EDITTEXT IDC_NEWVALUE,93,7,75,12,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,21,29,50,14 PUSHBUTTON "Cancel",IDCANCEL,103,29,50,14 END @@ -383,8 +408,8 @@ STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Enter Address" FONT 8, "MS Sans Serif" BEGIN - LTEXT "Enter address (hexdezimal):",IDC_STATIC,8,9,90,8 - EDITTEXT IDC_ENTERADR,106,7,43,12,ES_AUTOHSCROLL + LTEXT "Enter address (hexadecimal):",IDC_STATIC,8,9,93,8 + EDITTEXT IDC_ENTERADR,105,7,44,12,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,14,29,50,14 PUSHBUTTON "Cancel",IDCANCEL,92,29,50,14 END @@ -394,9 +419,9 @@ STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Enter Breakpoint" FONT 8, "MS Sans Serif" BEGIN - LTEXT "Enter breakpoint (hexdezimal):",IDC_STATIC,8,9,96,8 - EDITTEXT IDC_ENTERADR,110,7,39,12,ES_AUTOHSCROLL - CONTROL "&Code",IDC_BPCODE,"Button",BS_AUTORADIOBUTTON | + LTEXT "Enter breakpoint (hexadecimal):",IDC_STATIC,8,9,99,8 + EDITTEXT IDC_ENTERADR,112,7,37,12,ES_AUTOHSCROLL + CONTROL "&Code",IDC_BPCODE,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,17,24,33,10 CONTROL "R&PL",IDC_BPRPL,"Button",BS_AUTORADIOBUTTON,17,50,30,10 CONTROL "Memory &Access",IDC_BPACCESS,"Button", @@ -409,15 +434,15 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,92,65,50,14 END -IDD_INSTRUCTIONS DIALOGEX 0, 0, 186, 169 +IDD_INSTRUCTIONS DIALOG DISCARDABLE 0, 0, 186, 169 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Last Instructions" FONT 8, "Courier New" BEGIN LTEXT "Instructions (disassembly maybe incorrect):", IDC_INSTR_TEXT,7,7,173,8 - LISTBOX IDC_INSTR_CODE,7,18,172,122,NOT LBS_NOTIFY | - LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | + LISTBOX IDC_INSTR_CODE,7,18,172,122,NOT LBS_NOTIFY | + LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP,WS_EX_NOPARENTNOTIFY PUSHBUTTON "&Copy Data",IDC_INSTR_COPY,7,148,50,14 PUSHBUTTON "C&lear Data",IDC_INSTR_CLEAR,68,148,50,14 @@ -461,8 +486,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,2,5,0 - PRODUCTVERSION 1,2,5,0 + FILEVERSION 1,3,0,0 + PRODUCTVERSION 1,3,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -479,12 +504,12 @@ BEGIN BEGIN VALUE "CompanyName", "Sebastien Carlier & Christoph Gießelink\0" VALUE "FileDescription", "HP38/39/40/48/49 Emulator\0" - VALUE "FileVersion", "1, 2, 5, 0\0" + VALUE "FileVersion", "1, 3, 0, 0\0" VALUE "InternalName", "Emu48\0" - VALUE "LegalCopyright", "Copyright © 2001\0" + VALUE "LegalCopyright", "Copyright © 2002\0" VALUE "OriginalFilename", "Emu48.exe\0" VALUE "ProductName", "Emu48\0" - VALUE "ProductVersion", "1, 2, 5, 0\0" + VALUE "ProductVersion", "1, 3, 0, 0\0" END END BLOCK "VarFileInfo" @@ -510,7 +535,7 @@ IDI_EMU48 ICON DISCARDABLE "Emu48.ico" // Menu // -IDR_MENU MENU DISCARDABLE +IDR_MENU MENU DISCARDABLE BEGIN POPUP "&File" BEGIN @@ -557,7 +582,7 @@ BEGIN END END -IDR_DEBUG MENU DISCARDABLE +IDR_DEBUG MENU DISCARDABLE BEGIN POPUP "&Debug" BEGIN @@ -586,10 +611,11 @@ BEGIN POPUP "&Info" BEGIN MENUITEM "&Last Instructions...", ID_INFO_LASTINSTRUCTIONS + MENUITEM "&Write Only Registers...", ID_INFO_WRITEONLYREG END END -IDR_DEBUG_CODE MENU DISCARDABLE +IDR_DEBUG_CODE MENU DISCARDABLE BEGIN POPUP "" BEGIN @@ -600,7 +626,7 @@ BEGIN END END -IDR_DEBUG_MEM MENU DISCARDABLE +IDR_DEBUG_MEM MENU DISCARDABLE BEGIN POPUP "" BEGIN @@ -610,7 +636,31 @@ BEGIN MENUITEM "Go to D&1", ID_DEBUG_MEM_GOD1 MENUITEM "Go to &Stack", ID_DEBUG_MEM_GOSTACK MENUITEM SEPARATOR - MENUITEM "Find...\tF", ID_DEBUG_MEM_FIND + MENUITEM "&Find...\tF", ID_DEBUG_MEM_FIND + MENUITEM SEPARATOR + POPUP "&Mapping" + BEGIN + MENUITEM "Map", ID_DEBUG_MEM_MAP + MENUITEM SEPARATOR + MENUITEM "NCE1", ID_DEBUG_MEM_NCE1 + , GRAYED + MENUITEM "NCE2", ID_DEBUG_MEM_NCE2 + , GRAYED + MENUITEM "CE1", ID_DEBUG_MEM_CE1, GRAYED + MENUITEM "CE2", ID_DEBUG_MEM_CE2, GRAYED + MENUITEM "NCE3", ID_DEBUG_MEM_NCE3 + , GRAYED + END + END +END + +IDR_DEBUG_STACK MENU DISCARDABLE +BEGIN + POPUP "" + BEGIN + MENUITEM "&Push", ID_DEBUG_STACK_PUSH + MENUITEM "P&op", ID_DEBUG_STACK_POP + MENUITEM "&Modify", ID_DEBUG_STACK_MODIFY END END @@ -621,18 +671,18 @@ END // TEXTINCLUDE // -1 TEXTINCLUDE DISCARDABLE +1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END -2 TEXTINCLUDE DISCARDABLE +2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""winres.h""\r\n" "\0" END -3 TEXTINCLUDE DISCARDABLE +3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" @@ -646,7 +696,7 @@ END // String Table // -STRINGTABLE DISCARDABLE +STRINGTABLE DISCARDABLE BEGIN ID_DEBUG_RUN "Run" ID_DEBUG_RUNCURSOR "Run to Cursor" @@ -659,6 +709,14 @@ BEGIN ID_BREAKPOINTS_CODEEDIT "Breakpoint List" END + +///////////////////////////////////////////////////////////////////////////// +// +// Manifest +// + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "Emu48.xml" + #endif // French (France) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/sources/Emu48/EMU48.XML b/sources/Emu48/EMU48.XML new file mode 100644 index 0000000..1c05f71 --- /dev/null +++ b/sources/Emu48/EMU48.XML @@ -0,0 +1,22 @@ + + + +Emu48 + + + + + + diff --git a/sources/Emu48/ENGINE.C b/sources/Emu48/ENGINE.C index a0bda4e..ebd88c8 100644 --- a/sources/Emu48/ENGINE.C +++ b/sources/Emu48/ENGINE.C @@ -32,8 +32,8 @@ DWORD dwGXCycles = 123; // GX cpu cycles in interval // variables for debugger engine HANDLE hEventDebug; // event handle to stop cpu thread -BOOL bDbgEnable = FALSE; // debugger mode enable flag -INT nDbgState = DBG_RUN; // state of debugger +BOOL bDbgAutoStateCtrl = TRUE; // debugger suspend control by SwitchToState() +INT nDbgState = DBG_OFF; // state of debugger BOOL bDbgNOP3 = FALSE; // halt on NOP3 (#420 opcode) BOOL bDbgRPL = FALSE; // halt on RPL entry @@ -52,6 +52,9 @@ WORD wInstrSize; // size of last instruction array WORD wInstrWp; // write pointer of instruction array WORD wInstrRp; // read pointer of instruction array +static BOOL bDbgRplBreak = FALSE; // flag for RPL breakpoint +static INT nDbgOldState = DBG_OFF; // old state of debugger for suspend/resume + static BOOL bCpuSlow = FALSE; // enable/disable real speed static DWORD dwEDbgT2 = 0; // debugger timer2 emulation @@ -80,12 +83,12 @@ static __inline VOID Debugger(VOID) // debugger part { LARGE_INTEGER lDummyInt; // sample timer ticks BOOL bStopEmulation; - BOOL bRplBreak = FALSE; // flag for RPL breakpoint - LPBYTE I = FASTPTR(Chipset.pc); // get opcode stream SaveInstrAddr(Chipset.pc); // save pc in last instruction buffer + bDbgRplBreak = FALSE; // flag for RPL breakpoint + // check for code breakpoints bStopEmulation = CheckBreakpoint(Chipset.pc, 1, BP_EXEC); @@ -100,7 +103,7 @@ static __inline VOID Debugger(VOID) // debugger part { dwRange = (I[2] & 0x8) ? 2 : 5; } - else // number of nibbles, (P,WP,XS,X,S,M,W) + else // number of nibbles, (P,WP,XS,X,S,M,W) { dwRange = (I[2] & 0x8) ? (I[3]+1) : (F_l[I[3]]); } @@ -121,7 +124,7 @@ static __inline VOID Debugger(VOID) // debugger part if (dwDbgRplPC == Chipset.pc) { dwDbgRplPC = -1; - bRplBreak = TRUE; + bDbgRplBreak = TRUE; bStopEmulation = TRUE; } @@ -186,7 +189,7 @@ static __inline VOID Debugger(VOID) // debugger part if (dwEDbgT2 == Chipset.t2) { // cpu cycles for one timer2 tick elapsed - if ((DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwEDbgCycles + if ((DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwEDbgCycles >= (SAMPLE / 8192) * (DWORD) T2CYCLES) { --Chipset.t2; @@ -206,21 +209,48 @@ static __inline VOID Debugger(VOID) // debugger part } dwEDbgT2 = Chipset.t2; // timer2 check reference value - // OutputDebugString(_T("Emulator stopped...\n")); - NotifyDebugger(bRplBreak); + // redraw debugger window and stop + NotifyDebugger(bDbgRplBreak); WaitForSingleObject(hEventDebug,INFINITE); - // OutputDebugString(_T("Emulator running...\n")); StartTimers(); // continue timers - Chipset.Shutdn = FALSE; - Chipset.bShutdnWake = FALSE; + if (nDbgState > DBG_OFF) // if debugger is active + { + Chipset.Shutdn = FALSE; + Chipset.bShutdnWake = FALSE; + } // init slow down part dwOldCyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); QueryPerformanceCounter(&lDummyInt); dwSpeedRef = lDummyInt.LowPart; } + return; +} + +VOID SuspendDebugger(VOID) +{ + // auto control enabled, emulation halted by debugger + if (bDbgAutoStateCtrl && nDbgState > DBG_OFF) + { + nDbgOldState = nDbgState; // save old state + nDbgState = DBG_SUSPEND; // suspend state + SetEvent(hEventDebug); // exit debugger + } + return; +} + +VOID ResumeDebugger(VOID) +{ + // auto control enabled, debugger is suspended + if (bDbgAutoStateCtrl && nDbgState == DBG_SUSPEND) + { + // active RPL breakpoint + if (bDbgRplBreak) dwDbgRplPC = Chipset.pc; + nDbgState = nDbgOldState; // set to old debugger state + } + return; } static __inline VOID CheckDisp(BOOL bSync) @@ -239,35 +269,6 @@ static __inline VOID CheckDisp(BOOL bSync) return; } -static __inline VOID CheckSerial(VOID) -{ - if (ioc_acc) // IOC changed - { - ioc_acc = FALSE; - - // COM port closed and serial on - if (bCommInit == FALSE && (Chipset.IORam[IOC] & SON) != 0) - { - bCommInit = CommOpen(szSerialWire,szSerialIr); // open COM ports - } - - // COM port opened and serial off - if (bCommInit == TRUE && (Chipset.IORam[IOC] & SON) == 0) - { - CommClose(); // close COM port - bCommInit = FALSE; - } - - // Interrupt on recv buffer full and receive buffer full - if ((Chipset.IORam[IOC] & ERBF) && (Chipset.IORam[RCS] & RBF)) - { - Chipset.Shutdn = FALSE; - INTERRUPT; - } - } - return; -} - static __inline VOID AdjustSpeed(VOID) // adjust emulation speed { if (bCpuSlow || bKeySlow) // emulation slow down @@ -285,7 +286,7 @@ static __inline VOID AdjustSpeed(VOID) // adjust emulation speed } while((dwTicks = lAct.LowPart-dwSpeedRef) <= dwTickRef); - // workaround for QueryPerformanceCounter() in Win2k, + // workaround for QueryPerformanceCounter() in Win2k, // if last command sequence took over 50ms -> synchronize if(dwTicks > 819 * dwTickRef) // time for last commands > 50ms (819 / 16384Hz) { @@ -304,6 +305,23 @@ static __inline VOID AdjustSpeed(VOID) // adjust emulation speed return; } +VOID CheckSerial(VOID) +{ + // COM port closed and serial on + if (bCommInit == FALSE && (Chipset.IORam[IOC] & SON) != 0) + { + bCommInit = CommOpen(szSerialWire,szSerialIr); // open COM ports + } + + // COM port opened and serial off + if (bCommInit == TRUE && (Chipset.IORam[IOC] & SON) == 0) + { + CommClose(); // close COM port + bCommInit = FALSE; + } + return; +} + VOID AdjKeySpeed(VOID) // slow down key repeat { WORD i; @@ -338,34 +356,38 @@ VOID SetSpeed(BOOL bAdjust) // set emulation speed dwSpeedRef = lTime.LowPart; // save reference time } bCpuSlow = bAdjust; // save emulation speed + return; } VOID UpdateKdnBit(VOID) // update KDN bit { if (Chipset.intk && (DWORD) (Chipset.cycles & 0xFFFFFFFFF) - Chipset.dwKdnCycles > (DWORD) T2CYCLES * 16) - IOBit(0x19,8,Chipset.in != 0); + IOBit(SRQ2,KDN,Chipset.in != 0); + return; } BOOL WaitForSleepState(VOID) // wait for cpu SHUTDN then sleep state { DWORD dwRefTime; - DisableDebugger(); // disable debugger + SuspendDebugger(); // suspend debugger - dwRefTime = timeGetTime(); + dwRefTime = timeGetTime(); // wait for the SHUTDN command with 1.5 sec timeout while (timeGetTime() - dwRefTime < 1500L && !Chipset.Shutdn) Sleep(0); if (Chipset.Shutdn) // not timeout, cpu is down SwitchToState(SM_SLEEP); // go to sleep state + else + ResumeDebugger(); // timeout, resume to debugger return SM_SLEEP != nNextState; // state not changed, emulator was busy } UINT SwitchToState(UINT nNewState) { - UINT nOldState = nState; + UINT nOldState = nState; if (nState == nNewState) return nOldState; switch (nState) @@ -374,12 +396,12 @@ UINT SwitchToState(UINT nNewState) switch (nNewState) { case SM_INVALID: // -> Invalid - DisableDebugger(); // disable debugger nNextState = SM_INVALID; if (Chipset.Shutdn) SetEvent(hEventShutdn); else bInterrupt = TRUE; + SuspendDebugger(); // suspend debugger while (nState!=nNextState) Sleep(0); UpdateWindowStatus(); break; @@ -399,8 +421,8 @@ UINT SwitchToState(UINT nNewState) case SM_SLEEP: // -> Sleep nNextState = SM_SLEEP; bInterrupt = TRUE; // exit main loop - SetEvent(hEventDebug); // exit debugger - SetEvent(hEventShutdn); // exit shutdown + SuspendDebugger(); // suspend debugger + SetEvent(hEventShutdn); // exit shutdown while (nState!=nNextState) Sleep(0); bInterrupt = FALSE; ResetEvent(hEventDebug); @@ -415,11 +437,13 @@ UINT SwitchToState(UINT nNewState) nNextState = SM_RUN; // don't enter opcode loop on interrupt request bInterrupt = Chipset.Shutdn || Chipset.SoftInt; + ResumeDebugger(); SetEvent(hEventShutdn); while (nState!=nNextState) Sleep(0); UpdateWindowStatus(); break; case SM_RETURN: // -> Return + DisableDebugger(); // disable debugger nNextState = SM_RETURN; SetEvent(hEventShutdn); WaitForSingleObject(hThread,INFINITE); @@ -438,7 +462,8 @@ UINT SwitchToState(UINT nNewState) case SM_RUN: // -> Run nNextState = SM_RUN; // don't enter opcode loop on interrupt request - bInterrupt = !bDbgEnable && (Chipset.Shutdn || Chipset.SoftInt); + bInterrupt = (nDbgState == DBG_OFF) && (Chipset.Shutdn || Chipset.SoftInt); + ResumeDebugger(); SetEvent(hEventShutdn); // leave sleep state break; case SM_INVALID: // -> Invalid @@ -448,6 +473,7 @@ UINT SwitchToState(UINT nNewState) UpdateWindowStatus(); break; case SM_RETURN: // -> Return + DisableDebugger(); // disable debugger nNextState = SM_INVALID; SetEvent(hEventShutdn); while (nState!=nNextState) Sleep(0); @@ -482,7 +508,7 @@ loop: nState = SM_RETURN; // in return state return 0; // kill thread } - ioc_acc = TRUE; // test if UART on + CheckSerial(); // test if UART on } while (nNextState == SM_RUN) { @@ -523,16 +549,27 @@ loop: dwSpeedRef = lDummyInt.LowPart; SetHP48Time(); // update HP48 time & date StartTimers(); + // start display counter/update engine + StartDisplay((BYTE)(((Chipset.IORam[LINECOUNT+1]<<4)|Chipset.IORam[LINECOUNT])&0x3F)); } PCHANGED; while (!bInterrupt) { - if (bDbgEnable) Debugger(); // debugger active + if (nDbgState > DBG_OFF) // debugger active + { + Debugger(); + + // if suspended skip next opcode execution + if (nDbgState == DBG_SUSPEND) + { + if (Chipset.Shutdn) break; + continue; + } + } EvalOpcode(FASTPTR(Chipset.pc)); // execute opcode - CheckDisp(!Chipset.Shutdn); // check for display update - CheckSerial(); // serial support + GRAYOFF(CheckDisp(!Chipset.Shutdn)); // check for display update AdjustSpeed(); // adjust emulation speed } bInterrupt = FALSE; // be sure to reenter opcode loop @@ -568,6 +605,7 @@ loop: } _ASSERT(nNextState != SM_RUN); + StopDisplay(); // stop display counter/update StopTimers(); while (nNextState == SM_SLEEP) // go into sleep state diff --git a/sources/Emu48/FILES.C b/sources/Emu48/FILES.C index 788ed81..d39dd9b 100644 --- a/sources/Emu48/FILES.C +++ b/sources/Emu48/FILES.C @@ -48,7 +48,8 @@ static BYTE pbySignatureV[16] = "Emu49 Document\xFE"; static HANDLE hCurrentFile = NULL; static CHIPSET BackupChipset; -BOOL bBackup = FALSE; + +BOOL bBackup = FALSE; //################ //# @@ -59,7 +60,7 @@ BOOL bBackup = FALSE; WORD WriteStack(LPBYTE lpBuf,DWORD dwSize) // separated from LoadObject() { BOOL bBinary; - DWORD dwLength, dwAddress, i; + DWORD dwAddress, i; bBinary = ((lpBuf[dwSize+0]=='H') && (lpBuf[dwSize+1]=='P') @@ -69,7 +70,7 @@ WORD WriteStack(LPBYTE lpBuf,DWORD dwSize) // separated from LoadObject() && (lpBuf[dwSize+5]==((cCurrentRomType!='X') ? '8' : '9')) && (lpBuf[dwSize+6]=='-')); - for (i=0; iright = pRc->right - pRc->left + nPosX; + pRc->bottom = pRc->bottom - pRc->top + nPosY; + pRc->left = nPosX; + pRc->top = nPosY; + SetWindowPlacement(hWnd,&wndpl); + return; +} + + + //################ //# //# Patch @@ -238,7 +262,7 @@ BOOL PatchRom(LPCTSTR szFilename) } while (lpBuf[nPos]); if (lpBuf[nPos]==';') // comment ? { - do + do { nPos++; if (lpBuf[nPos]=='\n') @@ -359,7 +383,7 @@ BOOL MapRom(LPCTSTR szFilename) if (GetLastError() == ERROR_ALREADY_EXISTS) { AbortMessage(_T("Sharing file mapping handle.")); - } + } pbyRom = MapViewOfFile(hRomMap, bWrite ? FILE_MAP_WRITE : FILE_MAP_COPY, 0, 0, dwRomSize); if (pbyRom == NULL) { @@ -487,7 +511,6 @@ BOOL MapPort2(LPCTSTR szFilename) return FALSE; } dwPort2Size = dwFileSizeLo / 2048; // mapping size of port2 - if (dwPort2Size > 128) dwPort2Size = 128; return TRUE; } @@ -530,9 +553,9 @@ VOID ResetDocument(VOID) if (Chipset.Port0) LocalFree(Chipset.Port0); if (Chipset.Port1) LocalFree(Chipset.Port1); if (Chipset.Port2) LocalFree(Chipset.Port2); else UnmapPort2(); - FillMemory(&Chipset,sizeof(Chipset),0); - FillMemory(&RMap,sizeof(RMap),0); // delete MMU mappings - FillMemory(&WMap,sizeof(WMap),0); + ZeroMemory(&Chipset,sizeof(Chipset)); + ZeroMemory(&RMap,sizeof(RMap)); // delete MMU mappings + ZeroMemory(&WMap,sizeof(WMap)); UpdateWindowStatus(); return; } @@ -629,7 +652,7 @@ restore: } if (pbyRom) { - SetWindowPos(hWnd,NULL,Chipset.nPosX,Chipset.nPosY,0,0,SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE); + SetWindowLocation(hWnd,Chipset.nPosX,Chipset.nPosY); Map(0x00,0xFF); } return FALSE; @@ -638,8 +661,7 @@ restore: BOOL OpenDocument(LPCTSTR szFilename) { HANDLE hFile = INVALID_HANDLE_VALUE; - DWORD lBytesRead; - DWORD lSizeofChipset; + DWORD lBytesRead,lSizeofChipset; BYTE pbyFileSignature[16]; LPBYTE pbySig; UINT ctBytesCompared; @@ -725,21 +747,30 @@ BOOL OpenDocument(LPCTSTR szFilename) goto restore; } + // read chipset size inside file ReadFile(hFile, &lSizeofChipset, sizeof(lSizeofChipset), &lBytesRead, NULL); if (lBytesRead != sizeof(lSizeofChipset)) goto read_err; - if (lSizeofChipset!=sizeof(CHIPSET)) + if (lSizeofChipset <= sizeof(Chipset)) // actual or older chipset version { - AbortMessage(_T("This file is probably corrupted, and cannot be loaded.")); - goto restore; + // read chipset content + ZeroMemory(&Chipset,sizeof(Chipset)); // init chipset + ReadFile(hFile, &Chipset, lSizeofChipset, &lBytesRead, NULL); } + else // newer chipset version + { + // read my used chipset content + ReadFile(hFile, &Chipset, sizeof(Chipset), &lBytesRead, NULL); - ReadFile(hFile, &Chipset, lSizeofChipset, &lBytesRead, NULL); + // skip rest of chipset + SetFilePointer(hFile, lSizeofChipset-sizeof(Chipset), NULL, FILE_CURRENT); + lSizeofChipset = sizeof(Chipset); + } if (lBytesRead != lSizeofChipset) goto read_err; Chipset.Port0 = NULL; Chipset.Port1 = NULL; Chipset.Port2 = NULL; - SetWindowPos(hWnd,NULL,Chipset.nPosX,Chipset.nPosY,0,0,SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE); + SetWindowLocation(hWnd,Chipset.nPosX,Chipset.nPosY); if (szCurrentKml == NULL) { @@ -823,7 +854,7 @@ BOOL OpenDocument(LPCTSTR szFilename) LoadBreakpointList(hFile); // load debugger breakpoint list RomSwitch(Chipset.Bank_FF); // reload ROM view of HP49G and map memory - + if (Chipset.wRomCrc != CrcRom()) // ROM changed { CpuReset(); @@ -873,7 +904,7 @@ BOOL SaveDocument(VOID) SetFilePointer(hCurrentFile,0,NULL,FILE_BEGIN); // get document signature - pbySig = (Chipset.type=='6' || Chipset.type=='A') + pbySig = (Chipset.type=='6' || Chipset.type=='A') ? pbySignatureA : (Chipset.type=='E' ? pbySignatureB : ((Chipset.type!='X') ? pbySignatureE : pbySignatureV)); if (!WriteFile(hCurrentFile, pbySig, 16, &lBytesWritten, NULL)) @@ -947,14 +978,16 @@ BOOL SaveDocumentAs(LPCTSTR szFilename) BOOL SaveBackup(VOID) { - RECT Rect; + WINDOWPLACEMENT wndpl; if (pbyRom == NULL) return FALSE; // save window position - GetWindowRect(hWnd, &Rect); // update saved window position - Chipset.nPosX = (SWORD)Rect.left; - Chipset.nPosY = (SWORD)Rect.top; + _ASSERT(hWnd); // window open + wndpl.length = sizeof(wndpl); // update saved window position + GetWindowPlacement(hWnd, &wndpl); + Chipset.nPosX = (SWORD) wndpl.rcNormalPosition.left; + Chipset.nPosY = (SWORD) wndpl.rcNormalPosition.top; lstrcpy(szBackupFilename, szCurrentFilename); lstrcpy(szBackupKml, szCurrentKml); @@ -1017,7 +1050,7 @@ BOOL RestoreBackup(VOID) MapPort2((nArgc < 3) ? szPort2Filename : ppArgv[2]); } } - SetWindowPos(hWnd,NULL,Chipset.nPosX,Chipset.nPosY,0,0,SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE); + SetWindowLocation(hWnd,Chipset.nPosX,Chipset.nPosY); UpdateWindowStatus(); Map(0x00,0xFF); return TRUE; @@ -1031,7 +1064,7 @@ BOOL ResetBackup(VOID) if (BackupChipset.Port0) LocalFree(BackupChipset.Port0); if (BackupChipset.Port1) LocalFree(BackupChipset.Port1); if (BackupChipset.Port2) LocalFree(BackupChipset.Port2); - FillMemory(&BackupChipset,sizeof(BackupChipset),0); + ZeroMemory(&BackupChipset,sizeof(BackupChipset)); bBackup = FALSE; UpdateWindowStatus(); return TRUE; @@ -1047,7 +1080,7 @@ BOOL ResetBackup(VOID) static VOID InitializeOFN(LPOPENFILENAME ofn) { - FillMemory((LPVOID)ofn, sizeof(OPENFILENAME), 0); + ZeroMemory((LPVOID)ofn, sizeof(OPENFILENAME)); ofn->lStructSize = sizeof(OPENFILENAME); ofn->hwndOwner = hWnd; ofn->Flags = OFN_EXPLORER|OFN_HIDEREADONLY; @@ -1059,7 +1092,7 @@ BOOL GetOpenFilename(VOID) OPENFILENAME ofn; InitializeOFN(&ofn); - ofn.lpstrFilter = + ofn.lpstrFilter = _T("Emu38 Document (*.E38)\0*.E38\0") _T("Emu39 Document (*.E39)\0*.E39\0") _T("Emu48 Document (*.E48)\0*.E48\0") @@ -1102,7 +1135,7 @@ BOOL GetSaveAsFilename(VOID) OPENFILENAME ofn; InitializeOFN(&ofn); - ofn.lpstrFilter = + ofn.lpstrFilter = _T("Emu38 Document (*.E38)\0*.E38\0") _T("Emu39 Document (*.E39)\0*.E39\0") _T("Emu48 Document (*.E48)\0*.E48\0") @@ -1218,10 +1251,10 @@ BOOL LoadObject(LPCTSTR szFilename) // separated stack writing part wError = WriteStack(lpBuf,dwFileSizeLow); if (wError == S_ERR_BINARY) - AbortMessage(_T("The HP48 has not enough free memory left to load this binary file.")); + AbortMessage(_T("The calculator does not have enough\nfree memory to load this binary file.")); if (wError == S_ERR_ASCII) - AbortMessage(_T("The HP48 has not enough free memory left to load this text file.")); + AbortMessage(_T("The calculator does not have enough\nfree memory to load this text file.")); LocalFree(lpBuf); return (wError == S_ERR_NO); @@ -1346,7 +1379,7 @@ static HPALETTE CreateBIPalette(LPBITMAPINFOHEADER lpbi) pPal->palPalEntry[i].peGreen = green; pPal->palPalEntry[i].peBlue = blue; pPal->palPalEntry[i].peFlags = (BYTE)0; - + if (!(red += 32)) if (!(green += 32)) blue += 64; @@ -1366,7 +1399,7 @@ HBITMAP LoadBitmapFile(LPCTSTR szFilename) HBITMAP hBitmap; LPBITMAPFILEHEADER pBmfh; LPBITMAPINFO pBmi; - + if (hRomFile == NULL) return NULL; SetCurrentDirectory(szEmu48Directory); hFile = CreateFile(szFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); @@ -1379,19 +1412,19 @@ HBITMAP LoadBitmapFile(LPCTSTR szFilename) return NULL; } // opened with GENERIC_READ -> PAGE_READONLY -> FILE_MAP_READ - pbyFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); + pbyFile = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); if (pbyFile == NULL) { CloseHandle(hMap); CloseHandle(hFile); return NULL; } - + hBitmap = NULL; pBmfh = (LPBITMAPFILEHEADER)pbyFile; if (pBmfh->bfType != 0x4D42) goto quit; // "BM" pBmi = (LPBITMAPINFO)(pbyFile+sizeof(BITMAPFILEHEADER)); - + _ASSERT(hPalette == NULL); // resource free hPalette = CreateBIPalette(&pBmi->bmiHeader); // save old palette @@ -1423,7 +1456,7 @@ HBITMAP LoadBitmapFile(LPCTSTR szFilename) pBmi, DIB_RGB_COLORS); } _ASSERT(hBitmap != NULL); - + quit: UnmapViewOfFile(pbyFile); CloseHandle(hMap); diff --git a/sources/Emu48/I28F160.C b/sources/Emu48/I28F160.C index c7d7408..36e70fe 100644 --- a/sources/Emu48/I28F160.C +++ b/sources/Emu48/I28F160.C @@ -10,8 +10,6 @@ #include "Emu48.h" #include "i28f160.h" -#define _64KB (64*1024) // define 64KB - #define ARRAYSIZEOF(a) (sizeof(a) / sizeof(a[0])) // Flash Command Set @@ -56,11 +54,11 @@ #define WRS_LOCK_BITS 8 // Set/Clear Block Lock-Bits // read state defines -#define RDS_DATA 0 -#define RDS_ID 1 -#define RDS_QUERY 2 -#define RDS_SR 3 -#define RDS_XSR 4 +#define RDS_DATA 0 // data read +#define RDS_ID 1 // read identifier codes +#define RDS_QUERY 2 // read query +#define RDS_SR 3 // read status register +#define RDS_XSR 4 // read extended status register // global data WSMSET WSMset; @@ -117,9 +115,9 @@ static CONST BYTE byQueryTab[] = 0xB0, // 00, Manufacturer Code 0xD0, // 01, Device Code (16 Mbit) 0x00, // 02, Block Lock Configuration + 0x02, // 03, ?? - 0x00, // 03, Reserved for vendor-specific information - 0x00, // 04, " + 0x00, // 04, Reserved for vendor-specific information 0x00, // 05, " 0x00, // 06, " 0x00, // 07, " @@ -147,9 +145,9 @@ static CONST BYTE byQueryTab[] = 0x00, // 1A, " // System interface information - 0x27, // 1B, Vcc Logic Supply Minimum Program/Erase Voltage + 0x30, // 1B, Vcc Logic Supply Minimum Program/Erase Voltage (0x27 intel doc, 0x30 real chip) 0x55, // 1C, Vcc Logic Supply Maximum Program/Erase Voltage - 0x27, // 1D, Vpp [Programming] Supply Minimum Program/Erase Voltage + 0x30, // 1D, Vpp [Programming] Supply Minimum Program/Erase Voltage (0x27 intel doc, 0x30 real chip) 0x55, // 1E, Vpp [Programming] Supply Maximum Program/Erase Voltage 0x03, // 1F, Typical Time-Out per Single Byte/Word Program 0x06, // 20, Typical Time-Out for Max. Buffer Write @@ -184,7 +182,8 @@ static CONST BYTE byQueryTab[] = 0x03, // 3B, Block Status Register Mask 0x00, // 3C, " 0x50, // 3D, Vcc Logic Supply Optimum Program/Erase voltage - 0x50 // 3E, Vpp [Programming] Supply Optimum Program/Erase voltage + 0x50, // 3E, Vpp [Programming] Supply Optimum Program/Erase voltage + 0x00 // 3F, ?? }; @@ -285,8 +284,10 @@ static VOID WrStateE8(DWORD d) // write to buffer number of byte static VOID WrStateE8N(BYTE a, DWORD d) { - _ASSERT(a <= 0x1F); // check buffer size - a &= 0x1F; // maximum write buffer size + if (WRITE_BUFFER == a) return; // got another "Write Command" instead of length + + _ASSERT(a < (1 << byQueryTab[0x2A])); // check buffer size + a &= (1 << byQueryTab[0x2A]) - 1; // maximum write buffer size WSMset.byWrite1No += a; // save no. of byte to program WSMset.byWrite1Size = a; // save size to check write buffer boundaries WSMset.dwWrite1Addr = d; // byte block address of buffer1 @@ -302,8 +303,10 @@ static VOID WrStateE8D(BYTE a, DWORD d) // first data byte if (WSMset.byWrite1No == WSMset.byWrite1Size + 1) { + DWORD dwBlockMask = ~(((byQueryTab[0x30] << 8) | byQueryTab[0x2F]) * 256 - 1); + // same block - if ((WSMset.dwWrite1Addr & ~(_64KB-1)) == (d & ~(_64KB-1))) + if ((WSMset.dwWrite1Addr & dwBlockMask) == (d & dwBlockMask)) { WSMset.dwWrite1Addr = d; // byte block address of buffer1 WSMset.pbyWrite1[0] = a; // save byte @@ -356,14 +359,9 @@ static VOID WrStateE8C(BYTE a, DWORD d) a = WSMset.pbyWrite1[byPos]; // get char from buffer _ASSERT(d+1 < dwRomSize); // address valid? - *(pbyRom+d) &= (a & 0x0f); // write LSB - if (*(pbyRom+d) != (a & 0x0f)) // check writing - WSMset.byStatusReg |= BWSLBS; - ++d; // next nibble - *(pbyRom+d) &= (a >> 4); // write MSB - if (*(pbyRom+d) != (a >> 4)) // check writing - WSMset.byStatusReg |= BWSLBS; - ++d; // next nibble + // no error set in BWSLBS, because I could alway program a "0" + *(pbyRom+d++) &= (a & 0x0F); // write LSB + *(pbyRom+d++) &= (a >> 4); // write MSB } } else @@ -392,9 +390,9 @@ static VOID WrState40D(BYTE a, DWORD d) { d <<= 1; // nibble start address _ASSERT(d+1 < dwRomSize); // address valid? - // no error set in BWSLBS, because i could alway program a "0" - *(pbyRom+d++) &= (a & 0x0f); // write LSB - *(pbyRom+d++) &= (a >> 4); // write MSB + // no error set in BWSLBS, because I could alway program a "0" + *(pbyRom+d++) &= (a & 0x0F); // write LSB + *(pbyRom+d) &= (a >> 4); // write MSB WSMset.byStatusReg |= WSMS; // data written WSMset.uWrState = WRS_DATA; return; @@ -423,14 +421,13 @@ static VOID WrState20C(BYTE a, DWORD d) } else { - LPBYTE pbyBlock; - UINT i; + DWORD dwBlockSize = ((byQueryTab[0x30] << 8) | byQueryTab[0x2F]) * 256; - d &= ~(_64KB-1); // start of 64KB block - _ASSERT(2*(d+_64KB) <= dwRomSize); // address valid? - pbyBlock = pbyRom + (d << 1); // nibble start address - for (i = 0; i < 2*_64KB; ++i) // write 128K nibble - *pbyBlock++ = 0xf; // write nibble + d &= ~(dwBlockSize-1); // start of block + dwBlockSize *= 2; // block size in nibbles + _ASSERT(d+dwBlockSize <= dwRomSize); // address valid? + // write 128K nibble + FillMemory(pbyRom + (d << 1),dwBlockSize,0x0F); } } else @@ -456,32 +453,32 @@ static VOID WrState30(DWORD d) // full chip erase confirm static VOID WrState30C(BYTE a, DWORD d) { - LPBYTE pbyBlock; - UINT i,j; - if (CONFIRM == a) // chip erase confirm? { - pbyBlock = pbyRom; - for (i = 0; i < 32; ++i) // check all blocks + UINT i; + + WORD wNoOfBlocks = (byQueryTab[0x2E] << 8) | byQueryTab[0x2D]; + DWORD dwBlockSize = ((byQueryTab[0x30] << 8) | byQueryTab[0x2F]) * 256; + + LPBYTE pbyBlock = pbyRom; + + dwBlockSize *= 2; // block size in nibbles + + for (i = 0; i <= wNoOfBlocks; ++i) // check all blocks { - _ASSERT(2*((i+1)*_64KB) <= dwRomSize); + _ASSERT((i+1)*dwBlockSize <= dwRomSize); _ASSERT(i < ARRAYSIZEOF(WSMset.byLockCnfg)); // lock bit of block is set & WP# = low, locked blocks cannot be erased - if ((WSMset.byLockCnfg[i] & 1) != 0 && bWP == FALSE) - { - pbyBlock += 2*_64KB; // next block - WSMset.byStatusReg |= ECLBS; // error in block erasure - // WSMset.byStatusReg |= DPS; // lock bit detected - } - else + if ((WSMset.byLockCnfg[i] & 1) == 0 || bWP != FALSE) { WSMset.byLockCnfg[i] = 0; // clear block lock bit // write 128K nibble - for (j = 0; j < 2*_64KB; ++j) - *pbyBlock++ = 0xf; // write nibble + FillMemory(pbyBlock,dwBlockSize,0x0F); } + + pbyBlock += dwBlockSize; // next block } } else @@ -535,12 +532,14 @@ static VOID WrState60D(BYTE a, DWORD d) if (bWP) // WP# = high, can change block lock status WSMset.byLockCnfg[d>>16] = 1; // set block lock bit else - WSMset.byStatusReg |= DPS; // device protect detect, WP# = low + WSMset.byStatusReg |= (BWSLBS | DPS); // device protect detect, WP# = low break; case CONFIRM: // clear block lock bits if (bWP) // WP# = high, can change block lock status { - for (i = 0; i < 32; ++i) // clear all lock bits + WORD wNoOfBlocks = (byQueryTab[0x2E] << 8) | byQueryTab[0x2D]; + + for (i = 0; i <= wNoOfBlocks; ++i) // clear all lock bits { _ASSERT(i < ARRAYSIZEOF(WSMset.byLockCnfg)); WSMset.byLockCnfg[i] = 0; // clear block lock bit @@ -548,7 +547,7 @@ static VOID WrState60D(BYTE a, DWORD d) } else { - WSMset.byStatusReg |= DPS; // device protect detect, WP# = low + WSMset.byStatusReg |= (ECLBS | DPS); // device protect detect, WP# = low } break; default: // improper command sequence @@ -578,11 +577,35 @@ static BYTE RdStateId(DWORD d) BYTE byData; d >>= 1; // A0 is not connected, ignore it - _ASSERT((d & 0xFF) < 0x10); // id with valid address? - if ((d & 0xFF) != 0x02) // id code request + if ((d & 0x03) != 0x02) // id code request { + d &= 0x03; // data repetition + byData = byQueryTab[d]; // get data from first 4 bytes id/query table + } + else // block lock table + { + UINT uIndex = d >> 15; // index into lock table + _ASSERT(uIndex < ARRAYSIZEOF(WSMset.byLockCnfg)); + byData = WSMset.byLockCnfg[uIndex]; // get data from block lock table + + d &= 0x1F; // data repetition + if (d >= 4) byData |= 0x02; // set bit 1 on wrong ID adresses + } + return byData; +} + +// read query +static BYTE RdStateQuery(DWORD d) +{ + BYTE byData; + + d >>= 1; // A0 is not connected, ignore it + if ((d & 0x7F) != 0x02) // query request + { + d &= 0x7F; // data repetition + // get data from id/query table - byData = (d < ARRAYSIZEOF(byQueryTab)) ? byQueryTab[d] : 0; + byData = (d >= 0x40 && d < 0x50) ? 0 : byQueryTab[d&0x3F]; } else // block lock table { @@ -593,16 +616,6 @@ static BYTE RdStateId(DWORD d) return byData; } -// read query -static BYTE RdStateQuery(DWORD d) -{ - d >>= 1; // A0 is not connected, ignore it - // query with valid address? - _ASSERT(d >= 0x10 && d < ARRAYSIZEOF(byQueryTab)); - // get data from query table - return (d < ARRAYSIZEOF(byQueryTab)) ? byQueryTab[d] : 0; -} - // read status register static BYTE RdStateSR(DWORD d) { @@ -683,9 +696,9 @@ VOID FlashWrite(BYTE *a, DWORD d, UINT s) v = fnRdState[WSMset.uRdState](p); if (d & 1) // odd address - v = (v & 0x0f) | (*a << 4); // replace MSB + v = (v & 0x0F) | (*a << 4); // replace MSB else // even address - v = (v & 0xf0) | *a; // replace LSB + v = (v & 0xF0) | *a; // replace LSB ++a; ++d; --s; } diff --git a/sources/Emu48/KEYBOARD.C b/sources/Emu48/KEYBOARD.C index 2c2fc1f..fa7e151 100644 --- a/sources/Emu48/KEYBOARD.C +++ b/sources/Emu48/KEYBOARD.C @@ -91,7 +91,7 @@ VOID KeyboardEvent(BOOL bPress, UINT out, UINT in) } else { - _ASSERT(out < sizeof(Chipset.Keyboard_Row) / sizeof(Chipset.Keyboard_Row[0])); + _ASSERT(out < ARRAYSIZEOF(Chipset.Keyboard_Row)); if (bPress) // key pressed Chipset.Keyboard_Row[out] |= in; // set key marker in keyboard row else diff --git a/sources/Emu48/KML.C b/sources/Emu48/KML.C index 6894ec1..28296cd 100644 --- a/sources/Emu48/KML.C +++ b/sources/Emu48/KML.C @@ -11,49 +11,49 @@ #include "Emu48.h" #include "kml.h" -static VOID FatalError(); -static VOID InitLex(LPTSTR szScript); -static VOID CleanLex(); -static VOID SkipWhite(UINT nMode); -static TokenId ParseToken(UINT nMode); -static DWORD ParseInteger(); -static LPTSTR ParseString(); -static TokenId Lex(UINT nMode); -static Line* ParseLine(TokenId eCommand); -static Line* IncludeLines(LPCTSTR szFilename); -static Line* ParseLines(); -static Block* ParseBlock(TokenId eBlock); -static Block* IncludeBlocks(LPCTSTR szFilename); -static Block* ParseBlocks(); -static VOID FreeLines(Line* pLine); -static VOID PressButton(UINT nId); -static VOID ReleaseButton(UINT nId); -static VOID PressButtonById(UINT nId); -static VOID ReleaseButtonById(UINT nId); -static LPTSTR GetStringParam(Block* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam); -static DWORD GetIntegerParam(Block* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam); -static Line* SkipLines(Line* pLine, TokenId eCommand); -static Line* If(Line* pLine, BOOL bCondition); -static Line* RunLine(Line* pLine); -static Block* LoadKMLGlobal(LPCTSTR szFilename); +static VOID FatalError(VOID); +static VOID InitLex(LPTSTR szScript); +static VOID CleanLex(VOID); +static VOID SkipWhite(UINT nMode); +static TokenId ParseToken(UINT nMode); +static DWORD ParseInteger(VOID); +static LPTSTR ParseString(VOID); +static TokenId Lex(UINT nMode); +static KmlLine* ParseLine(TokenId eCommand); +static KmlLine* IncludeLines(LPCTSTR szFilename); +static KmlLine* ParseLines(VOID); +static KmlBlock* ParseBlock(TokenId eBlock); +static KmlBlock* IncludeBlocks(LPCTSTR szFilename); +static KmlBlock* ParseBlocks(VOID); +static VOID FreeLines(KmlLine* pLine); +static VOID PressButton(UINT nId); +static VOID ReleaseButton(UINT nId); +static VOID PressButtonById(UINT nId); +static VOID ReleaseButtonById(UINT nId); +static LPTSTR GetStringParam(KmlBlock* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam); +static DWORD GetIntegerParam(KmlBlock* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam); +static KmlLine* SkipLines(KmlLine* pLine, TokenId eCommand); +static KmlLine* If(KmlLine* pLine, BOOL bCondition); +static KmlLine* RunLine(KmlLine* pLine); +static KmlBlock* LoadKMLGlobal(LPCTSTR szFilename); -Block* pKml; -static Block* pVKey[256]; -static BYTE byVKeyMap[256]; -static Button pButton[256]; -static Annunciator pAnnunciator[6]; -static UINT nButtons = 0; -static UINT nScancodes = 0; -static UINT nAnnunciators = 0; -static BOOL bDebug = TRUE; -static UINT nLexLine; -static UINT nLexInteger; -static UINT nBlocksIncludeLevel; -static UINT nLinesIncludeLevel; -static DWORD nKMLFlags = 0; -static LPTSTR szLexString; -static LPTSTR szText; -static LPTSTR szLexDelim[] = +KmlBlock* pKml; +static KmlBlock* pVKey[256]; +static BYTE byVKeyMap[256]; +static KmlButton pButton[256]; +static KmlAnnunciator pAnnunciator[6]; +static UINT nButtons = 0; +static UINT nScancodes = 0; +static UINT nAnnunciators = 0; +static BOOL bDebug = TRUE; +static UINT nLexLine; +static UINT nLexInteger; +static UINT nBlocksIncludeLevel; +static UINT nLinesIncludeLevel; +static DWORD nKMLFlags = 0; +static LPTSTR szLexString; +static LPTSTR szText; +static LPTSTR szLexDelim[] = { _T(""), _T(" \t\n\r"), @@ -61,7 +61,7 @@ static LPTSTR szLexDelim[] = _T(" \t\r") }; -static Token pLexToken[] = +static KmlToken pLexToken[] = { {TOK_ANNUNCIATOR,000001,11,_T("Annunciator")}, {TOK_BACKGROUND, 000000,10,_T("Background")}, @@ -279,7 +279,7 @@ static VOID CreateKmlList() do { KmlScript* pScript; - Block* pBlock; + KmlBlock* pBlock; LPTSTR szTitle; pBlock = LoadKMLGlobal(pFindFileData.cFileName); @@ -457,7 +457,7 @@ fail: //# //################ -static VOID FatalError() +static VOID FatalError(VOID) { PrintfToLog(_T("Fatal Error at line %i"), nLexLine); szText[0] = 0; @@ -471,7 +471,7 @@ static VOID InitLex(LPTSTR szScript) return; } -static VOID CleanLex() +static VOID CleanLex(VOID) { nLexLine = 0; nLexInteger = 0; @@ -574,7 +574,7 @@ static TokenId ParseToken(UINT nMode) return TOK_NONE; } -static DWORD ParseInteger() +static DWORD ParseInteger(VOID) { DWORD nNum = 0; while (_istdigit(*szText)) @@ -585,7 +585,7 @@ static DWORD ParseInteger() return nNum; } -static LPTSTR ParseString() +static LPTSTR ParseString(VOID) { LPTSTR szString; LPTSTR szBuffer; @@ -642,12 +642,12 @@ static TokenId Lex(UINT nMode) return ParseToken(nMode); } -static Line* ParseLine(TokenId eCommand) +static KmlLine* ParseLine(TokenId eCommand) { - UINT i, j; - DWORD nParams; - TokenId eToken; - Line* pLine; + UINT i, j; + DWORD nParams; + TokenId eToken; + KmlLine* pLine; i = 0; while (pLexToken[i].nLen) @@ -658,7 +658,7 @@ static Line* ParseLine(TokenId eCommand) if (pLexToken[i].nLen == 0) return NULL; j = 0; - pLine = LocalAlloc(LPTR,sizeof(Line)); + pLine = LocalAlloc(LPTR,sizeof(KmlLine)); pLine->eCommand = eCommand; nParams = pLexToken[i].nParams; loop: @@ -712,13 +712,13 @@ errline: return NULL; } -static Line* IncludeLines(LPCTSTR szFilename) +static KmlLine* IncludeLines(LPCTSTR szFilename) { - HANDLE hFile; - LPTSTR lpbyBuf; - UINT uOldLine; - LPTSTR szOldText; - Line* pLine; + HANDLE hFile; + LPTSTR lpbyBuf; + UINT uOldLine; + LPTSTR szOldText; + KmlLine* pLine; SetCurrentDirectory(szEmu48Directory); hFile = CreateFile(szFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); @@ -752,12 +752,12 @@ static Line* IncludeLines(LPCTSTR szFilename) return pLine; } -static Line* ParseLines() +static KmlLine* ParseLines(VOID) { - Line* pLine; - Line* pFirst = NULL; - TokenId eToken; - UINT nLevel = 0; + KmlLine* pLine; + KmlLine* pFirst = NULL; + TokenId eToken; + UINT nLevel = 0; while ((eToken = Lex(LEX_COMMAND))) { @@ -824,15 +824,15 @@ abort: return NULL; } -static Block* ParseBlock(TokenId eType) +static KmlBlock* ParseBlock(TokenId eType) { - UINT u1; - Block* pBlock; - TokenId eToken; + UINT u1; + KmlBlock* pBlock; + TokenId eToken; nLinesIncludeLevel = 0; - pBlock = LocalAlloc(LPTR,sizeof(Block)); + pBlock = LocalAlloc(LPTR,sizeof(KmlBlock)); pBlock->eType = eType; u1 = 0; @@ -880,13 +880,13 @@ static Block* ParseBlock(TokenId eType) return pBlock; } -static Block* IncludeBlocks(LPCTSTR szFilename) +static KmlBlock* IncludeBlocks(LPCTSTR szFilename) { - HANDLE hFile; - LPTSTR lpbyBuf; - UINT uOldLine; - LPTSTR szOldText; - Block* pFirst; + HANDLE hFile; + LPTSTR lpbyBuf; + UINT uOldLine; + LPTSTR szOldText; + KmlBlock* pFirst; SetCurrentDirectory(szEmu48Directory); hFile = CreateFile(szFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); @@ -920,11 +920,11 @@ static Block* IncludeBlocks(LPCTSTR szFilename) return pFirst; } -static Block* ParseBlocks() +static KmlBlock* ParseBlocks(VOID) { TokenId eToken; - Block* pFirst = NULL; - Block* pBlock; + KmlBlock* pFirst = NULL; + KmlBlock* pBlock; while ((eToken=Lex(LEX_BLOCK))!=TOK_NONE) { @@ -975,9 +975,9 @@ abort: //# //################ -static VOID InitGlobal(Block* pBlock) +static VOID InitGlobal(KmlBlock* pBlock) { - Line* pLine = pBlock->pFirstLine; + KmlLine* pLine = pBlock->pFirstLine; while (pLine) { switch (pLine->eCommand) @@ -1051,9 +1051,9 @@ static VOID InitGlobal(Block* pBlock) return; } -static Line* InitBackground(Block* pBlock) +static KmlLine* InitBackground(KmlBlock* pBlock) { - Line* pLine = pBlock->pFirstLine; + KmlLine* pLine = pBlock->pFirstLine; while (pLine) { switch (pLine->eCommand) @@ -1076,9 +1076,9 @@ static Line* InitBackground(Block* pBlock) return NULL; } -static Line* InitLcd(Block* pBlock) +static KmlLine* InitLcd(KmlBlock* pBlock) { - Line* pLine = pBlock->pFirstLine; + KmlLine* pLine = pBlock->pFirstLine; while (pLine) { switch (pLine->eCommand) @@ -1105,11 +1105,11 @@ static Line* InitLcd(Block* pBlock) return NULL; } -static Line* InitAnnunciator(Block* pBlock) +static KmlLine* InitAnnunciator(KmlBlock* pBlock) { - Line* pLine = pBlock->pFirstLine; + KmlLine* pLine = pBlock->pFirstLine; UINT nId = pBlock->nId-1; - if (nId >= 6) + if (nId >= ARRAYSIZEOF(pAnnunciator)) { PrintfToLog(_T("Wrong Annunciator Id %i"), nId); return NULL; @@ -1141,9 +1141,9 @@ static Line* InitAnnunciator(Block* pBlock) return NULL; } -static VOID InitButton(Block* pBlock) +static VOID InitButton(KmlBlock* pBlock) { - Line* pLine = pBlock->pFirstLine; + KmlLine* pLine = pBlock->pFirstLine; UINT nLevel = 0; if (nButtons>=256) { @@ -1216,7 +1216,7 @@ static VOID InitButton(Block* pBlock) //# //################ -static Line* SkipLines(Line* pLine, TokenId eCommand) +static KmlLine* SkipLines(KmlLine* pLine, TokenId eCommand) { UINT nLevel = 0; while (pLine) @@ -1238,7 +1238,7 @@ static Line* SkipLines(Line* pLine, TokenId eCommand) return pLine; } -static Line* If(Line* pLine, BOOL bCondition) +static KmlLine* If(KmlLine* pLine, BOOL bCondition) { pLine = pLine->pNext; if (bCondition) @@ -1274,7 +1274,7 @@ static Line* If(Line* pLine, BOOL bCondition) return pLine; } -static Line* RunLine(Line* pLine) +static KmlLine* RunLine(KmlLine* pLine) { switch (pLine->eCommand) { @@ -1321,11 +1321,11 @@ static Line* RunLine(Line* pLine) //# //################ -static VOID FreeLines(Line* pLine) +static VOID FreeLines(KmlLine* pLine) { while (pLine) { - Line* pThisLine = pLine; + KmlLine* pThisLine = pLine; UINT i = 0; DWORD nParams; while (pLexToken[i].nLen) // search in all token definitions @@ -1351,11 +1351,11 @@ static VOID FreeLines(Line* pLine) return; } -VOID FreeBlocks(Block* pBlock) +VOID FreeBlocks(KmlBlock* pBlock) { while (pBlock) { - Block* pThisBlock = pBlock; + KmlBlock* pThisBlock = pBlock; pBlock = pBlock->pNext; FreeLines(pThisBlock->pFirstLine); LocalFree(pThisBlock); @@ -1411,13 +1411,13 @@ VOID KillKML(VOID) //# //################ -static LPTSTR GetStringParam(Block* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam) +static LPTSTR GetStringParam(KmlBlock* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam) { while (pBlock) { if (pBlock->eType == eBlock) { - Line* pLine = pBlock->pFirstLine; + KmlLine* pLine = pBlock->pFirstLine; while (pLine) { if (pLine->eCommand == eCommand) @@ -1432,13 +1432,13 @@ static LPTSTR GetStringParam(Block* pBlock, TokenId eBlock, TokenId eCommand, UI return NULL; } -static DWORD GetIntegerParam(Block* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam) +static DWORD GetIntegerParam(KmlBlock* pBlock, TokenId eBlock, TokenId eCommand, UINT nParam) { while (pBlock) { if (pBlock->eType == eBlock) { - Line* pLine = pBlock->pFirstLine; + KmlLine* pLine = pBlock->pFirstLine; while (pLine) { if (pLine->eCommand == eCommand) @@ -1583,10 +1583,10 @@ static VOID DrawButton(UINT nId) else { RECT Rect; - Rect.left = x0; - Rect.right = x0 + pButton[nId].nCx; - Rect.top = y0; - Rect.bottom = y0 + pButton[nId].nCy; + Rect.left = x0 - nBackgroundX; + Rect.top = y0 - nBackgroundY; + Rect.right = Rect.left + pButton[nId].nCx; + Rect.bottom = Rect.top + pButton[nId].nCy; InvalidateRect(hWnd, &Rect, FALSE); // call WM_PAINT for background and display redraw } break; @@ -1599,10 +1599,10 @@ static VOID DrawButton(UINT nId) else { RECT Rect; - Rect.left = x0; - Rect.right = x0 + pButton[nId].nCx; - Rect.top = y0; - Rect.bottom = y0 + pButton[nId].nCy; + Rect.left = x0 - nBackgroundX; + Rect.top = y0 - nBackgroundY; + Rect.right = Rect.left + pButton[nId].nCx; + Rect.bottom = Rect.top + pButton[nId].nCy; InvalidateRect(hWnd, &Rect, FALSE); // call WM_PAINT for background and display redraw } break; @@ -1648,7 +1648,7 @@ static VOID PressButton(UINT nId) } else { - Line* pLine = pButton[nId].pOnDown; + KmlLine* pLine = pButton[nId].pOnDown; while ((pLine)&&(pLine->eCommand!=TOK_END)) { pLine = RunLine(pLine); @@ -1667,7 +1667,7 @@ static VOID ReleaseButton(UINT nId) } else { - Line* pLine = pButton[nId].pOnUp; + KmlLine* pLine = pButton[nId].pOnUp; while ((pLine)&&(pLine->eCommand!=TOK_END)) { pLine = RunLine(pLine); @@ -1759,7 +1759,7 @@ VOID DrawAnnunciator(UINT nId, BOOL bOn) UINT nSx,nSy; --nId; // zero based ID - if (nId>=6) return; + if (nId >= ARRAYSIZEOF(pAnnunciator)) return; if (bOn) { nSx = pAnnunciator[nId].nDx; // position of annunciator @@ -1794,6 +1794,9 @@ VOID DrawAnnunciator(UINT nId, BOOL bOn) static BOOL ClipButton(UINT x, UINT y, UINT nId) { + x += nBackgroundX; // source display offset + y += nBackgroundY; + return (pButton[nId].nOx<=x) && (pButton[nId].nOy<=y) &&(x<(pButton[nId].nOx+pButton[nId].nCx)) @@ -1897,7 +1900,7 @@ VOID RunKey(BYTE nId, BOOL bPressed) { if (pVKey[nId]) { - Line* pLine = pVKey[nId]->pFirstLine; + KmlLine* pLine = pVKey[nId]->pFirstLine; byVKeyMap[nId] = bPressed; while (pLine) pLine = RunLine(pLine); } @@ -1921,12 +1924,12 @@ VOID RunKey(BYTE nId, BOOL bPressed) //# //################ -static Block* LoadKMLGlobal(LPCTSTR szFilename) +static KmlBlock* LoadKMLGlobal(LPCTSTR szFilename) { - HANDLE hFile; - LPTSTR lpBuf; - Block* pBlock; - DWORD eToken; + HANDLE hFile; + LPTSTR lpBuf; + KmlBlock* pBlock; + DWORD eToken; SetCurrentDirectory(szEmu48Directory); hFile = CreateFile(szFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); @@ -1952,10 +1955,10 @@ static Block* LoadKMLGlobal(LPCTSTR szFilename) BOOL InitKML(LPCTSTR szFilename, BOOL bNoLog) { - HANDLE hFile; - LPTSTR lpBuf; - Block* pBlock; - BOOL bOk = FALSE; + HANDLE hFile; + LPTSTR lpBuf; + KmlBlock* pBlock; + BOOL bOk = FALSE; KillKML(); diff --git a/sources/Emu48/KML.H b/sources/Emu48/KML.H index e4ac63f..fdfa914 100644 --- a/sources/Emu48/KML.H +++ b/sources/Emu48/KML.H @@ -61,32 +61,32 @@ typedef enum eTokenId #define TYPE_INTEGER 01 #define TYPE_STRING 02 -typedef struct Token +typedef struct KmlToken { TokenId eId; DWORD nParams; DWORD nLen; TCHAR szName[20]; -} Token; +} KmlToken; -typedef struct Line +typedef struct KmlLine { - struct Line* pNext; + struct KmlLine* pNext; TokenId eCommand; DWORD nParam[6]; -} Line; +} KmlLine; -typedef struct Block +typedef struct KmlBlock { TokenId eType; DWORD nId; - struct Line* pFirstLine; - struct Block* pNext; -} Block; + struct KmlLine* pFirstLine; + struct KmlBlock* pNext; +} KmlBlock; #define BUTTON_NOHOLD 0x0001 #define BUTTON_VIRTUAL 0x0002 -typedef struct Button +typedef struct KmlButton { UINT nId; BOOL bDown; @@ -96,20 +96,20 @@ typedef struct Button UINT nDx, nDy; UINT nCx, nCy; UINT nOut, nIn; - Line* pOnDown; - Line* pOnUp; -} Button; + KmlLine* pOnDown; + KmlLine* pOnUp; +} KmlButton; -typedef struct Annunciator +typedef struct KmlAnnunciator { UINT nOx, nOy; UINT nDx, nDy; UINT nCx, nCy; -} Annunciator; +} KmlAnnunciator; -extern Block* pKml; +extern KmlBlock* pKml; extern BOOL DisplayChooseKml(CHAR cType); -extern VOID FreeBlocks(Block* pBlock); +extern VOID FreeBlocks(KmlBlock* pBlock); extern VOID DrawAnnunciator(UINT nId, BOOL bOn); extern VOID RefreshButtons(RECT *rc); extern VOID MouseButtonDownAt(UINT nFlags, DWORD x, DWORD y); diff --git a/sources/Emu48/MOPS.C b/sources/Emu48/MOPS.C index 00f0f61..8466ae2 100644 --- a/sources/Emu48/MOPS.C +++ b/sources/Emu48/MOPS.C @@ -27,16 +27,12 @@ #define P2MAPBASE ((BYTE)(Chipset.P2Base & ~Chipset.P2Size)) #define BSMAPBASE ((BYTE)(Chipset.BSBase & ~Chipset.BSSize)) -BOOL ioc_acc = FALSE; // flag ioc changed - BOOL bFlashRomArray = TRUE; // flag ROM mode BYTE disp = 0; // flag for update display area static LPBYTE pbyRomView[2] = {NULL,NULL}; // HP49G ROM views -static BYTE byVblRef = 0; // VBL stop reference - // CRC calculation static WORD crc_table[16] = { @@ -133,17 +129,6 @@ static __inline VOID UpdateDisplay(DWORD d, UINT s) return; } -// 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; -} - // port mapping LPBYTE RMap[256] = {NULL,}; @@ -294,8 +279,11 @@ static VOID MapP2(BYTE a, BYTE b) pbyTemp += (((Chipset.Bank_FF>>1)-1)&dwPort2Mask) << 18; } - m = (dwPort2Size*2048)-1; // real size of module, address mask for mirroring - p = (a<<12)&m; // offset to begin of P2 in nibbles + // max. size per bank is 128KB + m = (dwPort2Size > 128) ? 128 : dwPort2Size; + + m = (m * 2048) - 1; // real size of module, address mask for mirroring + p = (a << 12) & m; // offset to begin of P2 in nibbles // SX: CE2.2 = CE2 // GX: CE2.2 = BEN & /DA19 & /NCE3 @@ -673,23 +661,16 @@ VOID CpuReset(VOID) // register setting after Cpu Reset Chipset.boffset = 0; // left margin Chipset.lcounter = 0; // number of main display lines Chipset.contrast = 0; // contrast dark - Chipset.dispon = 0; // display off UpdateContrast(Chipset.contrast); // update contrast // display update when changing to run state CommSetBaud(); // new baudrate - ioc_acc = TRUE; // new IOC value, close serial port + CheckSerial(); // close serial port RomSwitch(Chipset.Bank_FF); // force new memory mapping return; } -BYTE GetLineCounter(VOID) // get line counter value -{ - _ASSERT(byVblRef < 0x40); - return (0x40 + F4096Hz() - byVblRef) & 0x3F; -} - VOID Npeek(BYTE *a, DWORD d, UINT s) { enum MMUMAP eMap; @@ -702,20 +683,23 @@ VOID Npeek(BYTE *a, DWORD d, UINT s) eMap = MapData(d); // get active memory controller if (M_IO == eMap) // I/O access { - // update CRC - Nunpack(Chipset.IORam+CRC, Chipset.crc, 4); - // update CARDSTAT - Chipset.IORam[CARDSTAT] = (Chipset.IORam[CARDCTL] & ECDT) ? Chipset.cards_status : 0; - // update SRQ2 - ReadIO(a,SRQ2,1); - // update timer1 - Chipset.IORam[TIMER1] = Chipset.t1; - // update timer2 - Nunpack(Chipset.IORam+TIMER2, ReadT2(), 8); - v = d&0x3F; - c = MIN(s,0x40-v); - memcpy(a, Chipset.IORam+v, c); + + if (v >= RBR_LSB && v <= RBR_MSB) + { + // don't read RBR content with the function ReadIO() + c = MIN(s,RBR_MSB-v+1); + memcpy(a, Chipset.IORam+v, c); + } + else + { + // all others registers + if (v < RBR_LSB && (v+s) > RBR_LSB) + c = MIN(s,RBR_LSB-v); + else + c = MIN(s,0x40-v); + ReadIO(a,v,c); + } } else { @@ -832,7 +816,7 @@ VOID Nread(BYTE *a, DWORD d, UINT s) } } - for (u=0; u>4; break; case 0x03: *a = 0; @@ -1125,7 +1109,7 @@ VOID ReadIO(BYTE *a, DWORD d, DWORD s) Chipset.IORam[d] = (nCurrentClass != 40) ? (Chipset.IORam[d] & ~IRI) // HP39G : (Chipset.IORam[d] | IRI); // HP40G - } + } *a = Chipset.IORam[d]; // return IR CONTROL value #if defined DEBUG_SERIAL { @@ -1252,22 +1236,20 @@ VOID WriteIO(BYTE *a, DWORD d, DWORD s) case 0x00: if ((c^Chipset.IORam[d])&DON) // DON bit changed { - Chipset.dispon = c>>3; disp |= (DISP_POINTER | DISP_MAIN | DISP_MENUE); // adjust VBL counter start/stop values - if (Chipset.dispon) // display is on + if ((c & DON) != 0) // set display on { - // get positive VBL difference between now and stop time - byVblRef = ( 0x40 - + F4096Hz() - - (((Chipset.IORam[0x29] << 4) | Chipset.IORam[0x28]) & 0x3F) - ) & 0x3F; + Chipset.IORam[d] |= DON; // for StartDisplay(); + UpdateContrast(Chipset.contrast); + StartDisplay((BYTE) Chipset.lcounter); // start display update } else // display is off { - BYTE a[2]; - ReadIO(a,0x28,2); // update VBL at display off time + Chipset.IORam[d] &= ~DON; + UpdateContrast(Chipset.contrast); + StopDisplay(); // stop display update } } // OFF bits changed @@ -1420,8 +1402,8 @@ VOID WriteIO(BYTE *a, DWORD d, DWORD s) Chipset.IORam[TBR_MSB] = 0; } Chipset.IORam[d]=c; - UpdateUSRQ(); // update USRQ - ioc_acc = TRUE; // new IOC value + CheckSerial(); // handle UART on/off + if (UpdateUSRQ()) INTERRUPT; // update USRQ bit #if defined DEBUG_SERIAL { TCHAR buffer[256]; @@ -1436,7 +1418,12 @@ VOID WriteIO(BYTE *a, DWORD d, DWORD s) case 0x11: if (Chipset.IORam[IOC] & SON) { - Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); + EnterCriticalSection(&csIOLock); + { + // critical section because of RER access + Chipset.IORam[d]=(Chipset.IORam[d]&8)|(c&7); + } + LeaveCriticalSection(&csIOLock); #if defined DEBUG_SERIAL { TCHAR buffer[256]; @@ -1453,6 +1440,7 @@ VOID WriteIO(BYTE *a, DWORD d, DWORD s) if (Chipset.IORam[IOC] & SON) { Chipset.IORam[d]=c; + CommTxBRK(); // update BRK condition #if defined DEBUG_SERIAL { TCHAR buffer[256]; @@ -1466,7 +1454,7 @@ VOID WriteIO(BYTE *a, DWORD d, DWORD s) // 00113 = HP:CRER // 00113 @ Serial Clear RER (writing anything clears RER bit) case 0x13: - Chipset.IORam[RCS]&=~RER; + IOBit(RCS,RER,FALSE); #if defined DEBUG_SERIAL { TCHAR buffer[256]; @@ -1478,7 +1466,6 @@ VOID WriteIO(BYTE *a, DWORD d, DWORD s) // 00114 = HP:RBR // 00114 @ Serial Receive Buffer Register (Reading clears RBF bit) -// 00114 @ [RX RER RBZ RBF] case 0x14: break; // read-only case 0x15: break; // read-only @@ -1489,8 +1476,6 @@ VOID WriteIO(BYTE *a, DWORD d, DWORD s) if (Chipset.IORam[IOC] & SON) { Chipset.IORam[d]=c; - Chipset.IORam[TCS]|=TBF; - UpdateUSRQ(); // update USRQ tbr_acc = TRUE; // new TBR value #if defined DEBUG_SERIAL { @@ -1700,7 +1685,11 @@ VOID WriteIO(BYTE *a, DWORD d, DWORD s) } } - if (tbr_acc) CommTransmit(); // transmit char + if (tbr_acc) // addr 0x116-0x117 changed + { + IOBit(TCS,TBF,TRUE); // set transmit buffer full bit + CommTransmit(); // transmit char + } if (disp & DISP_POINTER) { diff --git a/sources/Emu48/OPCODES.C b/sources/Emu48/OPCODES.C index 67394a1..c408fda 100644 --- a/sources/Emu48/OPCODES.C +++ b/sources/Emu48/OPCODES.C @@ -16,6 +16,12 @@ #define GOYES3 {if(w.carry) o_goyes3(I);else{w.pc+=2;return;}} #define GOYES5 {if(w.carry) o_goyes5(I);else{w.pc+=2;return;}} +#if !defined BIGENDIAN +#define REG(t,r) (*((t*)&(r))) // low endian machine +#else +#define REG(t,r) (*((t*)((BYTE*)&(r)+sizeof(r)-sizeof(t)))) // big endian machine +#endif + #pragma intrinsic(memset,memcpy) #include "Ops.h" @@ -620,11 +626,11 @@ VOID o137(LPBYTE I) // CD1EX return; } -VOID o138(LPBYTE I)// D0=AS +VOID o138(LPBYTE I) // D0=AS { w.cycles+=7; w.pc+=3; - *((WORD*)&w.d0)=(WORD)Npack(w.A,4); + REG(WORD,w.d0)=(WORD)Npack(w.A,4); return; } @@ -632,14 +638,14 @@ VOID o139(LPBYTE I) // D1=AS { w.cycles+=7; w.pc+=3; - *((WORD*)&w.d1)=(WORD)Npack(w.A,4); + REG(WORD,w.d1)=(WORD)Npack(w.A,4); return; } VOID o13A(LPBYTE I) // AD0XS { DWORD d=w.d0; - *((WORD*)&w.d0)=(WORD)Npack(w.A,4); + REG(WORD,w.d0)=(WORD)Npack(w.A,4); Nunpack(w.A,d,4); w.cycles+=7; w.pc+=3; @@ -649,7 +655,7 @@ VOID o13A(LPBYTE I) // AD0XS VOID o13B(LPBYTE I) // AD1XS { DWORD d=w.d1; - *((WORD*)&w.d1)=(WORD)Npack(w.A,4); + REG(WORD,w.d1)=(WORD)Npack(w.A,4); Nunpack(w.A,d,4); w.cycles+=7; w.pc+=3; @@ -660,7 +666,7 @@ VOID o13C(LPBYTE I) // D0=CS { w.cycles+=7; w.pc+=3; - *((WORD*)&w.d0)=(WORD)Npack(w.C,4); + REG(WORD,w.d0)=(WORD)Npack(w.C,4); return; } @@ -668,24 +674,24 @@ VOID o13D(LPBYTE I) // D1=CS { w.cycles+=7; w.pc+=3; - *((WORD*)&w.d1)=(WORD)Npack(w.C,4); + REG(WORD,w.d1)=(WORD)Npack(w.C,4); return; } -VOID o13E(LPBYTE I) // AD0XS +VOID o13E(LPBYTE I) // CD0XS { DWORD d=w.d0; - *((WORD*)&w.d0)=(WORD)Npack(w.C,4); + REG(WORD,w.d0)=(WORD)Npack(w.C,4); Nunpack(w.C,d,4); w.cycles+=7; w.pc+=3; return; } -VOID o13F(LPBYTE I) // AD1XS +VOID o13F(LPBYTE I) // CD1XS { DWORD d=w.d1; - *((WORD*)&w.d1)=(WORD)Npack(w.C,4); + REG(WORD,w.d1)=(WORD)Npack(w.C,4); Nunpack(w.C,d,4); w.cycles+=7; w.pc+=3; @@ -783,7 +789,7 @@ VOID o19d2(LPBYTE I) // D0=(2) #dd { w.cycles+=4; w.pc+=4; - *((BYTE*)&w.d0)=(BYTE)Npack(I+2,2); + REG(BYTE,w.d0)=(BYTE)Npack(I+2,2); return; } @@ -791,7 +797,7 @@ VOID o1Ad4(LPBYTE I) // D0=(4) #dddd { w.cycles+=6; w.pc+=6; - *((WORD*)&w.d0)=(WORD)Npack(I+2,4); + REG(WORD,w.d0)=(WORD)Npack(I+2,4); return; } @@ -824,7 +830,7 @@ VOID o1Dd2(LPBYTE I) // D1=(2) #dd { w.cycles+=4; w.pc+=4; - *((BYTE*)&w.d1)=(BYTE)Npack(I+2,2); + REG(BYTE,w.d1)=(BYTE)Npack(I+2,2); return; } @@ -832,7 +838,7 @@ VOID o1Ed4(LPBYTE I) // D1=(4) #dddd { w.cycles+=6; w.pc+=6; - *((WORD*)&w.d1)=(WORD)Npack(I+2,4); + REG(WORD,w.d1)=(WORD)Npack(I+2,4); return; } diff --git a/sources/Emu48/OPCODES.H b/sources/Emu48/OPCODES.H index 2fd9d35..e498748 100644 --- a/sources/Emu48/OPCODES.H +++ b/sources/Emu48/OPCODES.H @@ -90,8 +90,8 @@ extern VOID o13A(LPBYTE I); // AD0XS extern VOID o13B(LPBYTE I); // AD1XS extern VOID o13C(LPBYTE I); // D0=CS extern VOID o13D(LPBYTE I); // D1=CS -extern VOID o13E(LPBYTE I); // AD0XS -extern VOID o13F(LPBYTE I); // AD1XS +extern VOID o13E(LPBYTE I); // CD0XS +extern VOID o13F(LPBYTE I); // CD1XS extern VOID o140(LPBYTE I); // DAT0=A A extern VOID o141(LPBYTE I); // DAT0=A A extern VOID o144(LPBYTE I); // DAT0=C A diff --git a/sources/Emu48/PCH.H b/sources/Emu48/PCH.H index 7ea124e..e7446fb 100644 --- a/sources/Emu48/PCH.H +++ b/sources/Emu48/PCH.H @@ -2,11 +2,14 @@ // PCH.H // +#define _WIN32_IE 0x0200 + #include #include #include #include #include +#include #include #include #include \ No newline at end of file diff --git a/sources/Emu48/RESOURCE.H b/sources/Emu48/RESOURCE.H index c56b8b2..a189496 100644 --- a/sources/Emu48/RESOURCE.H +++ b/sources/Emu48/RESOURCE.H @@ -8,19 +8,21 @@ #define IDR_DEBUG_TOOLBAR 103 #define IDR_DEBUG_CODE 104 #define IDR_DEBUG_MEM 105 -#define IDD_ABOUT 106 -#define IDD_SETTINGS 107 -#define IDD_CHOOSEKML 108 -#define IDD_KMLLOG 109 -#define IDD_DISASM 110 -#define IDD_DEBUG 111 -#define IDD_NEWVALUE 112 -#define IDD_ENTERADR 113 -#define IDD_BREAKEDIT 114 -#define IDD_ENTERBREAK 115 -#define IDD_INSTRUCTIONS 116 -#define IDD_FIND 117 -#define IDB_CHECKBOX 120 +#define IDR_DEBUG_STACK 106 +#define IDB_CHECKBOX 107 +#define IDD_ABOUT 108 +#define IDD_SETTINGS 109 +#define IDD_CHOOSEKML 110 +#define IDD_KMLLOG 111 +#define IDD_DISASM 112 +#define IDD_DEBUG 113 +#define IDD_NEWVALUE 114 +#define IDD_ENTERADR 115 +#define IDD_BREAKEDIT 116 +#define IDD_ENTERBREAK 117 +#define IDD_INSTRUCTIONS 118 +#define IDD_WRITEONLYREG 119 +#define IDD_FIND 120 #define IDC_PORT1WR 1000 #define IDC_AUTOSAVE 1001 #define IDC_AUTOSAVEONEXIT 1002 @@ -125,6 +127,10 @@ #define IDC_FIND_DATA 1101 #define IDC_FIND_ASCII 1102 #define IDC_FIND_CASE 1103 +#define IDC_ADDR20_24 1104 +#define IDC_ADDR25_27 1105 +#define IDC_ADDR28_29 1106 +#define IDC_ADDR30_34 1107 #define ID_FILE_NEW 40001 #define ID_FILE_OPEN 40002 #define ID_FILE_SAVE 40003 @@ -167,16 +173,35 @@ #define ID_DEBUG_MEM_GOD1 40042 #define ID_DEBUG_MEM_GOSTACK 40043 #define ID_DEBUG_MEM_FIND 40044 -#define ID_INFO_LASTINSTRUCTIONS 40045 -#define ID_INTR_STEPOVERINT 40046 +#define ID_DEBUG_MEM_MAP 40045 +#define ID_DEBUG_MEM_NCE1 40046 +#define ID_DEBUG_MEM_NCE2 40047 +#define ID_DEBUG_MEM_CE1 40048 +#define ID_DEBUG_MEM_CE2 40049 +#define ID_DEBUG_MEM_NCE3 40050 +#define ID_DEBUG_STACK_PUSH 40051 +#define ID_DEBUG_STACK_POP 40052 +#define ID_DEBUG_STACK_MODIFY 40053 +#define ID_INTR_STEPOVERINT 40054 +#define ID_INFO_LASTINSTRUCTIONS 40055 +#define ID_INFO_WRITEONLYREG 40056 + +// Resource definitions for Windows XP +// +#ifndef CREATEPROCESS_MANIFEST_RESOURCE_ID +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 +#endif +#ifndef RT_MANIFEST +#define RT_MANIFEST 24 +#endif // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 121 -#define _APS_NEXT_COMMAND_VALUE 40047 -#define _APS_NEXT_CONTROL_VALUE 1104 +#define _APS_NEXT_COMMAND_VALUE 40057 +#define _APS_NEXT_CONTROL_VALUE 1108 #define _APS_NEXT_SYMED_VALUE 108 #endif #endif diff --git a/sources/Emu48/SERIAL.C b/sources/Emu48/SERIAL.C index 13b3552..a1b25fc 100644 --- a/sources/Emu48/SERIAL.C +++ b/sources/Emu48/SERIAL.C @@ -20,15 +20,43 @@ #define NINT2USRQ (NINT2ERBZ || NINT2ERBF || NINT2ETBE) static HANDLE hComm = NULL; -static HANDLE hCThread = NULL; -static DWORD lSerialThreadId; +static HANDLE hCThreadTxd; +static HANDLE hCThreadEv; + +static HANDLE hEventTxd; +static BOOL bWriting; +static BYTE tbr; + static BOOL bReading; -static BYTE cBuffer[128]; +static BYTE cBuffer[32]; static WORD nRp; static DWORD dwBytesRead; -static DWORD WINAPI SerialThread(LPVOID pParam) +static DWORD WINAPI TransmitThread(LPVOID pParam) +{ + OVERLAPPED osWr = { 0 }; + + osWr.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); + + while (bWriting) + { + WaitForSingleObject(hEventTxd,INFINITE); + if (bWriting) + { + DWORD dwWritten; + if (!WriteFile(hComm,(LPCVOID) &tbr,1,&dwWritten,&osWr)) + if (GetLastError() == ERROR_IO_PENDING) + GetOverlappedResult(hComm,&osWr,&dwWritten,TRUE); + } + } + + CloseHandle(osWr.hEvent); // close write event handle + return 0; + UNREFERENCED_PARAMETER(pParam); +} + +static DWORD WINAPI EventThread(LPVOID pParam) { DWORD dwEvent; @@ -47,6 +75,19 @@ static DWORD WINAPI SerialThread(LPVOID pParam) SetEvent(hEventShutdn); // wake up emulation thread } } + if (dwEvent & EV_TXEMPTY) // signal transmit buffer empty + { + IOBit(TCS,TBZ,FALSE); // clear transmitter busy bit + CommTransmit(); // check for new char to transmit + } + if (dwEvent & EV_ERR) // signal error received + { + DWORD dwError; + + ClearCommError(hComm,&dwError,NULL); + if (dwError & (CE_FRAME | CE_OVERRUN | CE_BREAK)) + IOBit(RCS,RER,TRUE); // receiver error + } } return 0; UNREFERENCED_PARAMETER(pParam); @@ -60,11 +101,22 @@ BOOL CommOpen(LPTSTR strWirePort,LPTSTR strIrPort) _ASSERT(Chipset.IORam[IOC] & SON); // UART on if (hComm != NULL) // port already open + { CloseHandle(hComm); + hComm = NULL; + } if (lstrcmp(strPort, _T(NO_SERIAL))) // port defined { - hComm = CreateFile(strPort, + TCHAR szDevice[16] = _T("\\\\.\\"); + + // check if device buffer is big enough + _ASSERT(lstrlen(szDevice) + lstrlen(strPort) < ARRAYSIZEOF(szDevice)); + if (lstrlen(szDevice) + lstrlen(strPort) >= ARRAYSIZEOF(szDevice)) + return hComm != NULL; + + _tcscat(szDevice,strPort); // device name + hComm = CreateFile(szDevice, GENERIC_READ | GENERIC_WRITE, 0, NULL, @@ -74,17 +126,33 @@ BOOL CommOpen(LPTSTR strWirePort,LPTSTR strIrPort) if(hComm != INVALID_HANDLE_VALUE) { + DWORD dwThreadId; + nRp = 0; // reset receiver state dwBytesRead = 0L; SetCommTimeouts(hComm,&CommTimeouts); CommSetBaud(); - // set event RXD handler + CommTxBRK(); // update BRK condition + + // event to transmit character + hEventTxd = CreateEvent(NULL,FALSE,FALSE,NULL); + + // create char transmit handler + bWriting = TRUE; + hCThreadTxd = CreateThread(NULL,0,&TransmitThread,NULL,CREATE_SUSPENDED,&dwThreadId); + _ASSERT(hCThreadTxd); + SetThreadPriority(hCThreadTxd,THREAD_PRIORITY_ABOVE_NORMAL); + ResumeThread(hCThreadTxd); // start thread + + // create Comm event handler bReading = FALSE; - SetCommMask(hComm,EV_RXCHAR); // event on RX - hCThread = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)&SerialThread,NULL,0,&lSerialThreadId); - _ASSERT(lSerialThreadId); + SetCommMask(hComm,EV_RXCHAR | EV_TXEMPTY | EV_ERR); // event on RX, TX, error + hCThreadEv = CreateThread(NULL,0,&EventThread,NULL,CREATE_SUSPENDED,&dwThreadId); + _ASSERT(hCThreadEv); + SetThreadPriority(hCThreadEv,THREAD_PRIORITY_ABOVE_NORMAL); + ResumeThread(hCThreadEv); // start thread while (!bReading) Sleep(0); // wait for SerialThread started } else @@ -106,10 +174,18 @@ VOID CommClose(VOID) if (hComm != NULL) // port open { Sleep(25); // workaround to fix problems with some Kermit server - bReading = FALSE; // kill read thread + + bReading = FALSE; // kill event thread SetCommMask(hComm,0L); // clear all events and force WaitCommEvent to return - WaitForSingleObject(hCThread,INFINITE); // wait for thread termination - CloseHandle(hCThread); // close thread handle + WaitForSingleObject(hCThreadEv,INFINITE); + CloseHandle(hCThreadEv); + + bWriting = FALSE; // kill write thread + SetEvent(hEventTxd); // continue write thread + WaitForSingleObject(hCThreadTxd,INFINITE); + CloseHandle(hCThreadTxd); + + CloseHandle(hEventTxd); // close Txd event CloseHandle(hComm); // close port hComm = NULL; #if defined DEBUG_SERIAL @@ -127,8 +203,8 @@ VOID CommSetBaud(VOID) DCB dcb; - FillMemory(&dcb,sizeof(dcb),0); - dcb.DCBlength = sizeof(dcb); + ZeroMemory(&dcb,sizeof(dcb)); + dcb.DCBlength = sizeof(dcb); dcb.BaudRate = dwBaudrates[Chipset.IORam[BAUD] & 0x7]; dcb.fBinary = TRUE; dcb.fParity = TRUE; @@ -165,42 +241,86 @@ BOOL UpdateUSRQ(VOID) // USRQ handling return bUSRQ; } +VOID CommTxBRK(VOID) +{ + if (Chipset.IORam[TCS] & BRK) // BRK condition + { + if (hComm != NULL) // com port open + { + // abort data transfer + PurgeComm(hComm,PURGE_TXABORT | PURGE_TXCLEAR); + SetCommBreak(hComm); // set into BRK state + } + + // TBF and TBZ bits of TCS are undefined + + if (Chipset.IORam[TCS] & LPB) // is loopback bit set + { + dwBytesRead = nRp = 0; // clear receive buffer + cBuffer[dwBytesRead++] = 0; // save character in receive buffer + + CommReceive(); // receive available byte + IOBit(RCS,RER,TRUE); // receiver error (no stop bit) + } + } + else + { + if (hComm != NULL) // com port open + { + ClearCommBreak(hComm); // clear BRK state + } + } + return; +} + VOID CommTransmit(VOID) { - OVERLAPPED os = { 0 }; - DWORD dwWritten; - - BYTE tbr = (Chipset.IORam[TBR_MSB] << 4) | Chipset.IORam[TBR_LSB]; + BOOL bTxChar = FALSE; - #if defined DEBUG_SERIAL + EnterCriticalSection(&csTxdLock); + if ( (Chipset.IORam[TCS] & TBZ) == 0 // transmitter not busy + && (Chipset.IORam[TCS] & TBF) != 0) // transmit buffer full { - TCHAR buffer[256]; - if (isprint(tbr)) - wsprintf(buffer,_T("-> '%c'\n"),tbr); + tbr = (Chipset.IORam[TBR_MSB] << 4) | Chipset.IORam[TBR_LSB]; + + IOBit(TCS,TBF,FALSE); // clear transmit buffer full bit + IOBit(TCS,TBZ,TRUE); // set transmitter busy bit + + bTxChar = TRUE; + } + LeaveCriticalSection(&csTxdLock); + + if (bTxChar) // character to transmit + { + #if defined DEBUG_SERIAL + { + TCHAR buffer[256]; + if (isprint(tbr)) + wsprintf(buffer,_T("-> '%c'\n"),tbr); + else + wsprintf(buffer,_T("-> %02X\n"),tbr); + OutputDebugString(buffer); + } + #endif + + if (Chipset.IORam[TCS] & LPB) // is loopback bit set + { + if (dwBytesRead == 0) nRp = 0; // no character received, reset read pointer + cBuffer[nRp+dwBytesRead] = tbr; // save character in receive buffer + ++dwBytesRead; + + CommReceive(); // receive available byte + } + + if (hComm != NULL) // com port open + { + SetEvent(hEventTxd); // write TBR byte + } else - wsprintf(buffer,_T("-> %02X\n"),tbr); - OutputDebugString(buffer); + { + IOBit(TCS,TBZ,FALSE); // clear transmitter busy bit + } } - #endif - - if (Chipset.IORam[TCS] & LPB) // is loopback bit set - { - if (dwBytesRead == 0) nRp = 0; // no character received, reset read pointer - cBuffer[nRp+dwBytesRead] = tbr; // save character in receive buffer - ++dwBytesRead; - - CommReceive(); // receive byte available - } - - if (hComm != NULL) // com port open - { - os.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL); - WriteFile(hComm,(LPCVOID) &tbr,1,&dwWritten,&os); - GetOverlappedResult(hComm,&os,&dwWritten,TRUE); - CloseHandle(os.hEvent); - } - - Chipset.IORam[TCS] &= (~TBF); // clear transmit buffer if (UpdateUSRQ()) // update USRQ bit INTERRUPT; return; @@ -251,7 +371,7 @@ VOID CommReceive(VOID) --dwBytesRead; Chipset.IORam[RCS] |= RBF; // receive buffer full - if (UpdateUSRQ()) // update USRQ bit + if(UpdateUSRQ()) // update USRQ bit INTERRUPT; } while(0); diff --git a/sources/Emu48/TIMER.C b/sources/Emu48/TIMER.C index e05e5a8..dd9143d 100644 --- a/sources/Emu48/TIMER.C +++ b/sources/Emu48/TIMER.C @@ -40,7 +40,7 @@ static TIMECAPS tc; // timer information static BOOL bT2Val; // last access values valid -static __inline int MAX(int a, int b) {return (a>b)?a:b;} +static __inline int MAX(int a, int b) {return (a>b)?a:b;} static void CALLBACK TimeProc(UINT uEventId, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2); @@ -56,34 +56,42 @@ static DWORD CalcT2(VOID) // calculate timer2 value DWORD dwT2Dif; QueryPerformanceCounter(&lT2Act); // actual time - // calculate ticks since reference point - dwT2 -= (DWORD) - (((lT2Act.QuadPart - lT2Ref.QuadPart) * T2_FREQ) + // calculate realtime timer2 ticks since reference point + dwT2 -= (DWORD) + (((lT2Act.QuadPart - lT2Ref.QuadPart) * T2_FREQ) / lFreq.QuadPart); - // 2nd timer call in a time 32ms time frame - dwT2Dif = dwT2Ref - dwT2; - if (bT2Val && dwT2Dif > 0x01 && dwT2Dif <= 0x100) + dwT2Dif = dwT2Ref - dwT2; // timer2 ticks since last request + + // checking if the MSB of dwT2Dif can be used as sign flag + _ASSERT((DWORD) tc.wPeriodMax < ((1<<(sizeof(dwT2Dif)*8-1))/8192)*1000); + + // 2nd timer call in a 32ms time frame or elapsed time is negative (Win2k bug) + if (bT2Val && ((dwT2Dif > 0x01 && dwT2Dif <= 0x100) || (dwT2Dif & 0x80000000) != 0)) { #define CYC_PER_TICK (2 * 72) DWORD dwT2Ticks = ((DWORD) (Chipset.cycles & 0xFFFFFFFF) - dwT2Cyc) / CYC_PER_TICK; - if (dwT2Ticks >= dwT2Dif) + // estimated >= real elapsed timer2 ticks and positive time + if (dwT2Ticks >= dwT2Dif && (dwT2Dif & 0x80000000) == 0) { + // reached actual time -> new synchronizing dwT2Cyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); } else { - dwT2 = dwT2Ref - dwT2Ticks; - dwT2Cyc += dwT2Ticks * CYC_PER_TICK; + // real time too long or got negative time elapsed + dwT2 = dwT2Ref - dwT2Ticks; // estimated timer2 value from CPU cycles + dwT2Cyc += dwT2Ticks * CYC_PER_TICK; // estimated CPU cycles for the timer2 ticks } #undef CYC_PER_TICK } else { + // valid actual time -> new synchronizing dwT2Cyc = (DWORD) (Chipset.cycles & 0xFFFFFFFF); } - dwT2Ref = dwT2; + dwT2Ref = dwT2; // new reference time bT2Val = TRUE; // access values valid } return dwT2; @@ -104,7 +112,7 @@ static VOID CheckT1(BYTE nT1) _ASSERT((nT1&8) != 0); // timer1 MSB set // timer MSB is one and either INT or WAKE is set - if ( (Chipset.IORam[TIMER1_CTRL]&WKE) + if ( (Chipset.IORam[TIMER1_CTRL]&WKE) || (Chipset.IORam[TIMER1_CTRL]&INTR)) Chipset.IORam[TIMER1_CTRL] |= SRQ; // set SRQ // cpu not sleeping and T1 -> Interrupt @@ -139,7 +147,7 @@ static VOID CheckT2(DWORD dwT2) _ASSERT((dwT2&0x80000000) != 0); // timer2 MSB set // timer MSB is one and either INT or WAKE is set - if ( (Chipset.IORam[TIMER2_CTRL]&WKE) + if ( (Chipset.IORam[TIMER2_CTRL]&WKE) || (Chipset.IORam[TIMER2_CTRL]&INTR)) Chipset.IORam[TIMER2_CTRL] |= SRQ; // set SRQ // cpu not sleeping and T2 -> Interrupt @@ -174,7 +182,7 @@ static VOID RescheduleT2(BOOL bRefPoint) uDelay = CalcT2(); // actual timer value for delay } uDelay &= 0x7FFFFFFF; // execute timer2 event when MSB change - uDelay >>= 3; // timer delay in ms + uDelay = (uDelay + 7) >> 3; // timer delay in ms uDelay = MAX(tc.wPeriodMin,uDelay); // wait minimum delay of timer if ((bOutRange = uDelay > tc.wPeriodMax)) // delay greater maximum delay uDelay = tc.wPeriodMax; // wait maximum delay time @@ -308,11 +316,11 @@ VOID StartTimers(VOID) // initialisation of NINT2 lines bNINT2T1 = (Chipset.IORam[TIMER1_CTRL]&INTR) != 0 && (Chipset.t1 & 8) != 0; bNINT2T2 = (Chipset.IORam[TIMER2_CTRL]&INTR) != 0 && (Chipset.t2 & 0x80000000) != 0; - // set timer resolution to 1 ms, if failed don't use "Accurate timer" - bAccurateTimer = (timeBeginPeriod(1) == TIMERR_NOERROR); timeGetDevCaps(&tc,sizeof(tc)); // get timer resolution CheckT1(Chipset.t1); // check for timer1 interrupts CheckT2(Chipset.t2); // check for timer2 interrupts + // set timer resolution to 1 ms, if failed don't use "Accurate timer" + bAccurateTimer = (timeBeginPeriod(1) == TIMERR_NOERROR); // set timer1 with given period uT1TimerId = timeSetEvent(T1_FREQ,0,(LPTIMECALLBACK)&TimeProc,1,TIME_PERIODIC); _ASSERT(uT1TimerId); // test if timer1 started @@ -336,10 +344,9 @@ VOID StopTimers(VOID) EnterCriticalSection(&csT2Lock); { Chipset.t2 = CalcT2(); // update chipset timer2 value - // AbortT2(); // removed, stop timer2 later } LeaveCriticalSection(&csT2Lock); - AbortT2(); // stop timer2 here + AbortT2(); // stop timer2 outside critical section } bStarted = FALSE; if (bAccurateTimer) // "Accurate timer" running diff --git a/sources/Emu48/TYPES.H b/sources/Emu48/TYPES.H index 456c1aa..f190262 100644 --- a/sources/Emu48/TYPES.H +++ b/sources/Emu48/TYPES.H @@ -95,7 +95,7 @@ typedef struct UINT lcounter; UINT sync; // not used BYTE contrast; - BOOL dispon; + BOOL dispon; // not used DWORD start1; DWORD start12; DWORD end1; diff --git a/sources/GCCPatch/EMU48GCC.RC b/sources/GCCPatch/EMU48GCC.RC index b575e37..b44f6d2 100644 --- a/sources/GCCPatch/EMU48GCC.RC +++ b/sources/GCCPatch/EMU48GCC.RC @@ -29,6 +29,14 @@ LANGUAGE LANG_FRENCH, SUBLANG_FRENCH #ifdef APSTUDIO_INVOKED GUIDELINES DESIGNINFO DISCARDABLE BEGIN + IDD_WRITEONLYREG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 173 + TOPMARGIN, 7 + BOTTOMMARGIN, 75 + END + IDD_FIND, DIALOG BEGIN LEFTMARGIN, 6 @@ -133,7 +141,24 @@ END // Dialog // -IDD_FIND DIALOGEX 0, 0, 197, 47 +IDD_WRITEONLYREG DIALOG DISCARDABLE 0, 0, 180, 82 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Write Only Registers" +FONT 8, "MS Sans Serif" +BEGIN + LTEXT "20-24 (Display Start Address)",IDC_STATIC,11,11,131,8 + RTEXT "00000",IDC_ADDR20_24,144,11,25,8 + LTEXT "25-27 (Display Line Offset)",IDC_STATIC,11,21,131,8 + RTEXT "000",IDC_ADDR25_27,144,21,25,8 + LTEXT "28-29 (Display Line Counter)",IDC_STATIC,11,31,131,8 + RTEXT "00",IDC_ADDR28_29,144,31,25,8 + LTEXT "30-34 (Display Secondary Start Address)",IDC_STATIC,11, + 41,131,8 + RTEXT "00000",IDC_ADDR30_34,144,41,25,8 + DEFPUSHBUTTON "OK",IDOK,65,61,50,14 +END + +IDD_FIND DIALOG DISCARDABLE 0, 0, 197, 47 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_TOOLWINDOW CAPTION "Find" @@ -144,7 +169,7 @@ BEGIN WS_VSCROLL | WS_TABSTOP CONTROL "Find &ASCII",IDC_FIND_ASCII,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,46,30,49,10 - DEFPUSHBUTTON "Find Next",IDOK,140,7,50,14 + DEFPUSHBUTTON "&Find Next",IDOK,140,7,50,14 PUSHBUTTON "Cancel",IDCANCEL,140,26,50,14 END @@ -163,7 +188,7 @@ BEGIN PUSHBUTTON "&Delete",IDC_BREAKEDIT_DELETE,44,81,30,14 END -IDD_ABOUT DIALOGEX 0, 0, 261, 160 +IDD_ABOUT DIALOG DISCARDABLE 0, 0, 261, 160 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About Emu48" FONT 8, "MS Sans Serif" @@ -171,14 +196,14 @@ BEGIN ICON IDI_EMU48,IDC_STATIC,7,6,20,20,SS_REALSIZEIMAGE, WS_EX_TRANSPARENT LTEXT "",IDC_VERSION,29,6,151,8,NOT WS_GROUP - LTEXT "Copyright © 2001 Sébastien Carlier && Christoph Gießelink", + LTEXT "Copyright © 2002 Sébastien Carlier && Christoph Gießelink", IDC_STATIC,29,18,181,8 DEFPUSHBUTTON "OK",IDOK,215,12,39,14 EDITTEXT IDC_LICENSE,7,33,247,112,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY END -IDD_SETTINGS DIALOGEX 0, 0, 167, 209 +IDD_SETTINGS DIALOG DISCARDABLE 0, 0, 167, 209 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Settings" FONT 8, "MS Sans Serif" @@ -233,7 +258,7 @@ BEGIN PUSHBUTTON "V",IDC_UPDATE,115,17,10,14 END -IDD_KMLLOG DIALOGEX 0, 0, 301, 167 +IDD_KMLLOG DIALOG DISCARDABLE 0, 0, 301, 167 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "KML Script Compilation Result" FONT 8, "MS Sans Serif" @@ -251,7 +276,7 @@ BEGIN WS_BORDER | WS_VSCROLL | NOT WS_TABSTOP,WS_EX_CLIENTEDGE END -IDD_DISASM DIALOGEX 0, 0, 255, 165 +IDD_DISASM DIALOG DISCARDABLE 0, 0, 255, 165 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Disassembler" FONT 8, "Courier New" @@ -277,7 +302,7 @@ BEGIN WS_TABSTOP,WS_EX_NOPARENTNOTIFY END -IDD_DEBUG DIALOGEX 0, 0, 279, 269 +IDD_DEBUG DIALOG DISCARDABLE 0, 0, 279, 269 STYLE WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Debugger" MENU IDR_DEBUG @@ -372,8 +397,8 @@ STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "New Value" FONT 8, "MS Sans Serif" BEGIN - LTEXT "New value (hexdezimal):",IDC_STATIC,8,9,81,8 - EDITTEXT IDC_NEWVALUE,91,7,77,12,ES_AUTOHSCROLL + LTEXT "New value (hexadecimal):",IDC_STATIC,8,9,81,8 + EDITTEXT IDC_NEWVALUE,93,7,75,12,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,21,29,50,14 PUSHBUTTON "Cancel",IDCANCEL,103,29,50,14 END @@ -383,8 +408,8 @@ STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Enter Address" FONT 8, "MS Sans Serif" BEGIN - LTEXT "Enter address (hexdezimal):",IDC_STATIC,8,9,90,8 - EDITTEXT IDC_ENTERADR,106,7,43,12,ES_AUTOHSCROLL + LTEXT "Enter address (hexadecimal):",IDC_STATIC,8,9,93,8 + EDITTEXT IDC_ENTERADR,105,7,44,12,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,14,29,50,14 PUSHBUTTON "Cancel",IDCANCEL,92,29,50,14 END @@ -394,8 +419,8 @@ STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Enter Breakpoint" FONT 8, "MS Sans Serif" BEGIN - LTEXT "Enter breakpoint (hexdezimal):",IDC_STATIC,8,9,96,8 - EDITTEXT IDC_ENTERADR,110,7,39,12,ES_AUTOHSCROLL + LTEXT "Enter breakpoint (hexadecimal):",IDC_STATIC,8,9,99,8 + EDITTEXT IDC_ENTERADR,112,7,37,12,ES_AUTOHSCROLL CONTROL "&Code",IDC_BPCODE,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,17,24,33,10 CONTROL "R&PL",IDC_BPRPL,"Button",BS_AUTORADIOBUTTON,17,50,30,10 @@ -409,7 +434,7 @@ BEGIN PUSHBUTTON "Cancel",IDCANCEL,92,65,50,14 END -IDD_INSTRUCTIONS DIALOGEX 0, 0, 186, 169 +IDD_INSTRUCTIONS DIALOG DISCARDABLE 0, 0, 186, 169 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Last Instructions" FONT 8, "Courier New" @@ -438,7 +463,6 @@ IDR_DEBUG_TOOLBAR BITMAP MOVEABLE PURE "dbgtool.bmp" // Toolbar // - #if 0 // with VC++, this definition would be possible: @@ -460,7 +484,7 @@ END #else -// winres does not recognize TOOLBAR, so this workaround is used: +// windres does not recognize TOOLBAR, so this workaround is used: IDR_DEBUG_TOOLBAR 241 DISCARDABLE BEGIN @@ -475,7 +499,7 @@ BEGIN ID_BREAKPOINTS_SETBREAK, ID_BREAKPOINTS_CODEEDIT, 0, - ID_DEBUG_STEP, + ID_DEBUG_STEP, ID_DEBUG_STEPOVER, ID_DEBUG_STEPOUT, ID_DEBUG_RUNCURSOR @@ -490,8 +514,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,2,5,0 - PRODUCTVERSION 1,2,5,0 + FILEVERSION 1,3,0,0 + PRODUCTVERSION 1,3,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -508,12 +532,12 @@ BEGIN BEGIN VALUE "CompanyName", "Sebastien Carlier & Christoph Gießelink\0" VALUE "FileDescription", "HP38/39/40/48/49 Emulator\0" - VALUE "FileVersion", "1, 2, 5, 0\0" + VALUE "FileVersion", "1, 3, 0, 0\0" VALUE "InternalName", "Emu48\0" - VALUE "LegalCopyright", "Copyright © 2001\0" + VALUE "LegalCopyright", "Copyright © 2002\0" VALUE "OriginalFilename", "Emu48.exe\0" VALUE "ProductName", "Emu48\0" - VALUE "ProductVersion", "1, 2, 5, 0\0" + VALUE "ProductVersion", "1, 3, 0, 0\0" END END BLOCK "VarFileInfo" @@ -615,6 +639,7 @@ BEGIN POPUP "&Info" BEGIN MENUITEM "&Last Instructions...", ID_INFO_LASTINSTRUCTIONS + MENUITEM "&Write Only Registers...", ID_INFO_WRITEONLYREG END END @@ -639,7 +664,31 @@ BEGIN MENUITEM "Go to D&1", ID_DEBUG_MEM_GOD1 MENUITEM "Go to &Stack", ID_DEBUG_MEM_GOSTACK MENUITEM SEPARATOR - MENUITEM "Find...\tF", ID_DEBUG_MEM_FIND + MENUITEM "&Find...\tF", ID_DEBUG_MEM_FIND + MENUITEM SEPARATOR + POPUP "&Mapping" + BEGIN + MENUITEM "Map", ID_DEBUG_MEM_MAP + MENUITEM SEPARATOR + MENUITEM "NCE1", ID_DEBUG_MEM_NCE1 + , GRAYED + MENUITEM "NCE2", ID_DEBUG_MEM_NCE2 + , GRAYED + MENUITEM "CE1", ID_DEBUG_MEM_CE1, GRAYED + MENUITEM "CE2", ID_DEBUG_MEM_CE2, GRAYED + MENUITEM "NCE3", ID_DEBUG_MEM_NCE3 + , GRAYED + END + END +END + +IDR_DEBUG_STACK MENU DISCARDABLE +BEGIN + POPUP "" + BEGIN + MENUITEM "&Push", ID_DEBUG_STACK_PUSH + MENUITEM "P&op", ID_DEBUG_STACK_POP + MENUITEM "&Modify", ID_DEBUG_STACK_MODIFY END END @@ -688,6 +737,14 @@ BEGIN ID_BREAKPOINTS_CODEEDIT "Breakpoint List" END + +///////////////////////////////////////////////////////////////////////////// +// +// Manifest +// + +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "Emu48.xml" + #endif // French (France) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/sources/GCCPatch/Makefile b/sources/GCCPatch/Makefile index 2b65b92..5fefdfe 100644 --- a/sources/GCCPatch/Makefile +++ b/sources/GCCPatch/Makefile @@ -8,10 +8,8 @@ LD=gcc CFLAGS= -Wall -Wno-missing-braces -Wno-format -Wno-uninitialized -O3 # Things we need around to compile successfully -# I wonder why we need this _WIN32_IE definition, should take the -# default from the headers, but take a look and win32api 0.5's commctrl.h -DEFINES= -D_TCHAR=TCHAR -DIDC_STATIC=-1 -D_WIN32_IE=0x0300 +DEFINES= -D_TCHAR=TCHAR -DIDC_STATIC=-1 LDFLAGS= -s -mwindows diff --git a/sources/GCCPatch/PCH.H b/sources/GCCPatch/PCH.H index 77ad481..d49ec3d 100644 --- a/sources/GCCPatch/PCH.H +++ b/sources/GCCPatch/PCH.H @@ -2,36 +2,27 @@ // PCH.H (MinGW version) // +#define _WIN32_IE 0x0200 + #include #include #include #include #include -#include // for functions missing in stdlib.h +#include #include #include // #include // missing file -#define _ASSERT(a) // normally defined in missing crtdbg.h +#define _ASSERT(a) // normally defined in missing CRTDBG.H -#if defined POINTSTOPOINT // wrong implemented, so new correct definition - #undef POINTSTOPOINT +#if defined POINTSTOPOINT // wrong implementation, so new correct definition + #undef POINTSTOPOINT // fixed in w32api-1.2, but because of so many old versions... #define POINTSTOPOINT(pt, pts) \ { (pt).x = (LONG)(SHORT)LOWORD(*(LONG*)&pts); \ (pt).y = (LONG)(SHORT)HIWORD(*(LONG*)&pts); } #endif -#if !defined TTN_GETDISPINFOA // missing - #define TTN_GETDISPINFOA (TTN_FIRST - 0) -#endif -#if !defined TTN_GETDISPINFOW // missing - #define TTN_GETDISPINFOW (TTN_FIRST - 10) -#endif - -#if !defined TTN_GETDISPINFO // missing - #ifdef UNICODE - #define TTN_GETDISPINFO TTN_GETDISPINFOW - #else - #define TTN_GETDISPINFO TTN_GETDISPINFOA - #endif +#if !defined TTN_GETDISPINFO // missing in COMMCTRL.H + #define TTN_GETDISPINFO TTN_NEEDTEXT #endif diff --git a/sources/GCCPatch/ReadMe.txt b/sources/GCCPatch/README.TXT similarity index 63% rename from sources/GCCPatch/ReadMe.txt rename to sources/GCCPatch/README.TXT index 40f190a..f0967a3 100644 --- a/sources/GCCPatch/ReadMe.txt +++ b/sources/GCCPatch/README.TXT @@ -4,16 +4,18 @@ Emu48 is normally compiled with the Microsoft Visual C++ V6.0 compiler. An alternative is the GCC compiler from the GNU project. A great distribution -of the compiler and the necessary file is MinGW at www.mingw.org. +of the compiler and the necessary files is MinGW at www.mingw.org. Emu48 was tested with the following MinGW file versions: -libbfd-2.10.91-20010121.zip -binutils-2.10.91-20010114.zip -ld-2.10.91-20010126.zip -gcc-2.95.2-3.zip -w32api-0.5.1.tar.gz -mingw-runtime-0.5-20010221.tar.gz +MinGW-1.1.tar.gz + +and + +MinGW-1.1.tar.gz +w32api-1.2.tar.gz +mingw-runtime-1.2.tar.gz + Because of some inconsistences in the current MinGW distribution you have to add the files of this archive to the actual Emu48 source files. @@ -22,4 +24,4 @@ Many thanks to Pedro A. Arranda Guti compatible. -04/21/01 (c) by Christoph Gießelink, cgiess@swol.de +03/19/02 (c) by Christoph Gießelink, cgiess@swol.de diff --git a/sources/MkShared/MKSHARED.C b/sources/MkShared/MKSHARED.C new file mode 100644 index 0000000..94593d6 --- /dev/null +++ b/sources/MkShared/MKSHARED.C @@ -0,0 +1,172 @@ +/* + * MkShared, (c) 2002 Christoph Giesselink (cgiess@swol.de) + * + * 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. + */ + +#define WIN32_LEAN_AND_MEAN +#define WIN32_EXTRA_LEAN +#include +#include +#include +#include "resource.h" + +#define _KB(n) (2*(n)*1024) + +#define ARRAYSIZEOF(a) (sizeof(a) / sizeof(a[0])) + +#define DEFAULTFILE "SHARED.BIN" + +static VOID SetInformation(HWND hWnd,LPCTSTR strSize,LPCTSTR strNoOfPorts,LPCTSTR strPorts) +{ + SetDlgItemText(hWnd,IDC_FILE_SIZE,strSize); + SetDlgItemText(hWnd,IDC_NO_OF_PORTS,strNoOfPorts); + SetDlgItemText(hWnd,IDC_PORT_NO,strPorts); + return; +} + +static VOID WriteCardFile(LPCTSTR strFilename,INT nBlocks) +{ + HANDLE hFile = CreateFile(strFilename,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + DWORD dwWritten; + + LPBYTE pbyBuffer = LocalAlloc(LPTR,_KB(1)); + + while (nBlocks--) WriteFile(hFile, pbyBuffer, _KB(1), &dwWritten, NULL); + + LocalFree(pbyBuffer); + + CloseHandle(hFile); + return; + } + return; +} + +static LRESULT CALLBACK WndProc(HWND hWnd, UINT iMsg, WPARAM wParam, LPARAM lParam) +{ + static _TCHAR szFilename[256]; + static WORD wSize; + + HCURSOR hCursor; + + switch (iMsg) + { + case WM_COMMAND: + switch (wParam) + { + case IDC_CARD32: + wSize = 32; + SetInformation(hWnd,_T("64kb"),_T("1"),_T("2")); + return 0; + case IDC_CARD128: + wSize = 128; + SetInformation(hWnd,_T("256kb"),_T("1"),_T("2")); + return 0; + case IDC_CARD256: + wSize = 256; + SetInformation(hWnd,_T("512kb"),_T("2"),_T("2,3")); + return 0; + case IDC_CARD512: + wSize = 512; + SetInformation(hWnd,_T("1mb"),_T("4"),_T("2 through 5")); + return 0; + case IDC_CARD1024: + wSize = 1024; + SetInformation(hWnd,_T("2mb"),_T("8"),_T("2 through 9")); + return 0; + case IDC_CARD2048: + wSize = 2048; + SetInformation(hWnd,_T("4mb"),_T("16"),_T("2 through 17")); + return 0; + case IDC_CARD4096: + wSize = 4096; + SetInformation(hWnd,_T("8mb"),_T("32"),_T("2 through 33")); + return 0; + case IDOK: + GetDlgItemText(hWnd,IDC_FILENAME,szFilename,ARRAYSIZEOF(szFilename)); + hCursor = SetCursor(LoadCursor(NULL,IDC_WAIT)); + WriteCardFile(szFilename,wSize); // create file + SetCursor(hCursor); // restore cursor + return 0; + } + return 0; + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + + return DefWindowProc(hWnd,iMsg,wParam,lParam); +} + +INT WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrev,LPSTR lpszCmdLine,INT nCmdShow) +{ + HWND hWnd; + MSG msg; + WNDCLASS wc; +// RECT rc; + HFONT hFont; + + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = DLGWINDOWEXTRA; + wc.hInstance = hInst; + wc.hIcon = LoadIcon(hInst,MAKEINTRESOURCE(IDI_MKSHARED)); + wc.hCursor = LoadCursor(NULL,IDC_ARROW); + wc.hbrBackground = (HBRUSH) COLOR_WINDOW; + wc.lpszMenuName = NULL; + wc.lpszClassName = _T("CMkShared"); + RegisterClass(&wc); + + hWnd = CreateDialog(hInst,MAKEINTRESOURCE(IDD_MAIN),0,NULL); + _ASSERT(hWnd); + +#if 0 + // center window + GetWindowRect(hWnd, &rc); + SetWindowPos(hWnd, HWND_TOP, + ((GetSystemMetrics(SM_CXSCREEN) - (rc.right - rc.left)) / 2), + ((GetSystemMetrics(SM_CYSCREEN) - (rc.bottom - rc.top)) / 2), + 0, 0, SWP_NOSIZE | SWP_NOACTIVATE); +#endif + + // initialization + hFont = CreateFont(20,0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET,OUT_DEVICE_PRECIS,CLIP_DEFAULT_PRECIS,PROOF_QUALITY,DEFAULT_PITCH|TMPF_TRUETYPE|FF_ROMAN,"Times New Roman"); + _ASSERT(hFont); + SendDlgItemMessage(hWnd,IDC_STATIC_TITLE,WM_SETFONT,(WPARAM)hFont,MAKELPARAM(TRUE,0)); + SetDlgItemText(hWnd,IDC_FILENAME,_T(DEFAULTFILE)); + + // set to 32kb + SendDlgItemMessage(hWnd,IDC_CARD32,BM_SETCHECK,1,0); + PostMessage(hWnd,WM_COMMAND,IDC_CARD32,0); + + while(GetMessage(&msg,NULL,0,0)) + { + if(!IsDialogMessage(hWnd,&msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + DeleteObject(hFont); + return msg.wParam; + UNREFERENCED_PARAMETER(hPrev); + UNREFERENCED_PARAMETER(lpszCmdLine); + UNREFERENCED_PARAMETER(nCmdShow); +} diff --git a/sources/MkShared/MKSHARED.DSP b/sources/MkShared/MKSHARED.DSP new file mode 100644 index 0000000..9225de7 --- /dev/null +++ b/sources/MkShared/MKSHARED.DSP @@ -0,0 +1,114 @@ +# Microsoft Developer Studio Project File - Name="MKSHARED" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=MKSHARED - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "MKSHARED.MAK". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "MKSHARED.MAK" CFG="MKSHARED - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "MKSHARED - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "MKSHARED - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "MKSHARED - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir ".\Release" +# PROP Intermediate_Dir ".\Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x407 /d "NDEBUG" +# ADD RSC /l 0x407 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "MKSHARED - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "MKSHARED___" +# PROP BASE Intermediate_Dir "MKSHARED___" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir ".\Debug" +# PROP Intermediate_Dir ".\Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FR /YX /FD /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32 +# ADD BASE RSC /l 0x407 /d "_DEBUG" +# ADD RSC /l 0x407 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib comctl32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "MKSHARED - Win32 Release" +# Name "MKSHARED - Win32 Debug" +# Begin Group "Source" + +# PROP Default_Filter "*.c" +# Begin Source File + +SOURCE=.\MKSHARED.C +# End Source File +# End Group +# Begin Group "Header" + +# PROP Default_Filter "*.h" +# End Group +# Begin Group "Resource" + +# PROP Default_Filter "*.rc" +# Begin Source File + +SOURCE=.\MKSHARED.RC +# End Source File +# End Group +# Begin Source File + +SOURCE=.\MKSHARED.ICO +# End Source File +# End Target +# End Project diff --git a/sources/MkShared/MKSHARED.DSW b/sources/MkShared/MKSHARED.DSW new file mode 100644 index 0000000..b5a9ec9 --- /dev/null +++ b/sources/MkShared/MKSHARED.DSW @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 5.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "MKSHARED"=.\MKSHARED.DSP - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/sources/MkShared/MKSHARED.ICO b/sources/MkShared/MKSHARED.ICO new file mode 100644 index 0000000000000000000000000000000000000000..78689079eac804e150d0b1d327371e877a12a6f7 GIT binary patch literal 1318 zcmds$&q@O^5XQeX!Y+7d-=IB-#~y^ut%3z#N7#dchXx|FkK-fkqx2Da?4cNclWjGM z|M2M7Ofoay%p{vmq)I26PRGh{(mBd_e6UZj@m=7XB3DSIQ)U47IfBGc2*bjRs=n`8 zjYxSYCDRao=CbLp2pn)r>1fofOGj`q77j1(8~^HXor@hIM@Q>DV`dQs*Tu+) zM6ivN>BWKwZ{|QhE@E`S>>=A<9%vo1p`+1FW5vPD&&&=@F8)eu{T(f>{e6+$D8`i? zZYU1jaQORUaq6!jSKV+l+m8bFd08DMiwK2n5v55=K55~57hd>(ROx{NEA%R*XD!oS zleANvwyM&GUg?owy(2x-l6&rXC)HbN1U-SCLoX4+D#9J9HDyM0*(9ZYp3-)i(w;2} z=nGi?kC7>I?EfD9a|Hf27N~ zBr|VXDR$tNE;A5)FzdloJ+R!=9ju`y_zn3T&biZ(wCCOLDv3MQ2Ii0i-(EfB*mh literal 0 HcmV?d00001 diff --git a/sources/MkShared/MKSHARED.RC b/sources/MkShared/MKSHARED.RC new file mode 100644 index 0000000..5e494c0 --- /dev/null +++ b/sources/MkShared/MKSHARED.RC @@ -0,0 +1,180 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// German (Germany) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEU) +#ifdef _WIN32 +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MAIN DIALOG DISCARDABLE 15, 32, 164, 167 +STYLE WS_MINIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Create Emu48 Ram Card" +CLASS "CMkShared" +FONT 8, "MS Sans Serif" +BEGIN + ICON IDI_MKSHARED,IDC_STATIC,6,6,20,20 + CTEXT "Emu48\nRam Card Creator",IDC_STATIC_TITLE,34,6,94,26, + NOT WS_GROUP + ICON IDI_MKSHARED,IDC_STATIC,137,6,20,20 + GROUPBOX "Size",IDC_STATIC,6,30,90,53 + CONTROL "32kb",IDC_CARD32,"Button",BS_AUTORADIOBUTTON | BS_RIGHT | + WS_GROUP | WS_TABSTOP,14,40,34,10 + CONTROL "128kb",IDC_CARD128,"Button",BS_AUTORADIOBUTTON | + BS_RIGHT,14,50,34,10 + CONTROL "256kb",IDC_CARD256,"Button",BS_AUTORADIOBUTTON | + BS_RIGHT,14,60,34,10 + CONTROL "512kb",IDC_CARD512,"Button",BS_AUTORADIOBUTTON | + BS_RIGHT,14,70,34,10 + CONTROL "1mb",IDC_CARD1024,"Button",BS_AUTORADIOBUTTON | + BS_RIGHT,60,40,26,10 + CONTROL "2mb",IDC_CARD2048,"Button",BS_AUTORADIOBUTTON | + BS_RIGHT,60,50,26,10 + CONTROL "4mb",IDC_CARD4096,"Button",BS_AUTORADIOBUTTON | + BS_RIGHT,60,60,26,10 + GROUPBOX "Filename:",IDC_STATIC,6,86,152,28 + EDITTEXT IDC_FILENAME,13,96,138,12,ES_AUTOHSCROLL + DEFPUSHBUTTON "Create",IDOK,108,50,50,14 + GROUPBOX "File Information:",IDC_STATIC,6,118,152,43 + LTEXT "Actual File Size:",IDC_STATIC,13,129,57,8,NOT WS_GROUP + LTEXT "Static",IDC_FILE_SIZE,72,129,79,8,NOT WS_GROUP + LTEXT "Number of Ports:",IDC_STATIC,13,138,57,8,NOT WS_GROUP + LTEXT "Static",IDC_NO_OF_PORTS,72,138,79,8,NOT WS_GROUP + LTEXT "Ports:",IDC_STATIC,13,147,57,8,NOT WS_GROUP + LTEXT "Static",IDC_PORT_NO,72,147,79,8,NOT WS_GROUP +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_MAIN, DIALOG + BEGIN + LEFTMARGIN, 6 + RIGHTMARGIN, 158 + TOPMARGIN, 6 + BOTTOMMARGIN, 161 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_MKSHARED ICON DISCARDABLE "MKSHARED.ICO" + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,1,0,0 + PRODUCTVERSION 1,1,0,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "00000000" + BEGIN + VALUE "CompanyName", "Christoph Gießelink\0" + VALUE "FileDescription", "HP48 RAM Card Creator for Emu48\0" + VALUE "FileVersion", "1, 1, 0, 0\0" + VALUE "InternalName", "MkShared\0" + VALUE "LegalCopyright", "Copyright © 2002\0" + VALUE "OriginalFilename", "MkShared.exe\0" + VALUE "ProductName", "MkShared\0" + VALUE "ProductVersion", "1, 1, 0, 0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 0 + END +END + +#endif // !_MAC + +#endif // German (Germany) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/sources/MkShared/Main.c b/sources/MkShared/Main.c deleted file mode 100644 index d99e696..0000000 --- a/sources/MkShared/Main.c +++ /dev/null @@ -1,65 +0,0 @@ -#define WIN32_LEAN_AND_MEAN -#define WIN32_EXTRA_LEAN -#include -#include -#include - -HANDLE hFile; -DWORD dwWritten; -UINT nSize; -DWORD nBlocks; -LPBYTE pbyBuffer; - -UINT main(int argc, char *argv[]) -{ - if (argc != 3) - { - printf("Usage:\n\t%s \n", argv[0]); - return 1; - } - nSize = atoi(argv[2]); - switch (nSize) - { - case 32: - nBlocks = 16; - break; - case 128: - nBlocks = 64; - break; - case 256: - nBlocks = 128; - break; - case 512: - nBlocks = 256; - break; - case 1024: - nBlocks = 512; - break; - case 2048: - nBlocks = 1024; - break; - case 4096: - nBlocks = 2048; - break; - default: - puts("Error: Valid sizes are 32, 128, 256, 512, 1024, 2048 and 4096.\n"); - return 0; - } - - hFile = CreateFile(argv[1],GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL); - if (hFile == INVALID_HANDLE_VALUE) - { - printf("Cannot open file %s.\n", argv[1]); - return 1; - } - - pbyBuffer = LocalAlloc(LPTR,4096); - - while (nBlocks--) WriteFile(hFile, pbyBuffer, 4096, &dwWritten, NULL); - - LocalFree(pbyBuffer); - CloseHandle(hFile); - - printf("Done."); - return 0; -} diff --git a/sources/MkShared/MkShared.mak b/sources/MkShared/MkShared.mak deleted file mode 100644 index 378f95f..0000000 --- a/sources/MkShared/MkShared.mak +++ /dev/null @@ -1,186 +0,0 @@ -# Microsoft Developer Studio Generated NMAKE File, Format Version 40001 -# ** DO NOT EDIT ** - -# TARGTYPE "Win32 (x86) Console Application" 0x0103 - -!IF "$(CFG)" == "" -CFG=MkShared - Win32 Debug -!MESSAGE No configuration specified. Defaulting to MkShared - Win32 Debug. -!ENDIF - -!IF "$(CFG)" != "MkShared - Win32 Release" && "$(CFG)" !=\ - "MkShared - Win32 Debug" -!MESSAGE Invalid configuration "$(CFG)" specified. -!MESSAGE You can specify a configuration when running NMAKE on this makefile -!MESSAGE by defining the macro CFG on the command line. For example: -!MESSAGE -!MESSAGE NMAKE /f "MkShared.mak" CFG="MkShared - Win32 Debug" -!MESSAGE -!MESSAGE Possible choices for configuration are: -!MESSAGE -!MESSAGE "MkShared - Win32 Release" (based on\ - "Win32 (x86) Console Application") -!MESSAGE "MkShared - Win32 Debug" (based on "Win32 (x86) Console Application") -!MESSAGE -!ERROR An invalid configuration is specified. -!ENDIF - -!IF "$(OS)" == "Windows_NT" -NULL= -!ELSE -NULL=nul -!ENDIF -################################################################################ -# Begin Project -CPP=cl.exe -RSC=rc.exe - -!IF "$(CFG)" == "MkShared - Win32 Release" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 0 -# PROP BASE Output_Dir "Release" -# PROP BASE Intermediate_Dir "Release" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 0 -# PROP Output_Dir "Release" -# PROP Intermediate_Dir "Release" -# PROP Target_Dir "" -OUTDIR=.\Release -INTDIR=.\Release - -ALL : "$(OUTDIR)\MkShared.exe" - -CLEAN : - -@erase ".\Release\MkShared.exe" - -@erase ".\Release\Main.obj" - -"$(OUTDIR)" : - if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" - -# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c -# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /YX /c -CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\ - /Fp"$(INTDIR)/MkShared.pch" /YX /Fo"$(INTDIR)/" /c -CPP_OBJS=.\Release/ -CPP_SBRS= -# ADD BASE RSC /l 0x40c /d "NDEBUG" -# ADD RSC /l 0x40c /d "NDEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -BSC32_FLAGS=/nologo /o"$(OUTDIR)/MkShared.bsc" -BSC32_SBRS= -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib /nologo /subsystem:console /machine:I386 -LINK32_FLAGS=kernel32.lib /nologo /subsystem:console /incremental:no\ - /pdb:"$(OUTDIR)/MkShared.pdb" /machine:I386 /out:"$(OUTDIR)/MkShared.exe" -LINK32_OBJS= \ - "$(INTDIR)/Main.obj" - -"$(OUTDIR)\MkShared.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) - $(LINK32) @<< - $(LINK32_FLAGS) $(LINK32_OBJS) -<< - -!ELSEIF "$(CFG)" == "MkShared - Win32 Debug" - -# PROP BASE Use_MFC 0 -# PROP BASE Use_Debug_Libraries 1 -# PROP BASE Output_Dir "Debug" -# PROP BASE Intermediate_Dir "Debug" -# PROP BASE Target_Dir "" -# PROP Use_MFC 0 -# PROP Use_Debug_Libraries 1 -# PROP Output_Dir "Debug" -# PROP Intermediate_Dir "Debug" -# PROP Target_Dir "" -OUTDIR=.\Debug -INTDIR=.\Debug - -ALL : "$(OUTDIR)\MkShared.exe" - -CLEAN : - -@erase ".\Debug\MkShared.exe" - -@erase ".\Debug\Main.obj" - -@erase ".\Debug\MkShared.ilk" - -@erase ".\Debug\MkShared.pdb" - -@erase ".\Debug\vc40.pdb" - -@erase ".\Debug\vc40.idb" - -"$(OUTDIR)" : - if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" - -# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c -# ADD CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /YX /c -CPP_PROJ=/nologo /MLd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE"\ - /Fp"$(INTDIR)/MkShared.pch" /YX /Fo"$(INTDIR)/" /Fd"$(INTDIR)/" /c -CPP_OBJS=.\Debug/ -CPP_SBRS= -# ADD BASE RSC /l 0x40c /d "_DEBUG" -# ADD RSC /l 0x40c /d "_DEBUG" -BSC32=bscmake.exe -# ADD BASE BSC32 /nologo -# ADD BSC32 /nologo -BSC32_FLAGS=/nologo /o"$(OUTDIR)/MkShared.bsc" -BSC32_SBRS= -LINK32=link.exe -# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib /nologo /subsystem:console /debug /machine:I386 -# ADD LINK32 kernel32.lib /nologo /subsystem:console /debug /machine:I386 -LINK32_FLAGS=kernel32.lib /nologo /subsystem:console /incremental:yes\ - /pdb:"$(OUTDIR)/MkShared.pdb" /debug /machine:I386\ - /out:"$(OUTDIR)/MkShared.exe" -LINK32_OBJS= \ - "$(INTDIR)/Main.obj" - -"$(OUTDIR)\MkShared.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) - $(LINK32) @<< - $(LINK32_FLAGS) $(LINK32_OBJS) -<< - -!ENDIF - -.c{$(CPP_OBJS)}.obj: - $(CPP) $(CPP_PROJ) $< - -.cpp{$(CPP_OBJS)}.obj: - $(CPP) $(CPP_PROJ) $< - -.cxx{$(CPP_OBJS)}.obj: - $(CPP) $(CPP_PROJ) $< - -.c{$(CPP_SBRS)}.sbr: - $(CPP) $(CPP_PROJ) $< - -.cpp{$(CPP_SBRS)}.sbr: - $(CPP) $(CPP_PROJ) $< - -.cxx{$(CPP_SBRS)}.sbr: - $(CPP) $(CPP_PROJ) $< - -################################################################################ -# Begin Target - -# Name "MkShared - Win32 Release" -# Name "MkShared - Win32 Debug" - -!IF "$(CFG)" == "MkShared - Win32 Release" - -!ELSEIF "$(CFG)" == "MkShared - Win32 Debug" - -!ENDIF - -################################################################################ -# Begin Source File - -SOURCE=.\Main.c - -"$(INTDIR)\Main.obj" : $(SOURCE) "$(INTDIR)" - - -# End Source File -# End Target -# End Project -################################################################################ diff --git a/sources/MkShared/MkShared.mdp b/sources/MkShared/MkShared.mdp deleted file mode 100644 index 844cb4b091e395fb3b4ff95b4c171b04a4df47e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34816 zcmeI5Pf#0G9LImLK%umhTA);|eMPI)mV^RUM61m|YT5)kq%E}*r*3v1VP&%~v%8_- zOr7f4Klu0H^sYBA9-Q&Q#TjSx?0E3dvlpH5?BMThvLOUAf>21B-_C4y_x*nFz2E!1 zO<=z~ez}yqIIstrAOi3R(AIDSmT7Bf0{0tw^fdUFpdZ5U6!d~*(c!)lmBu09{lNRH zafVDIEhEqbd!hY$ICMEVJm61U5B>e4)vlb9cSSf5K7AS%lNKy~ZNfb~%I)`iCVW#o zm)GAF&vSTO!d-jo*E>Ee|E0|5x1{v!kT1A-$HEoBiSTGDVQYLYb;S~IbJej^iP_|g zVsR~%P;}F;E-!aN_$r@wB0{~&vpGX?IKY`=1G2ZW0lBANVVT_f_u#L!_51-QB|0>FgOe7rfTslHyyc$Z$!LW2v#Hql&>)+P#-qVx zvpOS4b_Fd4(&Cb8|RIWJ3U_D61i?7a(A-vzVY?0F%`R##wzvpdv*L-W!49+5H8-( zRs5^=As@tick#u2B>SToiMm^rcB;9zw00$vp-H@b+VSn9SQ+lb@P;oIxB83kE+4c( zcuY5VnX$RGD{^V7@LKGg4n%+m5CI}U1c(3;C=$r{e2@4#0ltR;cPB7JfC$tNfj>dM zf3x)dt)D5|)<~P71r?xHdr z_A^BdqNU7lFN$l@uYB|Tn#%mV_R9T!!iz6)$0pYFyPluyrZT=tKY1;+kd63BU)d(< zf299$jr2d#|KukEZA5?w5CI}U1c<3=mjEjt#}LFj}-DD@nM zF6f3HII?3sQ%Ac`pbe!zDZ$C@=Qj&S_tlm4q?lGP%e$f(d>7in!gMTQ^oONMI<;cy z0k1SdN^hnJfj#aX4;OGSIgTwiT2)cVd8Q> zM>q3I8khV<=T)97cY4Nc#GJ!WH5SqBoUrwB`v++64qBG7z(OK#YPQ=1*SO||@gaKj zj2S|4a50%FtSm+|k>i$PmRvqUm#(6VKBsG(r5D&uZV9iAk5?RGCZAVShfQ)jZgjcn*_m3oW)e^Rtb{H;VT|3Y69+eVx%ar1%GiSY%LGNV9 zI~fM}0sWRC5x*1%usL4P`ags~8xbG^M1Tko0V2@21W5mD+_|Tb*FOQ$|LT7Zw=#ln zA&BcgTTAOdNAR(DTsTZI3?`8*P_Mf{U>^kUdEJu!3qblW0O`N#4C%l0|KHx{ zA{~hU5g-CYfCvzQN&=++RuZ9WB2YgBNdK*$xq1+hJ`E>P^>`Le!E^9DoGxY6dk}q5 zo3%!u&DHF;5|2g^FY%8$ep&vBH)JrX6`-;+ug*kfOi1t z{}1l_pSG+y(|1FW{$Kb;wAGnvSO1ZK^#2C_B&VSh0U|&IhyW4TNdlz*?<8&NiwJBl k0n-1smuJ1THVQApD{vlOh1cK$1R+#!1E6@fK%fo&1;XCIDgXcg diff --git a/sources/MkShared/RESOURCE.H b/sources/MkShared/RESOURCE.H new file mode 100644 index 0000000..3b48fa5 --- /dev/null +++ b/sources/MkShared/RESOURCE.H @@ -0,0 +1,31 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by MKSHARED.RC +// +#define IDD_MAIN 100 +#define IDI_MKSHARED 101 +#define IDC_CARD32 1000 +#define IDC_CARD128 1001 +#define IDC_CARD256 1002 +#define IDC_CARD512 1003 +#define IDC_CARD1024 1004 +#define IDC_CARD2048 1005 +#define IDC_CARD4096 1006 +#define IDC_STATIC_TITLE 1007 +#define IDC_FILENAME 1008 +#define IDC_FILE_SIZE 1009 +#define IDC_NO_OF_PORTS 1010 +#define IDC_PORT_NO 1011 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1012 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif