From e6aae21937491246f1dedc4f1214e64229a8af74 Mon Sep 17 00:00:00 2001 From: Martin Haye Date: Sun, 10 Oct 2021 09:30:40 -0700 Subject: [PATCH] Added sound generator, and a godmode tester for it. --- .../src/org/badvision/A2PackPartitions.groovy | 1 + Platform/Apple/virtual/src/include/sound.i | 12 ++ Platform/Apple/virtual/src/plasma/combat.pla | 2 - Platform/Apple/virtual/src/plasma/diskops.pla | 11 +- Platform/Apple/virtual/src/plasma/gamelib.plh | 3 +- .../Apple/virtual/src/plasma/gameloop.pla | 22 ++-- Platform/Apple/virtual/src/plasma/godmode.pla | 46 +++++++ Platform/Apple/virtual/src/sound/sound.s | 120 ++++++++++++++++++ 8 files changed, 204 insertions(+), 13 deletions(-) create mode 100644 Platform/Apple/virtual/src/include/sound.i diff --git a/Platform/Apple/tools/PackPartitions/src/org/badvision/A2PackPartitions.groovy b/Platform/Apple/tools/PackPartitions/src/org/badvision/A2PackPartitions.groovy index d7e98e7a..8588a9a5 100644 --- a/Platform/Apple/tools/PackPartitions/src/org/badvision/A2PackPartitions.groovy +++ b/Platform/Apple/tools/PackPartitions/src/org/badvision/A2PackPartitions.groovy @@ -2432,6 +2432,7 @@ class A2PackPartitions assembleCode("fontEngine", "src/font/") assembleCode("tileEngine", "src/tile/") assembleCode("marks", "src/marks/") + assembleCode("sound", "src/sound/") assembleCode("gen_mapSpecials", "src/mapScripts/") assembleCode("gen_mapExits", "src/mapScripts/") diff --git a/Platform/Apple/virtual/src/include/sound.i b/Platform/Apple/virtual/src/include/sound.i new file mode 100644 index 00000000..dfa828a7 --- /dev/null +++ b/Platform/Apple/virtual/src/include/sound.i @@ -0,0 +1,12 @@ +;**************************************************************************************** +; Copyright (C) 2021 The 8-Bit Bunch. Licensed under the Apache License, Version 1.1 +; (the "License"); you may not use this file except in compliance with the License. +; You may obtain a copy of the License at . +; Unless required by applicable law or agreed to in writing, software distributed under +; the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +; ANY KIND, either express or implied. See the License for the specific language +; governing permissions and limitations under the License. +;**************************************************************************************** + +; in main LC, bank 1 +genSound = $FF80 diff --git a/Platform/Apple/virtual/src/plasma/combat.pla b/Platform/Apple/virtual/src/plasma/combat.pla index ef006ee7..7de29cf3 100644 --- a/Platform/Apple/virtual/src/plasma/combat.pla +++ b/Platform/Apple/virtual/src/plasma/combat.pla @@ -1448,8 +1448,6 @@ def _combat_zoneEncounter(s_encZone)#1 is TYPE_ENEMY if enemyCombatTurn(p); combatPause(); fin break - otherwise - brk() wend fin p = p=>p_combatNext diff --git a/Platform/Apple/virtual/src/plasma/diskops.pla b/Platform/Apple/virtual/src/plasma/diskops.pla index a3ac9a27..718154f0 100644 --- a/Platform/Apple/virtual/src/plasma/diskops.pla +++ b/Platform/Apple/virtual/src/plasma/diskops.pla @@ -22,6 +22,8 @@ const fontEngine = $EC00 // main mem LC const fontEngineLen = $F00 // really a bit less, but this leaves space for debug code const fontData = $FB00 // main mem LC const fontDataLen = $4FA // really only $474, but we need to fill all gaps +const soundCode = $FF80 // main mem LC +const soundLen = $7A // FF80.FFF9 const expandVec = $200 // aux mem (only for raycaster) const expandMax = $3600 // max size of unsplit expander @@ -179,13 +181,14 @@ end /////////////////////////////////////////////////////////////////////////////////////////////////// // Load and display the title screen, set up everything in memory def _startup()#1 - word pEngine, pFont, pMarks, pExpand, expanderSize + word pEngine, pFont, pMarks, pExpand, expanderSize, pSound puts("Loading game.\n") auxMmgr(FREE_MEMORY, $800) // was temporarily reserved by gameloop to keep diskops bytecode out // Allocate and permanently lock mem for the font engine and its font (up in LC ram) + // Also we can load the sound generator now. mmgr(START_LOAD, 1) // partition 1 is where code lives mmgr(SET_MEM_TARGET, fontEngine) mmgr(REQUEST_MEMORY, fontEngineLen) @@ -198,6 +201,7 @@ def _startup()#1 // Load them into lo mem pEngine = mmgr(QUEUE_LOAD, CODE_FONT_ENGINE<<8 | RES_TYPE_CODE) pFont = mmgr(QUEUE_LOAD, 1<<8 | RES_TYPE_FONT) + pSound = mmgr(QUEUE_LOAD, CODE_SOUND<<8 | RES_TYPE_CODE) mmgr(FINISH_LOAD, 0) // Relocate font engine and font data to their final spots up in the language card @@ -207,7 +211,11 @@ def _startup()#1 // Tell the font engine where to find its font setFont(fontData) + // Relocate the sound routine to its proper place + memcpy(pSound, soundCode, soundLen, 0) + // Load the title screen and show it. + pResourceIndex = 0 // special case - can't load resourceIndex yet because future expander loadFrameImg(1) // title screen is fixed at #1 ^$C050 // graphics ^$C057 // hi-res @@ -247,6 +255,7 @@ def _startup()#1 mmgr(FREE_MEMORY, pExpand) // To reduce fragmentation, load the resource index in the lowest possible aux mem spot. + // Note: this has to be done after the expander, since it temporarily uses this space. pResourceIndex = $800 auxMmgr(SET_MEM_TARGET, pResourceIndex) auxMmgr(QUEUE_LOAD, CODE_RESOURCE_INDEX<<8 | RES_TYPE_CODE) diff --git a/Platform/Apple/virtual/src/plasma/gamelib.plh b/Platform/Apple/virtual/src/plasma/gamelib.plh index 675600f0..25ebdc98 100644 --- a/Platform/Apple/virtual/src/plasma/gamelib.plh +++ b/Platform/Apple/virtual/src/plasma/gamelib.plh @@ -24,7 +24,6 @@ import gamelib predef beep()#0 predef benchPlayer()#0 predef blit(isAux, srcData, dstScreenPtr, nLines, lineSize)#0 - predef brk()#0 predef buildString()#0 predef buySell(storeCode, profitRatio)#0 predef calcPlayerArmor(player)#0 @@ -33,6 +32,7 @@ import gamelib predef callProRWTS(cmdPlusOpenFlg, filename, addr, size)#1 predef charToUpper(c)#1 predef checkEncounter(x, y, force)#0 + predef clampByte(val)#1 predef clearEncounterZones()#0 predef clearPortrait()#0 predef clearWindow()#0 @@ -58,6 +58,7 @@ import gamelib predef forEach(p, do)#0 predef forSome(p, sel, do)#0 predef fullAddItem(pItem, doit)#1 + predef genSound(dnnoise, dnvelo, dndelay, upnoise, upvelo, updelay, negdur)#0 predef getCharResponse()#1 predef getCursor()#2 predef getDir()#1 diff --git a/Platform/Apple/virtual/src/plasma/gameloop.pla b/Platform/Apple/virtual/src/plasma/gameloop.pla index 7a6ca180..b40f7eb8 100644 --- a/Platform/Apple/virtual/src/plasma/gameloop.pla +++ b/Platform/Apple/virtual/src/plasma/gameloop.pla @@ -220,6 +220,7 @@ asm _defs !source "../../include/plasma.i" !source "../../include/mem.i" !source "../../include/fontEngine.i" +!source "../../include/sound.i" !source "../../include/prorwts.i" ; Optional debug printing support @@ -826,11 +827,10 @@ asm getFloppyFlg()#1 end /////////////////////////////////////////////////////////////////////////////////////////////////// -// Execute a monitor breakpoint -export asm brk()#0 - bit setText - bit page1 - brk +// Generate a sound on the Apple II speaker +export asm genSound(dnnoise, dnvelo, dndelay, upnoise, upvelo, updelay, dur)#0 + +asmPlasmNoRet 7 + jmp genSound end /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -1703,6 +1703,9 @@ export def lookupResourcePart(sectionNum, resourceNum)#1 word ptr byte n, i + // Special case: main frame img laods before resource index + if !pResourceIndex; return 1; fin + // Skip to the requested section (starting just after version num) ptr = pResourceIndex while sectionNum > 0 @@ -1712,7 +1715,7 @@ export def lookupResourcePart(sectionNum, resourceNum)#1 // And grab the number from that section's table n = readAuxByte(ptr) - if resourceNum > n; fatal("lkupFail1"); fin + if resourceNum > n; fatal("lkup1"); fin n = readAuxByte(ptr + resourceNum) // If resource is on the current map's disk, prefer that @@ -2088,7 +2091,7 @@ def initMap(x, y, dir)#0 // Load the map curMapPartition = lookupResourcePart(mapIs3D+1, mapNum) - if !curMapPartition; fatal("lkupFail2"); fin + if !curMapPartition; fatal("lkup2"); fin mmgr(START_LOAD, curMapPartition) pCurMap = mmgr(QUEUE_LOAD, mapNum<<8 | (RES_TYPE_2D_MAP+mapIs3D)) mmgr(FINISH_LOAD, 0) @@ -2721,7 +2724,7 @@ export def setPortrait(portraitNum)#0 // Load the portrait image and display it part = lookupResourcePart(3, portraitNum) - if !part; fatal("lkupFail2"); fin + if !part; fatal("lkup3"); fin // Commented out below, because it prevents cycling thru all portraits (in god mode) // NO: if part > 1; part = curMapPartition; fin // Look on disk 1 or current disk only mmgr(START_LOAD, part) @@ -3201,6 +3204,7 @@ def initCmds()#0 cmdTbl['G'] = @cheatCmd // next ground cmdTbl['&'] = @printMem // print mem cmdTbl['^'] = @cheatCmd // edit flags + cmdTbl['*'] = @cheatCmd // soundGen test fin // Commands handled differently in 3D vs 2D @@ -3555,7 +3559,7 @@ export def hisHerTheir(gender)#1 end /////////////////////////////////////////////////////////////////////////////////////////////////// -def clampByte(val)#1 +export def clampByte(val)#1 return max(0, min(255, val)) end diff --git a/Platform/Apple/virtual/src/plasma/godmode.pla b/Platform/Apple/virtual/src/plasma/godmode.pla index 32c56a24..9f0e4e13 100644 --- a/Platform/Apple/virtual/src/plasma/godmode.pla +++ b/Platform/Apple/virtual/src/plasma/godmode.pla @@ -25,6 +25,8 @@ predef _addItem()#1 predef _addPlayer()#1 word[] funcTbl = @_cheatCmd, @_addItem, @_addPlayer +predef parseDecWithDefault(str, default)#1 + asm _defs ; Use hi-bit ASCII for Apple II @@ -59,6 +61,49 @@ asm readStr()#1 rts end +/////////////////////////////////////////////////////////////////////////////////////////////////// +def testSoundGen()#0 + word pStr, n, dur, updelay, upvelo, upnoise, dndelay, dnvelo, dnnoise, print + ^$C051; ^$C054 + updelay = 0 + upvelo = 0 + dndelay = 0 + dnvelo = 0 + upnoise = 0 + dnnoise = 0 + dur = 1000 + print = TRUE + while TRUE + if print + puts("\n") + printf3("Up: A:dly=%d B:velo=%d C:noise=%d\n", updelay, upvelo, upnoise) + printf3("Dn: D:dly=%d E:velo=%d F:noise=%d\n", dndelay, dnvelo, dnnoise) + printf1("Dur: G=%d\n", dur) + fin + puts("Noise cmd, e.g. 50G: ") + pStr = readStr() + if ^pStr >= 2 + print = TRUE + n = parseDec(pStr) + when charToUpper(^(pStr + ^pStr)) + is 'A'; updelay = n; break + is 'B'; upvelo = n; break + is 'C'; upnoise = clampByte(n); break + is 'D'; dndelay = n; break + is 'E'; dnvelo = n; break + is 'F'; dnnoise = clampByte(n); break + is 'G'; dur = n; break + otherwise + puts("Error.\n") + wend + else + print = FALSE + ^$25 = ^$25 - 1 + fin + genSound(dnnoise, dnvelo, dndelay, upnoise, upvelo, updelay, dur) + loop +end + /////////////////////////////////////////////////////////////////////////////////////////////////// def parseDecWithDefault(str, default)#1 if ^str == 0 @@ -221,6 +266,7 @@ def _cheatCmd(key)#1 is 'Y'; nextSky(); break is 'G'; nextGround(); break is '^'; editFlags(); break + is '*'; testSoundGen(); break otherwise fatal("gmcmd") wend return 0 diff --git a/Platform/Apple/virtual/src/sound/sound.s b/Platform/Apple/virtual/src/sound/sound.s index e69de29b..17fd416a 100644 --- a/Platform/Apple/virtual/src/sound/sound.s +++ b/Platform/Apple/virtual/src/sound/sound.s @@ -0,0 +1,120 @@ +;**************************************************************************************** +; Copyright (C) 2021 The 8-Bit Bunch. Licensed under the Apache License, Version 1.1 +; (the "License"); you may not use this file except in compliance with the License. +; You may obtain a copy of the License at . +; Unless required by applicable law or agreed to in writing, software distributed under +; the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +; ANY KIND, either express or implied. See the License for the specific language +; governing permissions and limitations under the License. +;**************************************************************************************** + +; Use hi-bit ASCII for Apple II +!convtab "../include/hiBitAscii.ct" + +; Global definitions +!source "../include/global.i" +!source "../include/mem.i" +!source "../include/sound.i" +!source "../include/plasma.i" + +* = genSound + +; hardware +spkr = $C030 + +; init +doGenSound: +!zone { +; stack offsets +.o_delay = 0 +.o_velo = 1 +.o_noise = 2 + +; variables +.negdur = $2 ; 2 bytes +.upx = $4 +.dnx = $5 +.pbits = $6 ; 2 bytes +.rnd = $8 + + eor #$FF + sta .negdur + tya + eor #$FF + sta .negdur+1 + inx + stx .upx + inx + inx + inx + stx .dnx + + lda #$EC + sta .pbits+1 + ldy #0 + sty .pbits ; 28 bytes + +.cycle: + sta spkr + + lda .o_velo+evalStkL,x + clc + adc .o_delay+evalStkL,x + sta .o_delay+evalStkL,x + lda .o_velo+evalStkH,x + adc .o_delay+evalStkH,x + pha + ror ; carry out from the addition... + eor .o_velo+evalStkH,x ; ... should match sign of the velocity + asl ; stach match status in carry + pla + bcc + ; if match, accept the result... + lda .o_delay+evalStkH,x ; ... else over/underflow occurred; revert. ++ sta .o_delay+evalStkH,x + + sec +- sbc #1 + pha + pla + pha + pla + bcs - + +.calcnoise: + lda .rnd + asl ; cute trick: asl + adc #0 is a lossless rotate that also clears carry + adc (.pbits),y + sta .rnd + dey + bne .npause + inc .pbits+1 + bne + + ldy #$EC + sty .pbits+1 ++ ldy #$FF +.npause: + and .o_noise+evalStkL,x + sec +- sbc #1 + pha + pla + pha + pla + bcs - + +.flip + cpx .dnx + ldx .upx + bcs + + ldx .dnx ++ + +.nextdur: + inc .negdur + bne .cycle + inc .negdur+1 + bne .cycle + +stop: + rts +}